Filament 5: адмін-панелі
20 питань · ~20 хв · Версія v3.0
Увійдіть, щоб продовжити
Ресурси й схеми, таблиці й фільтри, дії й сповіщення, менеджери зв'язків, віджети, доступ і мультитенантність - питання всіх рівнів, від junior до senior.
- За спробу
- 20
- У пулі
- 48
- Проходжень
- 0
- Середній бал
- -
- Пройшли на 70%+
- -
Питання для підготовки
31 питанняFilament - фреймворк Server-Driven UI для Laravel: інтерфейси (адмінки, панелі) описуються на PHP через структуровані об'єкти, а не верстку.
public static function form(Schema $schema): Schema
{
return $schema->components([
TextInput::make('title')->required(),
Select::make('status')->options(Status::class),
]);
}
- Побудований на Livewire, Alpine.js і Tailwind CSS.
- Будівельні блоки: Resources, Forms, Tables, Actions, Infolists, Widgets.
- Компоненти ініціалізуються статичними
make()-методами; динамічні значення задаються замиканнями з утилітамиGet/Set.
Ресурс - це опис того, як модель виглядає в адмінці: таблиця, форма й сторінки навколо них.
php artisan make:filament-resource Vacancy --generate
Команда створює клас ресурсу, сторінки List/Create/Edit, а форму й таблицю виносить в окремі файли (Schemas/VacancyForm.php, Tables/VacancyTable.php). Прапорці --embed-schemas і --embed-table лишають їх усередині класу ресурсу.
Дві головні частини - форма й таблиця:
public static function form(Schema $schema): Schema
{
return $schema->components([
TextInput::make('title')
->required()
->maxLength(255),
Select::make('level')
->options(VacancyLevel::class)
->required(),
Toggle::make('is_published'),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('title')->searchable()->sortable(),
TextColumn::make('company.name')->label('Компанія'),
IconColumn::make('is_published')->boolean(),
])
->filters([
SelectFilter::make('level')->options(VacancyLevel::class),
]);
}
Що це дає без додаткового коду: список із пошуком, сортуванням і пагінацією, форми створення й редагування з валідацією, масові дії, фільтри.
На чому побудовано: Livewire відповідає за реактивність без написання JS, Alpine - за дрібні взаємодії в браузері, Tailwind - за оформлення. Тому Filament - це PHP-код, а не окремий фронтенд-застосунок.
Що варто знати одразу: форма й таблиця - це звичайні PHP-масиви обʼєктів, тож видимість поля, опції списку чи доступність дії задаються замиканнями й можуть залежати від користувача та стану запису.
Filament будує таблицю з Eloquent-запиту, тож усі звичайні проблеми продуктивності лишаються - але ховаються за декларативним синтаксисом.
N+1 у колонках. Колонка зі звʼязком робить запит на рядок:
TextColumn::make('company.name') // + 1 запит на кожен рядок
Лікується модифікацією запиту таблиці:
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->with(['company', 'technologies']);
}
Лічильники. ->counts('vacancies') замість завантаження всієї колекції заради count().
Дорогі обчислення в state(). Замикання виконується для кожного рядка, тож звернення до бази всередині - те саме N+1, лише написане інакше.
Сортування й пошук за звʼязком роблять join, і без індексу на зовнішньому ключі це помітно:
TextColumn::make('company.name')
->searchable() // where по приєднаній таблиці
->sortable()
Що ще сповільнює адмінку:
->paginated([10, 25, 50, 'all'])з опцієюallна великій таблиці вивантажує все в памʼять одним кліком користувача.- Глобальний пошук по багатьох ресурсах виконує запит на кожен зареєстрований ресурс.
- Мініатюри без кешу:
ImageColumn, що генерує прев'ю на льоту, робить це для кожного рядка. - Опції
Selectбез->searchable()вивантажують усі записи довідника у форму.
Як шукати причину: Debugbar або Telescope на сторінці ресурсу покажуть кількість запитів. Таблиця на 25 рядків має вкладатися в одиниці запитів; десятки - ознака незавантаженого звʼязку.
Дія (Action) у Filament - кнопка з необов'язковим модальним вікном і логікою, що виконується після підтвердження. Дії живуть у заголовку сторінки, в рядках таблиці, у масових операціях.
use Filament\Actions\Action;
use Filament\Forms\Components\Select;
Action::make('updateAuthor')
->label('Змінити автора')
->schema([
Select::make('authorId')
->label('Автор')
->options(User::query()->pluck('name', 'id'))
->required(),
])
->action(function (array $data, Post $record): void {
$record->author()->associate($data['authorId']);
$record->save();
})
Як це працює:
schema()описує поля модального вікна. Дані з форми приходять у замиканняaction()масивом$data, вже провалідованими.- У замикання автоматично підставляються залежності за іменем:
$record(поточний запис у таблиці чи на сторінці),$data,$livewire. fillForm()заповнює форму наявними даними.
Корисні налаштування:
->requiresConfirmation()- просте підтвердження без форми (для видалення, архівування).->visible(fn (Post $record) => ...)/->authorize('update')- показувати дію лише тим, кому можна.->successNotificationTitle('Збережено')- сповіщення після виконання.
Важливо: дія - не лише кнопка. Її логіка виконується на сервері через Livewire, тож перевірку прав роблять у самій дії (authorize), а не лише приховуванням кнопки.
Filament використовує політики моделей Laravel. Якщо для моделі ресурсу зареєстровано політику, Filament перевіряє її методи:
viewAny()- чи бачить користувач ресурс узагалі: без нього ресурс зникає з навігації, а сторінки віддають 403.view(),create(),update(),delete()- доступ до перегляду, створення, редагування й видалення конкретного запису.deleteAny(),forceDeleteAny(),restoreAny()- масові операції. Filament за замовчуванням не перевіряєdelete()для кожного запису окремо - це повільно. Якщо потрібно, у масової дії є->authorizeIndividualRecords().reorder()- зміна порядку рядків у таблиці.
class PostPolicy
{
public function viewAny(User $user): bool
{
return $user->hasRole('editor');
}
public function update(User $user, Post $post): bool
{
return $user->isAdmin() || $post->author_id === $user->id;
}
}
Що варто знати:
- Немає політики - немає обмежень: якщо політику не зареєстровано, Filament дозволяє все. Тому політика для кожної моделі в панелі - обов'язкова звичка.
- Перевірки повторюються на кожному Livewire-запиті, а не лише при відкритті сторінки: якщо доступ відібрали, наступна дія вже буде заборонена.
- Доступ до панелі визначає
canAccessPanel()на моделі користувача (інтерфейсFilamentUser). Без нього на проді в панель не пустить нікого. - Власні дії й сторінки політики ресурсу автоматично не покривають - їм потрібні
->authorize()чиcanAccess(). - Обмеження видимих рядків (редактор бачить лише свої пости) - через
getEloquentQuery()ресурсу, а не лише через політику.
Прочитати - ще не значить знати
20 питань, по одному на екран, ~20 хв. Після завершення - розбір кожної помилки з посиланням на питання.