【Laravel】EloquentのHasOneOrManyのsave()が便利、push()も便利、合わせるととても便利

  • 2020年2月27日
  • 2020年2月28日
  • Laravel

 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;
    }
}
>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG