15
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

生SQLからEloquent Modelを作る3つの方法

Last updated at Posted at 2019-01-04

はじめに

生SQLからEloquent Modelを作りたいと思ったことはないでしょうか?
3つほど方法があるので、ご紹介します。

DBCREATE 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 Modelfromにサブクエリをセットする

以下のように、Eloquent Modelfrom`にサブクエリをセットします。

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引数aliasModel::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;
    }

どういうときにどの方法を選ぶか

それぞれの方法のメリットとデメリットを列挙すると以下のようになります。

  • DBCREATE VIEWしてそのEloquent Modelを作る
    • SQLとEloquent Modelが分離されるので、コードがシンプルになる
    • CREATE VIEWもしくはALTER VIEWするマイグレーションスクリプトが必要になる
  • hydrateを使ってクエリ結果をEloquent Modelにマッピングする
    • hydrateするタイミングで動的にモデルを生成できる
    • hydrateしないことには何もできない(findなどクエリ系のメソッドは一切使えない)
  • fromSubを使って、Eloquent Modelfromにサブクエリをセットする
    • findなどクエリ系のメソッドが使える(更新系は除く)
    • サブクエリの結果に対して、クエリを発行することになるので、SQLが重くなる可能性がある

おわりに

3つの方法それぞれにメリットとデメリットがありますが、いずれにしても、肥大化するEloquent Modelを分割することにはなるので、それぞれに合った方法を採用してみてはいかがでしょうか?

個人的には、クエリが十分に早ければ、fromSubを使う方法がマイグレーションも必要ないし、採用しやすいのではないかと思います。

ではでは。

15
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?