1
0

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 3 years have passed since last update.

LaravelのModel周りの補完をできる限り完璧に行えるように解析してみる

Last updated at Posted at 2021-11-16
1 / 17

phpstormで定義元にジャンプしたときの参照元


Illuminate\Database\Eloquent\Model

  • $this->model::with();

Illuminate\Database\Eloquent\Builder

  • $this->model->where();
  • $this->model->get();

Illuminate\Database\Concerns\BuildsQueries

  • $this->model->find();
  • $this->model->first();

とりあえず部分的にメソッドを洗い出してみたが、登場人物としては3者洗出せた、はず。


各Modelは最終的にIlluminate\Database\Eloquent\Modelを継承していることになる作りになる。

<?php

namespace App\Http\Models;

use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\MustVerifyEmail;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\Access\Authorizable;

class User extends Model implements
    AuthenticatableContract,
    AuthorizableContract,
    CanResetPasswordContract
{
    use Authenticatable, Authorizable, CanResetPassword, MustVerifyEmail;
}

継承しているのでとりあえずwiまで打てば、withと補完が出てくる。

<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use App\Http\Models\User;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    public function __construct(User $user)
    {
        $relations = ['profile', 'Children']
        $user::with(relations)->where('age', '<=', 30 )->first();
    }
}

withは\Illuminate\Database\Eloquent\Builderを返り値としているので、冒頭の 洗い出しの通りそのままメソッドチェーンで->と打てば、findやfirstやgetなどがサジェストされるというわけだ。

/**
     * Begin querying a model with eager loading.
     *
     * @param  array|string  $relations
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public static function with($relations)
    {
        return static::query()->with(
            is_string($relations) ? func_get_args() : $relations
        );
    }

しかし今のままでは、$user->whereのようにいきなりwhereなどは使えず、
withなどを中継しないといけない作りになっている。


Illuminate\Database\Eloquent\Modelは各Modelが継承しているからpublicなメソッドにはアクセスができるが

Illuminate\Database\Eloquent\Builderで使えるwhereget

Illuminate\Database\Concerns\BuildsQueriesで使えるfindfirstがまだ使えない状態となっている。


使えないといってもサジェスト的に使えないというだけで実際には使えるようになっている。
しかしサジェストしてくれるようになればコーディングも楽になるのは間違いない。


その前に、Illuminate\Database\Eloquent\BuilderIlluminate\Database\Concerns\BuildsQueriesの関係だが、
前者はclassだが、後者はtraitだということからわかるように、後者は前者でuseされている。


従って、Illuminate\Database\Eloquent\Builderを使えるようにすればIlluminate\Database\Concerns\BuildsQueriesも使えるようになる。

<?php

namespace Illuminate\Database\Eloquent;

use BadMethodCallException;
use Closure;
use Exception;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Database\Concerns\BuildsQueries;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\ForwardsCalls;
use ReflectionClass;
use ReflectionMethod;

/**
 * @property-read HigherOrderBuilderProxy $orWhere
 *
 * @mixin \Illuminate\Database\Query\Builder
 */
class Builder
{
    use BuildsQueries, Concerns\QueriesRelationships, ForwardsCalls;

ここまでわかったらあとはphpdocの@mixinを使う。
各ModelとIlluminate\Database\Eloquent\Modelの間に基底クラスを作ってやれば良い。

<?php

namespace App\Core\Models\Bases;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;

/**
 * 全モデルで共通処理を担うモデルクラス。
 *
 * @property Carbon $created_at 作成日
 * @property Carbon $updated_at 更新日
 * @mixin Builder
 */
class BaseModel extends Model
{
    /**
     * @var array 書き換え禁止カラム
     */
    protected $guarded = ['id'];

    /**
     * BaseModel constructor.
     *
     * @param array $attributes
     */
    public function __construct(array $attributes = [])
    {
        parent::__construct($attributes);
    }
}
<?php

namespace App\Core\Models\Bases;

use Illuminate\Notifications\Notifiable;

/**
 * 対象モデルに継承することで、通知を行うことができる。
 */
class NotifiableModel extends BaseModel
{
    use Notifiable;
}
<?php

namespace App\Core\Models\Bases;

use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Auth\Authenticatable;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Auth\MustVerifyEmail;
use Laravel\Passport\HasApiTokens;

/**
 * Illuminate\Foundation\Auth\User のラッパークラス。
 * Authを機能させたいモデルに継承することでテーブル名が連動してAuth認証が働きかける。
 *
 * この基底モデルクラスを継承したモデルクラスは「通知」も処理することができる。(Notifiable)
 * NotifiableトレイトはRoutesNotifications->notify()を扱える。
 *
 * このモデルクラスは
 * 「Auth認証」(Authenticatable, Authorizable, CanResetPassword, MustVerifyEmail)
 * 「通知」(Notifiable)
 * 「API認証」(HasApiTokens)
 * 「ユーザーベースモデル」(BaseModel)
 * これらをラップした「サブ基底モデルクラス」である。
 */
class AuthModel extends NotifiableModel implements
    AuthenticatableContract,
    AuthorizableContract,
    CanResetPasswordContract
{
    use Authenticatable, Authorizable, CanResetPassword, MustVerifyEmail, HasApiTokens;
}
<?php

namespace App\Http\Models;

use App\Core\Models\Bases\AuthModel as Authenticatable;

class User extends Authenticatable
{
}
<?php

namespace App\Http\Models;

use App\Core\Models\Bases\BaseModel;

class Car extends BaseModel
{
}

これで、withを中継せずともwhereもサジェストするようになった。

<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use App\Http\Models\User;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    public function __construct(User $user)
    {
        $relations = ['profile', 'Children']
        $user::with(relations)->where('age', '<=', 30 )->first();
        $user->where('age', '<=', 30 )->first(); // これができるようになった。
    }
}

Modelの継承関係図

VP5D2i8m48NtEKL15uhq0YwashKYWg1kP4n63DP4aYcsnSFRTcp1MjszVEyZav_84pXARubOMeL948RFMR00Z_LI28vjiyioBZfyd-Uz_50KlNnbIPyrd99cJWUmDK_fmdsLq92MG9KJMqCE9FLfrkdYdnE3sQ17MeFt3-lKBq-qaPkjH_VrADDW81q2eJe2VVBqhjwSjsgAYKUZck7rIVZk4aXRCngIlQ8PYwYqoczv0G00.png

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?