あいすのブログ

【Laravel】firstOrCreate( ) の動作を詳しく見てみる

f:id:sera1098:20180721150726j:plain:w700

Laravelに関する記事を投稿していくシリーズ.記念すべき第1弾はfirstOrCreate()です!

firstOrCreate() とは

名前のとおり,第1引数に指定したパラメータを持つレコードが存在すれば first()で取得して,存在しなければレコードを作成します.

Laravel API によれば,firstOrCreate() の使い方は次のように書かれています.

Model firstOrCreate(array $attributes, array $values = [])

第1引数がレコードの検索条件,第2引数はレコードを作成する際に挿入する値です.第1引数で指定した以外のカラムの値を第2引数で指定できるということですね.

使い方

まず,第1引数だけを指定してみます.

以下のコードは,namehogeで,emailhoge@hoge.comのレコードが存在する場合はそのレコードを取得して,存在しない場合は新規でレコードを作成します.

$user = User::firstOrCreate([
    'name' => 'hoge',
    'email' => 'hoge@hoge.com',
]);

次は,第2引数も指定してみます.

第2引数を指定することで,agestatusにデフォルトの値ではなく,指定した値をレコードに挿入することができます.

$user = User::firstOrCreate([
    'name' => 'hoge',
    'email' => 'hoge@hoge.com',
], [
    'age' => 30,
    'status' => 10
]);

実は,第2引数を指定しなくても,以下のようにすれば上のコードと同じ動作になりますが,第2引数を指定した方が見やすいですね.

$user = User::firstOrCreate([
    'name' => 'hoge',
    'email' => 'hoge@hoge.com',
]);

$user->age = 30;
$user->status = 10;
$user->save();

ちなみに,$user->wasRecentlyCreatedの値を見れば, firstOrCreate()でレコードが作成されたかどうかを確認できます.作成した場合はTrue,作成していない場合はFalseです.

上のコードは,正しくはこう書くべきですね.

$user = User::firstOrCreate([
    'name' => 'hoge',
    'email' => 'hoge@hoge.com',
]);

if ($user->wasRecentlyCreated) {
    $user->age = 30;
    $user->status = 10;
    $user->save();
}

firstOrCreate() の定義を見てみよう

Illuminate/Database/Eloquent/Builder.phpの中で定義されています.

まずは,テーブルから$attributeの条件を満たすレコードを検索します.その結果,レコードが取得できれば,そのレコードをそのまま返します.レコードが取得できなかった場合は,新しくレコードを作成する処理に入ります.

public function firstOrCreate(array $attributes, array $values = [])
{
        if (! is_null($instance = $this->where($attributes)->first())) {
            return $instance;
        }

        return tap($this->newModelInstance($attributes + $values), function ($instance) {
            $instance->save();
        });
}

これがレコードを作成する処理ですね.

tap($this->newModelInstance($attributes + $values), function ($instance) {
            $instance->save();
});

最後に

firstOrCreate()は思っていたよりも簡単な関数でしたね.

難しそうな関数も実は普通に処理をしてるだけなんだなぁと気づけたのが今回の収穫です.