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
で使えるwhere
やget
や
Illuminate\Database\Concerns\BuildsQueries
で使えるfind
やfirst
がまだ使えない状態となっている。
使えないといってもサジェスト的に使えないというだけで実際には使えるようになっている。
しかしサジェストしてくれるようになればコーディングも楽になるのは間違いない。
その前に、Illuminate\Database\Eloquent\Builder
とIlluminate\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(); // これができるようになった。
}
}