You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel12+Filament4双SpatieMediaLibraryFileUpload的N+1查询优化问题

解决Laravel 12 + Filament v4 中Spatie Media Library的N+1查询问题

问题背景

在Laravel 12和Filament v4.0的用户编辑页面中,配置了两个SpatieMediaLibraryFileUpload组件(分别对应'avatars'和'cards'媒体集合),查询检测器提示存在N+1查询问题。尝试在UserResource的getEloquentQuery方法中添加->with(['media'])预加载,但问题未解决,仍出现多次重复的media表查询——日志显示共7条查询,其中2条重复,针对同一用户ID执行了3次media表查询。

现有代码

UserForm 组件配置

<?php

namespace App\Filament\Resources\Users\Schemas;

use App\Enums\User\UserGender;
use App\Enums\User\UserGrade;
use App\Enums\User\UserStatus;
use App\Rules\ArabicRule;
use App\Rules\MobileRule;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\ToggleButtons;
use Filament\Schemas\Schema;

class UserForm
{
    public static function configure(Schema $schema): Schema
    {
        return $schema
            ->components([
                // ---------- Avatar ----------
                SpatieMediaLibraryFileUpload::make('avatar')
                    ->key('user_avatar')
                    ->disk('cloudinary')
                    ->directory('users')
                    ->collection('avatars')
                    ->imageEditorAspectRatioOptions(['1:1'])
                    ->image()
                    ->avatar()
                    ->imageEditor()
                    ->alignCenter(),

                // ---------- Card ----------
                SpatieMediaLibraryFileUpload::make('card')
                    ->key('user_card')
                    ->disk('cloudinary')
                    ->directory('users')
                    ->collection('cards')
                    ->image()
                    ->imageEditor()
                    ->alignCenter(),
            ]);
    }
}

UserResource 预加载配置

public static function getEloquentQuery(): Builder
{
    return parent::getEloquentQuery()
        ->with(['media']);
}

查询日志

7 queries, 2 of which are duplicated.   Duration
8.01ms
select * from `media` where `media`.`model_id` in (1) and `media`.`model_type` = 'App\Models\User'  0.79ms
select `clubs`.*, `club_user`.`user_id` as `pivot_user_id`, `club_user`.`club_id` as `pivot_club_id`... 1.79ms
select * from `media` where `media`.`model_id` in (3) and `media`.`model_type` = 'App\Models\User'  0.93ms
select * from `media` where `media`.`model_id` in (3) and `media`.`model_type` = 'App\Models\User'  1.36ms
select * from `media` where `media`.`model_id` in (3) and `media`.`model_type` = 'App\Models\User'  1.00ms
select * from `users` where `slug` = '8e69fa51-6a33-4449-b862-d750f652d8ce' limit 1 0.95ms
select * from `users` where `id` = 1 limit 1    1.19ms

解决方案

1. 按指定媒体集合预加载

修改UserResource的getEloquentQuery方法,仅预加载编辑页面需要的两个媒体集合,避免组件因过滤集合发起重复查询:

public static function getEloquentQuery(): Builder
{
    return parent::getEloquentQuery()
        ->with(['media' => function ($query) {
            $query->whereIn('collection_name', ['avatars', 'cards']);
        }]);
}

2. 定义专属媒体关联(推荐)

在App\Models\User模型中为每个媒体集合定义单独的关联关系,让Filament组件直接复用预加载的关联数据:

// App\Models\User.php
public function avatars()
{
    return $this->media()->where('collection_name', 'avatars');
}

public function cards()
{
    return $this->media()->where('collection_name', 'cards');
}

然后在UserResource中预加载这两个关联:

public static function getEloquentQuery(): Builder
{
    return parent::getEloquentQuery()
        ->with(['avatars', 'cards']);
}

3. 排查额外查询触发点

检查是否存在其他逻辑重复调用$record->media或$record->getMedia(),比如自定义组件、表单回调等。确保所有媒体数据访问都基于预加载的集合,避免触发新的查询。

原理说明

原->with(['media'])仅预加载所有媒体数据,但Filament的SpatieMediaLibraryFileUpload组件在渲染时,会单独查询对应集合的媒体记录,导致重复查询。通过预加载指定集合或定义专属关联,组件会直接使用已预加载的数据,不再发起新的数据库请求,从而解决N+1问题。

内容的提问来源于stack exchange,提问作者John Deck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 16:33:23