多趣味ブログ

いろいろな事を広く浅く楽しむブログ

[FuelPHP]ORMの多対多リレーションメモ(前編)

      2016/12/16

imgres

PHP初心者がFuelPHP1.8でORMの多対多リレーションシップ使うメモです。本当に最初から説明します。

DB設定やORMパッケージの有効化はここでは解説しません!

多対多リレーションって何ぞや

多対多の前に多対1のリレーションをおさらい。

多対1リレーションシップ

下のような生徒を管理しているテーブルと、部活を管理しているテーブルがあるとします。

生徒テーブル
id 生徒名 部活id
1 田中 1
2 木村 2
3 佐藤 2
部活テーブル
id 部活名
1 やきう
2 バスケ

この場合、生徒テーブルの部活idをたどればどの部活に所属しているかわかる訳ですね。

生徒は一つの部活に入っていて、部活には多数の生徒が入っている。という関係になるので多対一という関係になります。なんか逆っぽいよねぇ。

多対1リレーションの限界

上の例では生徒は一つの部活にしか参加できない、また必ず一つの部活に入らなければいけなくなっています。

それでは部活を掛け持ちしている生徒や、何の部活にも入っていない生徒がいる場合はどうすればいいでしょうか?

生徒テーブルに部活idカラムを増やす? もし100種もの部活を掛け持ちしている超高校生がいたらどうでしょうか。それだけの部活IDを格納できるようにカラムを増やすのは現実的ではありません。

逆に何の部活にも参加していない生徒はどうでしょうか?

部活テーブルに帰宅部を増やす? いいえ、そのような部活は存在しません。部活というものを扱う部活テーブルに、部活ではないものを入れるのはナンセンスです。

こういうものを表現したいときに多対多リレーションを使います。

多対多リレーション

生徒_部活テーブルという『中間テーブル』を追加します。

生徒テーブル
id 生徒名
1 田中
2 木村
3 佐藤
生徒_部活テーブル
生徒id 部活id
1 1
1 2
2 2
部活テーブル
id 部活名
1 やきう
2 バスケ

こうすれば、生徒の田中くんがやきう部とバスケ部を掛け持ちしている事と、佐藤くんがどの部活にも入っていない事が表現できます。簡単ですね。

中間テーブルの種類

中間テーブルに値を格納する場合があります。生徒と部活テーブルは省略しますが、

入部テーブル
生徒id 部活id 入部日
1 1 2016/04/21
1 2 2016/05/12
2 2 2016/06/21
2 2 2017/10/02

こういうテーブルが考えられますね。木村君がバスケ部に2回入部している事がわかります。何かあったのでしょうか?

一つ前のような中間テーブルでは、重複しているものを格納しておく必要はない(むしろ消しておくべき)なのですが、今回のようなケースでは残すこともあり得ます。

FuelPHPでモデルに多対多リレーションを書く

テスト用のテーブルとモデルを作成

練習用のテーブルとモデルと管理ページをスキャフォールドしてしまいましょう。
コンソール等を起動しoilコマンドが使えるところ(大体fuelphp/)で以下のコマンド4つを一個ずつ実行。

$php oil g scaffold student name:string -s
$php oil g scaffold club name:string -s
$php oil g scaffold students_club student_id:int:unsigned club_id:int:unsigned --no-timestamp -s
$php oil r migrate

テーブル名は大体自由なんですが、中間テーブルに関しては 『テーブルA名s_テーブルB名s』というテーブル名になってるとFuelが勝手に読んでくれたりします(でも設定でテーブルを指定できるので気にしなくていいです)。

今回はデータの入っていない中間テーブルなのですが、この場合だと中間テーブル用のモデルは必要ありません。消しちゃってもOKです。

コマンドが全部通ったら [http://localhost/public/student] とかにアクセスして確認してください。

エラー出る場合はDB設定が抜けてないかとか、パッケージ設定にORMが指定されてるかとかを確認してください。

あと好きに生徒名や部活名を書いておきましょう。データが無ければ意味がない!

モデルの編集

とりあえずここでは生徒テーブル(student)のモデルからリレーションとデータの挿入までします。部活テーブル(club)もシンメトリな記述をすればできますのでそちらは省略。

fuel/app/classes/model/student.phpを編集。クラス内に以下のプロパティを記述します。

protected static $_many_many = array(
    'clubs' => array(
        'table_through' => 'students_clubs', 
        'model_to' => 'Model_club',
    )
);

table_throughもテーブル名が正確についていれば省略可能です。

正式な設定は公式ドキュメントのMany to Manyの項にあるのでそちらを参照をば

リレーションを張る

例としてID:1の生徒とID:1の部活を紐づけしてみましょう。

$student = Model_Student::find(1);
$club = Model_Club::find(1);
$student->clubs[] = $club;
$student->save();

これだけです。中間テーブルを覗いてみて値がちゃんと入っていればOKです。
プロパティの値を参照するときは、配列なのを考慮してforeachなどで回すのが良いかと思います。

$student = Model_Student::find(1);
foreach($student->clubs as $club){
    echo $club->name.':';
}

お疲れ様でした

長くなったのでいったん切ります。

参考にしたとこ

 - FuelPHP, PHP