PHP
初心者
laravel
Eloquent

LaravelのORMで初心者から職人へ

More than 1 year has passed since last update.

取得するとき

職人

User::find(1);

ナイス職人

User::active()->createdLastMonth()->get();

Userクラスにクエリースコープを作って、それを使う
クエリースコープは下記のようになる

class User extends Model
{
    public function scopeActive($query)
    {
        $query->where('active', true);
    }
}

ほぼSQL書いてる急ぎの職人

User::where('active', true)->where('created_at', '>', Carbon::now()->subMonth())->get();

ここはもうSQLを書くことに近づいてきてるね
便利だけど、気をつけてください!
これはすごくメンテしにくいコードに進むパターンだ。

データを入れるとき(インサートでも、アップデートでも)

職人

$user->fill($request->all())->save();

fill()を使うと、Eloquentは$fillableをチェックして、このプロパティを設定できるかどうかを確認してくれるね。
$fillableを無視したい場合はforceFill()を使うけど、気をつけてね!
普通、forceFillを使うのはデータベースシーダーとかだね。

書くことが好きな職人

$user->name     = $request->name;
$user->email    = $request->email;
// ... 永遠に続くorz
$user->birthday = $request->birthday;
$user->save();

これはそんなに悪いパターンじゃないけど、フィールドの数が多いほど、悪いパターンになっていく。
そして、この方法は$fillableを無視するから、注意!できるだけfill()にしてください

メソッド名に騙されてる可能性が高い人

$data = ['name' => 'Shintarou', 'is_administrator' => true];

$user->update($data);

直接SQLでデータを入れてるだけですね。
SQLを使えるのは力強いほど危険で、気をつけないとね。
上の例えで、データが$request->all()から来てる場合、update($data)することで、なるはずのない誰かが管理者になるかもしれない。

もちろん、update()使いたいときもあります!

User::haveNotLoggedOnForFewMonths()->update(['inactive' => true]); 

複数行にアップデートをしたいときには完璧だね!

作成するとき

 職人

User::create($data);

これは一番簡単な方法だね!読みやすいから使われるけど、実際には下記のようになっている

public static function create(array $attributes = [])
{
    $model = new static($attributes);

    $model->save();

    return $model;
}

ということで、下記のように書いてもおk!同じことになる。

new User($data);

使う場合を考えられなかったけど、一応ある方法

User::insert($data);

SQLのupdate()ができるように、SQLのinsert()もできます… ちゃんとした理由がないと使わないでくださいね。
これはタイムスタンプを自動的に入れないし、$fillableもチェックしてくれないんだ。

削除するとき

職人

User::find(1)->delete();

モデルを取得してから削除する。
注意:SELECT(find)してから、DELETEをしている。クエリーは2つ

PKだけで削除できる職人

User::destroy(1);
User::destroy([1, 2]);

取得せずに削除してる。クエリーは1つ

職人かもしれない、メソッド名に騙されてる人かもしれない

User::whereActive(false)->delete(); 

updateとinsertと同じ、SQL向きのメソッドだ。
複数行で削除したいなら、便利だけど注意が必要

直接でSQLのDELETEだから、Userクラスにたとえ、SoftDeletesのTraitがあったとしても、deleted_atをアップデートせずに、その1行がデータベースから消される。
それはもちろん危ないし、誰も望んでいないことだから、そのとき狙いはきっと下記のようなコードだね。

User::whereActive(false)->update(['deleted_at' => Carbon::now()]); 

だけど、今回は削除するロジックは2つの場所になるね… EloquentのSoftDeletesのTraitを使いたいね!

$inactiveUserIds = User::whereActive(false)->lists('id');
User::destroy($inactiveUserIds);

すべてのIDを取得してから、クエリー1つですべてのIDをEloquentで削除する :smile:

結論

この記事の目的はどこでEloquentのパワーフルなモデルから、自由なQuery BuilderでSQLになることを別々にすることだね。

User::where()みたいなメソッドを使うときは、EloquentのQuery Builderのインスタンスをもらうから注意!

そのQuery Builderオブジェクトにinsert()update()delete()を呼ぶとモデルのいろんな設定を無視して、単純にSQLとして行われる。

推敲

// 3つも同じ結果だけど…
User::where('id', 1)->get()[0]; // 読みづらい
User::where('id', 1)->first();  //
User::find(1);                  // 読みやすい

User::find(1)->fill($data)->save();  // Eloquent Modelとしてアップデートする
User::where('id', 1)->update($data); // Query BuilderのUPDATEを行う

// 3つも同じ
$user = User::create($data);
$user = new User($data)->save();
$user = new User()->fill($data)->save();

User::find(1)->delete();        // Eloquent Modelを取得してから、削除する
User::where('id', 1)->delete(); // Query BuilderのDELETEを行う