Laravel:多対多リレーション作成時の注意点
Laravelで多対多リレーション作成時の注意点
Laravelを触りだして約1か月が経ちました。
その間に常時SSL化の為のServer移動があり急ぎの仕事を余儀なくされましたが、やっとLaravelシステムに戻ってこれました。
1週間でのEC-CUBEサイト構築は頑張ったと自分を褒めてやりたい気分ですが、必要に迫まれれば大体なんでもやるもんですよ。
という事で、必要に迫まれてるため調べながらなんとか1日がかりで解決した多対多リレーションの備忘録です。
- 中間テーブルの名称はアルファベット順に参照元テーブルの単数形をスネーク記法で記載
- 中間テーブルに自動採番のidカラムがあってもOK。『参照元テーブルのテーブル名(単数)_id』のカラムは必須
- 参照元テーブルがAs、Bsの時、モデル名はA.phpとB.phpとなる。
- 其々のモデルにはモデル名と同じclass名を付け、異なるテーブル名の単数形をfunction名にして設置する
- viewでは配列の階層の数だけ@foreachを使い値を取り出す
中間テーブルの名称はアルファベット順にスネーク記法で記載
alists,blistsの多対多のリレーションの場合、中間テーブル名は『alist_blist』。
ググると『alists_blists』と命名しているものもあるが、参照元テーブルの単数形をスネーク記法で接続が基本です。
上記条件は明示的に指定して処理出来るので必須ではありません。例えば、blistsの方が親要素が強い時など、『blist_alist』の方がしっくりする事があります。
そういう時にはそれほど難しくなく対応可能です。
/* model:Alist */
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Alist extends Model
{
public function blist(){
return $this->belongsToMany('App\Models\Blist',blist_alist);//中間テーブル名を明示
}
}
中間テーブルに自動採番のidカラムがあってもOK
上手くリレーションしない時に上手くいかない要因なのではと疑いましたが、リレーションを張れるか否かには全く影響を与えないと判明しました。
地味に悩んでしまったため独立した項目として明示する事にしました。
/*マイグレーション:alist_blist*/
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAlistBlistTable extends Migration
{
public function up()
{
Schema::create('alist_blist', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('alist_id');
$table->foreign('blist_id')->references('id')->on('blists')->onDelete('cascade');
$table->unsignedInteger('ability_id');
$table->foreign('alist_id')->references('id')->on('alists')->onDelete('cascade');
$table->integer('fragment');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('alist_blist');
}
}
『参照元テーブルのテーブル名(単数)_id』のカラムは必須
ここも明示的に処理できますが、作成していて実感したのは『命名規制に沿って書いた方がかなり楽』という事です。
命名規制に沿って記載した場合、次のようなリレーションIDが必要になります。
- Table:alists.id を格納するカラム alist_id
- Table:blists.id を格納するカラム blist_id
中間テーブルではこの2つのカラムが必須です。
参照元テーブルがAs、Bsの時、モデル名はA.phpとB.php
これも命名規制に沿って記載すると楽なパートです。
上記『Table:alist』『Table:blists』を例にすると以下の通りです。
- Table:alistsを軸とするモデル名:Alist
- Table:blistsを軸とするモデル名:Blist
モデルclass名はモデル名と同じ名前を付ける
テーブル名を明示的に指示し記載した場合はその名称(モデル名:Alistsの場合、class名:Alists)になります。
命名規制に沿った記載の場合は以下の通りです。
- モデル Alistのclass名:Alist
- モデル Blistのclass名:Blist
モデルfunction名にはモデル名と異なるテーブル名の単数形を付ける
- モデル Alistのfunction名:Blist
- モデル Blistのfunction名:Alist
具体的には下記コードになります。
/* モデル名:Alist */
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Alist extends Model
{
public function blist(){
return $this->belongsToMany('App\Models\Blist');
}
}
/* Blist */
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Blist extends Model
{
public function Alist(){
return $this->belongsToMany('App\Models\Alist')
}
}
viewでは配列の階層の数だけ@foreachを使い値を取り出す
多対多で作成したリレーションは1つのカラムに複数の値が入る【入れ子構造】になります。
今回この取り出しに予想外の苦戦。
よく考えればPHPの多次元配列なので『@foreach』を次元数だけ実行するのは当たり前だったのですが、前段のリレーションが出来ていないのではとそっちばかり見てたんですよね。
お陰で多対多リレーションのpivotの動作まで検証できましたが時間が掛かってしまいました。
無知は罪ですね。
まとめ
前回のLaravel記事では命名規制について記載しましたが、このルールを使うとリレーションは超簡単でした。
ただ、Laravelが勝手にやってくれることなので間違い探しはまだ慣れたSQLの記述の方が早いという宝の持ち腐れ状態。
まだ怖いので本丸の商品データは触らず周辺のDBを構築しながら進めていこうかと思います。


