はじめに
生SQLからEloquent Model
を作りたいと思ったことはないでしょうか?
3つほど方法があるので、ご紹介します。
DB
でCREATE VIEW
してそのEloquent Model
を作る
以下のように、VIEW
を生成し、そのVIEW
に対するEloquent Model
を作ります。
CREATE VIEW view_user_with_groups AS (
SELECT
u.id,
u.email,
u.group_id,
g.name AS group_name
FROM users u
LEFT OUTER JOIN groups g ON u.group_id = g.id
);
use Illuminate\Database\Eloquent\Model;
class ViewUserWithGroup extends Model
{
}
hydrate
を使ってクエリ結果をEloquent Model
にマッピングする
以下のように、クエリ結果をEloquent Model
にマッピングします。
$sql = <<< SQL
SELECT
u.id,
u.email,
u.group_id,
g.name AS group_name
FROM users u
LEFT OUTER JOIN groups g ON u.group_id = g.id
WHERE u.id = :id
SQL;
$user = DB::select(DB::raw($sql), ['id' => auth()->id()]);
$userWithGroup = UserWithGroup::query()->hydrate($user);
use Illuminate\Database\Eloquent\Model;
class UserWithGroup extends Model
{
}
fromSub
を使って、Eloquent Model
のfrom
にサブクエリをセットする
以下のように、Eloquent Modelの
from`にサブクエリをセットします。
use Illuminate\Database\Eloquent\Model;
class UserWithGroup extends Model
{
protected static function boot()
{
parent::boot();
static::addGlobalScope('default', function (Builder $builder) {
$sql = <<< SQL
SELECT
u.id,
u.email,
u.group_id,
g.name AS group_name
FROM users u
LEFT OUTER JOIN groups g ON u.group_id = g.id
SQL;
$builder->fromSub($sql, 'user_with_groups');
});
}
}
$userWithGroup = UserWithGroup::find(auth()->id());
ポイントは、fromSub
の第2引数alias
をModel::getTable()
の戻り値と一致させておくことです。
protected $table = '{table_name}';
とテーブル名を直接指定してもOKです。
namespace Illuminate\Database\Eloquent;
abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable
{
public function getTable()
{
if (! isset($this->table)) {
return str_replace(
'\\', '', Str::snake(Str::plural(class_basename($this)))
);
}
return $this->table;
}
どういうときにどの方法を選ぶか
それぞれの方法のメリットとデメリットを列挙すると以下のようになります。
-
DB
でCREATE VIEW
してそのEloquent Model
を作る- SQLと
Eloquent Model
が分離されるので、コードがシンプルになる -
CREATE VIEW
もしくはALTER VIEW
するマイグレーションスクリプトが必要になる
- SQLと
-
hydrate
を使ってクエリ結果をEloquent Model
にマッピングする-
hydrate
するタイミングで動的にモデルを生成できる -
hydrate
しないことには何もできない(find
などクエリ系のメソッドは一切使えない)
-
-
fromSub
を使って、Eloquent Model
のfrom
にサブクエリをセットする-
find
などクエリ系のメソッドが使える(更新系は除く) - サブクエリの結果に対して、クエリを発行することになるので、SQLが重くなる可能性がある
-
おわりに
3つの方法それぞれにメリットとデメリットがありますが、いずれにしても、肥大化するEloquent Model
を分割することにはなるので、それぞれに合った方法を採用してみてはいかがでしょうか?
個人的には、クエリが十分に早ければ、fromSub
を使う方法がマイグレーションも必要ないし、採用しやすいのではないかと思います。
ではでは。