多対多とは
たとえばECサイトの受注データを考えてみると、
1件の受注レコードに複数の商品マスタレコードを紐づけますよね。
また、あるひとつの商品に何件注文が入っているか、
という方向からのリレーションも必要になるかと思います。
このように
・テーブルAのあるデータが、テーブルBのデータを複数もっている
・テーブルBのあるデータが、テーブルAのデータを複数もっている
という関係性を「多対多」のリレーションといいます。
Laravelでの多対多
LaravelのEloquent ORMでは「belongsToMany」という言い方をしており、
以下のようにModelファイルで指定します。
class Order extends Model { public function products() { return $this->belongsToMany('App\Product'); } }
多対多には中間テーブルが必要
・ordersテーブル
・productsテーブル
この2つのテーブルが多対多の関係にあるとき、もう一つ「中間テーブル」というものが必要になります。
Laravelでは「それぞれのモデル名の単数形ですべて小文字にしたものをアンダースコアで結合したテーブル名」が求められます。
わかりづらいですね。要するに「order_product」というテーブルが必要ということです。
class CreateOrderProduct extends Migration { public function up() { Schema::create('order_product', function (Blueprint $table) { $table->increments('id'); $table->unsignedInteger('order_id'); $table->unsignedInteger('product_id'); $table->timestamps(); $table->softDeletes(); }); } public function down() { Schema::dropIfExists('order_product'); } }
マイグレーションファイルはこんな感じになります。
・order_id
・product_id
この二つのカラムが必須になります。
このテーブルを用意して、先ほどのModelファイルでのbelongsToMany指定をしておけば、
あとはLaravelのほうでよしなにしてくれるという大変便利な仕組みになっています。
多対多のテストデータを一気にdb:seedしたい
ここからが本題ですが
多対多のデータは手入力すると非常に手間がかかりますので、
ここは是非ともLaravelのseed機能を使って自動生成したいわけです。
でもどうやって書くんだろう・・・なんか結構ややこしそうだなあ・・・
というイメージが先立ちますよね。
でも意外と簡単にできましたのでそのコードをご紹介したいと思います。
<?php use Illuminate\Database\Seeder; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\DB; class DatabaseSeeder extends Seeder { public function run() { // 商品データ登録 factory(App\Product::class, 10) ->create(); // 登録した商品データを全抽出 $products = App\Product::all(); // 受注データ登録 factory(App\Order::class, 10) ->create() ->each(function ($order) use ($products) { $order->products()->attach( $products->random(rand(1,3))->pluck('id')->toArray() // 1~3個のproductをorderにランダムに紐づけ ); }); } }・
・商品データを10件登録
・注文データを10件登録
・注文データ1件ごとに、商品データを1~3個ランダムでattach()する
というフローになっています。
$order->products()->attach($productIds)
この関数がキモです。
ここは、ほかのリレーションであればsave()やsaveMany()を使うところですが、
多対多の場合はattach()なんですね。
引数は紐づける対象のIDを配列で渡します。
$factory->define(App\Product::class, function (Faker $faker) { return [ 'name' => $faker->firstName, 'price' => random_int(100, 10000), 'status' => 1 // 販売中 ]; }); $factory->define(App\Order::class, function (Faker $faker) { $priceAmount = random_int(1000,100000); return [ 'ordered_at' => $faker->dateTime, 'price_amount' => $priceAmount, 'price_amount_taxed' => floor($priceAmount*1.08) ]; });
ちなみにfactoryファイルはこのようになっています。
ここは実際のテーブル構造に応じて読み替えてください。
Laravelのseed機能すごく便利
こういうちょっと込み入った処理も
非常にわかりやすく、使いやすく作られているなあと思いました。
玄人好みのフレームワークと呼ばれる理由がだんだんわかってきました。
よろしければお役立てください m (_ _) m