4
1

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 admin】columnメソッドの注意点

Last updated at Posted at 2021-09-27

始めに

  • Laraveladminを使っていると必ず出てくるGridクラス内のcolumnメソッドで色々ハマった
  • その代わりに色々知れたことをまとめる

その1

columnメソッド内でwith(Eager Loading)の処理をしている

  • Eager Loadingの成立確認すべく、get()をしてみた
$grid = new Grid(new Order());

$grid->model()->query()
    ->with(['orderable' => function (MorphTo $morphTo) {
    $morphTo->morphWith([
        User::class => ['userAttribute'],
    ]);
}])->get();

$grid->column('id', 'ID');
...
  • すると、以下エラーが発生

Method Illuminate\Database\Eloquent\Collection::with does not exist.

  • Eager Loadingは正常に機能(写真の通り)
  • ここでいうCollectionとは$grid->model()->...->get()の結果であると推測できるので、それ以降の処理でwithメソッドが使われていると推測
  • columnメソッドを深くたどると、やはりwithを使用していた

※columnメソッド→addRelationColumnメソッド

vendor\encore\laravel-admin\src\Grid.php
    protected function addRelationColumn($name, $label = '')
    { 
        // 1
        list($relation, $column) = explode('.', $name);
        $model = $this->model()->eloquent();
        ...
        $name = ($this->shouldSnakeAttributes() ? Str::snake($relation) : $relation).'.'.$column;
        // 2
        $this->model()->with($relation); // ココ!!!

        return $this->addColumn($name, $label)->setRelation($relation, $column);
    }
  • // 2にて、$gridのモデルに対して、withを実行
    • 今回ではOrderモデル($this->model())に対して、'orderable'をEager Loadingしている
    • column内でEager Loadingを作っているとは思わなかった

その2

columnメソッドでリレーションを張れる(Eager Loadingができる)のは1モデルだけ

  • 上記ソースコード内の//1でリレーション先のモデルと、取得するカラムを分割している

例えば、こうなる

example.php
$grid->column('orderableWithTrashed.deleted_at', '退会日時');

$relation(取得先リレーション名) = 'orderableWithTrashed'
$column(取得先カラム名) = 'deleted_at'
  • 要は columnメソッドでリレーションを張れる(Eager Loadingができる)のは1モデルだけ ということ
    • 二つ目以降の.(ドット)は切り捨てられる

その3

columnメソッド以前にEager Loadingしたリレーションは上書きされる

  • columnメソッドの第1引数でリレーションを張った場合、columnメソッド以前にEager Loadingしたリレーションは上書きされる
    • 以下はuserAttributeでのEager Loadingが上書きされ、displayメソッド内でuserAttributeメソッドによるアクセスをすると、N+1が発生する例
sample.php
$grid = new Grid(new Order());
// Eager Loding...希望通りEagerLoading
$grid->model()->query()
    ->with(['orderable' => function (MorphTo $morphTo) {
    $morphTo->morphWith([
        User::class => ['userAttribute'],
    ]);
}]);
// columnメソッドで値取得=Eager Loadingの上書きしてる...
$grid->column('orderable.age', __('年齢'))
    ->display(
        function ($adCode) {
        // ゲストの時
            if($this->orderable_type === 'GuestUser'){
                return $this->orderable->age;
            }
        // ユーザーの時
            return $this->orderable->userAttribute->age;
        }
    );

その4

同じgridの中で一つでもcolumnメソッドの引数でEager Loadingすれば、他のcolumnメソッドでEagerLodingしなくてよい

↓例

  • 以下3つのカラムをgridで取得
    1, Orderモデル id
    2, Userモデル/Guestモデル ad_code
    3, Userモデル/Guestモデル name
    ※2, 3, についてはorderableメソッドによるポリモーフィックで各モデルにアクセス

↓成功例①

$grid = new Grid(new Order());
$grid->column('id', __('admin.app.features.order.id'));
// リレーションを張るメソッドを引数に追加
$grid->column('orderable.ad_code', __('ad_code'));
// リレーションを張るメソッドを引数に追加
$grid->column('orderable.name', __('name'));
  • これが以下でも成功する。

↓成功例②

$grid = new Grid(new Order());
$grid->column('id', __('admin.app.features.order.id'));
// リレーションを張るメソッドを引数に追加
$grid->column('orderable.ad_code', __('ad_code'));
// リレーションを張るメソッドを削除
$grid->column('name', __('name'));
  • 理由は以下の通り
    • ad_codeのcolumnメソッド処理時にcolumnメソッド内部でwith(=Eager Loding)が実行
    • 他のcolumnメソッド内でも上記EagerLodingを踏襲して利用できるため
  • 試しに以下ソースコードのようにorderableメソッドを全て削除するとN+1が発生

↓失敗例②...N+1が発生

$grid = new Grid(new Order());
$grid->column('id', __('admin.app.features.order.id'));
// リレーションを張るメソッドを削除
$grid->column('ad_code', __('ad_code'));
// リレーションを張るメソッドを削除
$grid->column('name', __('name'));

所見

  • なかなか曲者のcolumnメソッドでした…
4
1
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
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?