Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

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 хв. Після завершення - розбір кожної помилки з посиланням на питання.