EloqeuentはLaravelの持つORM(データベースをマッピングしたモデル)です。これには他テーブルとの関係性をモデル内に定義するためのリレーションという仕組みが備わっています。
Eloquent:リレーション 6.x Laravel
例えば、次の様になります
<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * ユーザーに関連する電話レコードを取得 */ public function phone() { return $this->hasOne('App\Phone'); } } // 別ファイルで /** @var App\Phone $phone ユーザに関連する電話レコードインスタンス */ $phone = User::find($id)->phone;
上記の様にプロパティとして呼び出すだけで適切なSQLが発行され、関連したレコードを元にしたモデルインスタンスを得られます。
この呼び出しだけでも便利なのですが保存でも活躍します。詳しくは次リンクの”関連したモデルの挿入/更新”です。
Eloquent:リレーション 6.x Laravel
例としては次になります。
<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * ユーザーに関連する電話レコードを取得 */ public function phone() { return $this->hasOne('App\Phone'); } } // 別ファイルで $phone = new Phone($validatedRequest); // user_idカラムの様なリレーションに使うカラムの値が自動で代入される保存処理 User::find($id)->phone()->save($phone);
リレーション用のidの代入が不要な保存です。かなり便利です。当然の様に1対多の保存も備わっています。
$post = App\Post::find(1); $post->comments()->saveMany([ new App\Comment(['message' => 'A new comment.']), new App\Comment(['message' => 'Another comment.']), ]);
話は変わってEloquentにはpushというメソッドもあります。これはリレーションを使った再帰保存です。具体的には次の様になります。
$post = App\Post::find(1); $post->comments[0]->message = 'Message'; $post->comments[0]->author->name = 'Author Name'; $post->push(); // postに関連するcommentテーブルのレコード一つ目のmessageカラムを更新して保存 // postに関連するcommentテーブルのレコード一つ目に関連するauthorテーブルのレコードのnameカラムを更新して保存
トランザクションを書いた後長々とsaveを書き続ける必要はありません。pushで事足ります。
pushメソッドにはもの足りない部分があります。それは先述した外部キーの自動割り当てです。これがないため、外部キーを手動で割り当てた後にpushかひとつづつrelation()->save($model)をするかの二択になりがちです。いいとこどりをしたいものです。いいとこどりをするには例えば次の様なメソッドが考えられます。既存のpushメソッドになかったrelation()->save($model)を増やしました。
<?php namespace App\Models\Eloquents; use App\Models\Eloquents\Contracts\HasRulesContract; use App\Models\Eloquents\Traits\HasRules; use Eloquent as Model; use Illuminate\Database\Eloquent\Collection; /** * 認証機能持ち以外のモデルのベース */ abstract class BaseEloquent extends Model { /** * リレーションを参照した自動キー割り当て機能を搭載したpushメソッド. * 再帰によって呼び出し済みのリレーション先全てをsaveする. * @param bool $skipSave このループでsaveをスキップするならばtrue. 主に一つ前のループでリレーションを元にした保存をした時に使用 * @return bool */ public function push($skipSave = false) { if (! $skipSave && ! $this->save()) { return false; } // すべての関係をデータベースに同期するために単にスピンスルーします // 関係を作成し、このpushメソッドを介して各モデルを保存します。 // モデルインスタンスのこれらのネストされたリレーションすべてに再帰します。 foreach ($this->getRelations() as $relationName => $models) { $models = $models instanceof Collection ? $models->all() : [$models]; foreach (array_filter($models) as $model) { // HasOneOrManyがあった上で保存に失敗したらreturn false if (method_exists($this, $relationName) && $this->$relationName() instanceof HasOneOrMany) { if ($this->$relationName()->save($model)) { $successSave = true; } else { return false; } } // 再帰 if (! $model->push($successSave ?? false)) { return false; } } } return true; } }