Edited at

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


取得するとき


職人

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))->save();


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

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を行う