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
相关产品推荐
相关产品推荐

