Огляд ресурсів · Filament
Вступ
Ресурси - це статичні класи, які використовуються для побудови CRUD-інтерфейсів для ваших моделей Eloquent. Вони описують, як адміністратори можуть взаємодіяти з даними вашого застосунку за допомогою таблиць і форм.
Створення ресурсу
Щоб створити ресурс для моделі App\Models\Customer:
php artisan make:filament-resource Customer
Це створить кілька файлів у теці app/Filament/Resources:
.
+-- Customers
| +-- CustomerResource.php
| +-- Pages
| | +-- CreateCustomer.php
| | +-- EditCustomer.php
| | +-- ListCustomers.php
| +-- Schemas
| | +-- CustomerForm.php
| +-- Tables
| | +-- CustomersTable.php
Ваш новий клас ресурсу лежить у CustomerResource.php.
Класи в теці Pages використовуються для налаштування сторінок застосунку, які взаємодіють із вашим ресурсом. Усі вони є повносторінковими Livewire-компонентами, які ви можете змінювати як заманеться.
Класи в теці Schemas визначають вміст форм та інфолістів вашого ресурсу. Класи в теці Tables будують таблицю ресурсу.
Порада. Створили ресурс, а він не показується в меню навігації? Якщо у вас є політика моделі, переконайтеся, що метод
viewAny()повертаєtrue.
Прості (модальні) ресурси
Іноді моделі настільки прості, що записами достатньо керувати на одній сторінці, використовуючи модальні вікна для створення, редагування й видалення. Щоб згенерувати простий ресурс із модальними вікнами:
php artisan make:filament-resource Customer --simple
Ваш ресурс матиме сторінку «Manage» - це сторінка списку з доданими модальними вікнами.
Крім того, простий ресурс не має методу getRelations(), бо менеджери звʼязків показуються лише на сторінках редагування та перегляду, а в простих ресурсах їх немає. Усе інше без змін.
Автоматична генерація форм і таблиць
Щоб зекономити час, Filament може автоматично згенерувати форму і таблицю на основі стовпців бази даних вашої моделі - для цього є прапорець --generate:
php artisan make:filament-resource Customer --generate
Робота з мʼяким видаленням
Типово ви не зможете взаємодіяти з видаленими записами в застосунку. Якщо ви хочете додати до ресурсу можливість відновлювати, остаточно видаляти та фільтрувати видалені записи, використайте прапорець --soft-deletes під час генерації ресурсу:
php artisan make:filament-resource Customer --soft-deletes
Докладніше про мʼяке видалення читайте тут.
Генерація сторінки перегляду
Типово для ресурсу генеруються лише сторінки списку, створення та редагування. Якщо вам потрібна ще й сторінка перегляду, використайте прапорець --view:
php artisan make:filament-resource Customer --view
Вказання власного простору імен моделі
Типово Filament вважає, що ваша модель лежить у теці App\Models. Інший простір імен моделі можна передати прапорцем --model-namespace:
php artisan make:filament-resource Customer --model-namespace=Custom\\Path\\Models
У цьому прикладі модель має існувати за шляхом Custom\Path\Models\Customer. Зверніть увагу на подвійні обернені слеші \\ у команді - вони обовʼязкові.
Тепер під час генерації ресурсу Filament зможе знайти модель і прочитати схему бази даних.
Одночасна генерація моделі, міграції та фабрики
Щоб зекономити час на скафолдингу ресурсів, Filament може водночас згенерувати модель, міграцію та фабрику для нового ресурсу - прапорці --model, --migration і --factory працюють у будь-якій комбінації:
php artisan make:filament-resource Customer --model --migration --factory
Заголовки записів
Для ресурсу можна задати $recordTitleAttribute - назву стовпця моделі, за яким запис можна відрізнити від інших.
Наприклад, це може бути title статті блогу або name клієнта:
protected static ?string $recordTitleAttribute = 'name';
Це потрібно, щоб працювали такі можливості, як глобальний пошук.
Порада. Якщо одного стовпця недостатньо для ідентифікації запису, ви можете вказати назву аксесора Eloquent.
Форми ресурсу
Класи ресурсів містять метод form(), який будує форми на сторінках створення та редагування.
Типово Filament створює для вас окремий файл зі схемою форми, на який посилається метод form(). Так клас ресурсу лишається чистим і впорядкованим, інакше він може розростися:
use App\Filament\Resources\Customers\Schemas\CustomerForm;
use Filament\Schemas\Schema;
public static function form(Schema $schema): Schema
{
return CustomerForm::configure($schema);
}
У класі CustomerForm ви визначаєте поля та компонування форми:
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
public static function configure(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('name')->required(),
TextInput::make('email')->email()->required(),
// ...
]);
}
Метод components() визначає структуру форми. Це масив полів і компонентів компонування у тому порядку, в якому вони мають зʼявитися у формі.
Дивіться посібник у документації Forms про те, як будувати форми у Filament.
Порада. Якщо вам зручніше визначати форму безпосередньо в класі ресурсу, ви можете так і зробити, а клас схеми форми видалити зовсім:
use Filament\Forms\Components\TextInput; use Filament\Schemas\Schema; public static function form(Schema $schema): Schema { return $schema ->components([ TextInput::make('name')->required(), TextInput::make('email')->email()->required(), // ... ]); }
Приховування компонентів залежно від поточної операції
Метод hiddenOn() компонентів форми дозволяє динамічно приховувати поля залежно від поточної сторінки або дії.
У цьому прикладі ми приховуємо поле password на сторінці edit:
use Filament\Forms\Components\TextInput;
use Filament\Support\Enums\Operation;
TextInput::make('password')
->password()
->required()
->hiddenOn(Operation::Edit),
Або ж є скорочений метод visibleOn(), який показує поле лише на одній сторінці чи в одній дії:
use Filament\Forms\Components\TextInput;
use Filament\Support\Enums\Operation;
TextInput::make('password')
->password()
->required()
->visibleOn(Operation::Create),
Таблиці ресурсу
Класи ресурсів містять метод table(), який будує таблицю на сторінці списку.
Типово Filament створює для вас окремий файл таблиці, на який посилається метод table(). Так клас ресурсу лишається чистим і впорядкованим, інакше він може розростися:
use App\Filament\Resources\Customers\Tables\CustomersTable;
use Filament\Tables\Table;
public static function table(Table $table): Table
{
return CustomersTable::configure($table);
}
У класі CustomersTable ви визначаєте стовпці, фільтри й дії таблиці:
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\Filter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('name'),
TextColumn::make('email'),
// ...
])
->filters([
Filter::make('verified')
->query(fn (Builder $query): Builder => $query->whereNotNull('email_verified_at')),
// ...
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
Дивіться документацію tables, щоб дізнатися, як додавати стовпці, фільтри, дії та інше.
Порада. Якщо вам зручніше визначати таблицю безпосередньо в класі ресурсу, ви можете так і зробити, а клас таблиці видалити зовсім:
use Filament\Actions\BulkActionGroup; use Filament\Actions\DeleteBulkAction; use Filament\Actions\EditAction; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Filters\Filter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; public static function table(Table $table): Table { return $table ->columns([ TextColumn::make('name'), TextColumn::make('email'), // ... ]) ->filters([ Filter::make('verified') ->query(fn (Builder $query): Builder => $query->whereNotNull('email_verified_at')), // ... ]) ->recordActions([ EditAction::make(), ]) ->toolbarActions([ BulkActionGroup::make([ DeleteBulkAction::make(), ]), ]); }
Налаштування підпису моделі
Кожен ресурс має «підпис моделі» (model label), який автоматично генерується з назви моделі. Наприклад, модель App\Models\Customer отримає підпис customer.
Цей підпис використовується в кількох частинах інтерфейсу, і ви можете змінити його властивістю $modelLabel:
protected static ?string $modelLabel = 'cliente';
Або ж використайте getModelLabel(), щоб визначити динамічний підпис:
public static function getModelLabel(): string
{
return __('filament/resources/customer.label');
}
Налаштування підпису моделі в множині
Ресурси також мають «підпис моделі в множині», який автоматично генерується з підпису моделі. Наприклад, підпис customer перетвориться на customers.
Множинну форму підпису можна змінити властивістю $pluralModelLabel:
protected static ?string $pluralModelLabel = 'clientes';
Або ж задайте динамічний множинний підпис у методі getPluralModelLabel():
public static function getPluralModelLabel(): string
{
return __('filament/resources/customer.plural_label');
}
Автоматична капіталізація підпису моделі
Типово Filament автоматично робить перші літери кожного слова в підписі моделі великими в деяких частинах інтерфейсу. Наприклад, у заголовках сторінок, меню навігації та «хлібних крихтах».
Якщо ви хочете вимкнути цю поведінку для ресурсу, задайте в ньому $hasTitleCaseModelLabel:
protected static bool $hasTitleCaseModelLabel = false;
Елементи навігації ресурсу
Filament автоматично згенерує для вашого ресурсу елемент меню навігації, використовуючи множинний підпис.
Якщо ви хочете змінити підпис елемента навігації, використайте властивість $navigationLabel:
protected static ?string $navigationLabel = 'Mis Clientes';
Або ж задайте динамічний підпис навігації в методі getNavigationLabel():
public static function getNavigationLabel(): string
{
return __('filament/resources/customer.navigation_label');
}
Встановлення іконки навігації ресурсу
Властивість $navigationIcon приймає назву будь-якого Blade-компонента. Типово встановлено Heroicons. Проте ви можете створити власні компоненти іконок або встановити альтернативну бібліотеку.
use BackedEnum;
protected static string | BackedEnum | null $navigationIcon = 'heroicon-o-user-group';
Або ж задайте динамічну іконку навігації в методі getNavigationIcon():
use BackedEnum;
use Illuminate\Contracts\Support\Htmlable;
public static function getNavigationIcon(): string | BackedEnum | Htmlable | null
{
return 'heroicon-o-user-group';
}
Сортування елементів навігації ресурсу
Властивість $navigationSort дозволяє вказати порядок, у якому перелічені елементи навігації:
protected static ?int $navigationSort = 2;
Або ж задайте динамічний порядок елемента навігації в методі getNavigationSort():
public static function getNavigationSort(): ?int
{
return 2;
}
Групування елементів навігації ресурсу
Елементи навігації можна групувати, задавши властивість $navigationGroup:
use UnitEnum;
protected static string | UnitEnum | null $navigationGroup = 'Shop';
Або ж використайте метод getNavigationGroup(), щоб задати динамічний підпис групи:
public static function getNavigationGroup(): ?string
{
return __('filament/navigation.groups.shop');
}
Групування елементів навігації ресурсу під іншими елементами
Ви можете зробити елементи навігації дочірніми до інших елементів, задавши властивість $navigationParentItem. Батьківський елемент можна вказати або класом його сторінки чи ресурсу, або його підписом:
use App\Filament\Resources\Products\ProductsResource;
use UnitEnum;
protected static ?string $navigationParentItem = ProductsResource::class;
protected static string | UnitEnum | null $navigationGroup = 'Shop';
Або ж посилайтеся на батьківський елемент за його підписом:
use UnitEnum;
protected static ?string $navigationParentItem = 'Products';
protected static string | UnitEnum | null $navigationGroup = 'Shop';
Також ви можете визначати батьківський елемент динамічно методом getNavigationParentItem():
use App\Filament\Resources\Products\ProductsResource;
public static function getNavigationParentItem(): ?string
{
return ProductsResource::class;
}
Або ж повертайте підпис батьківського елемента:
public static function getNavigationParentItem(): ?string
{
return __('filament/navigation.groups.shop.items.products');
}
Батьківський і дочірній елементи мусять належати до однієї групи навігації. Якщо батьківський елемент має групу навігації, її треба задати й на дочірньому, інакше правильний батьківський елемент не вдасться визначити. Це стосується як посилання за класом, так і за підписом.
Порада. Якщо ви беретеся за третій рівень навігації в такий спосіб, варто розглянути кластери - логічне групування ресурсів і власних сторінок, які можуть мати власну окрему навігацію.
Генерація URL до сторінок ресурсу
Filament надає статичний метод getUrl() у класах ресурсів для генерації URL до ресурсів та окремих сторінок у них. Традиційно URL доводилося складати вручну або через хелпер route() Laravel, але ці способи вимагають знання слага ресурсу чи конвенцій іменування маршрутів.
Метод getUrl() без аргументів згенерує URL до сторінки списку ресурсу:
use App\Filament\Resources\Customers\CustomerResource;
CustomerResource::getUrl(); // /admin/customers
Ви також можете генерувати URL до окремих сторінок ресурсу. Назва кожної сторінки - це ключ масиву в масиві getPages() ресурсу. Наприклад, щоб згенерувати URL до сторінки створення:
use App\Filament\Resources\Customers\CustomerResource;
CustomerResource::getUrl('create'); // /admin/customers/create
Деякі сторінки в методі getPages() використовують параметри URL, як-от record. Щоб згенерувати URL до таких сторінок і передати запис, використайте другий аргумент:
use App\Filament\Resources\Customers\CustomerResource;
CustomerResource::getUrl('edit', ['record' => $customer]); // /admin/customers/edit/1
У цьому прикладі $customer може бути обʼєктом моделі Eloquent або ідентифікатором.
Генерація URL до модальних вікон ресурсу
Це особливо корисно, якщо ви використовуєте прості ресурси з однією сторінкою.
Щоб згенерувати URL для дії в таблиці ресурсу, передайте tableAction і tableActionRecord як параметри URL:
use App\Filament\Resources\Customers\CustomerResource;
use Filament\Actions\EditAction;
CustomerResource::getUrl(parameters: [
'tableAction' => EditAction::getDefaultName(),
'tableActionRecord' => $customer,
]); // /admin/customers?tableAction=edit&tableActionRecord=1
А якщо ви хочете згенерувати URL для дії на сторінці, наприклад CreateAction у заголовку, передайте її в параметр action:
use App\Filament\Resources\Customers\CustomerResource;
use Filament\Actions\CreateAction;
CustomerResource::getUrl(parameters: [
'action' => CreateAction::getDefaultName(),
]); // /admin/customers?action=create
Генерація URL до ресурсів в інших панелях
Якщо у вашому застосунку кілька панелей, getUrl() згенерує URL у межах поточної панелі. Ви можете вказати, з якою панеллю пов'язаний ресурс, передавши ідентифікатор панелі в аргумент panel:
use App\Filament\Resources\Customers\CustomerResource;
CustomerResource::getUrl(panel: 'marketing');
Налаштування запиту Eloquent для ресурсу
У Filament кожен запит до моделі ресурсу починається з методу getEloquentQuery().
Завдяки цьому легко застосувати власні обмеження запиту або скоупи моделі, які впливають на весь ресурс:
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->where('is_active', true);
}
Вимкнення глобальних скоупів
Типово Filament враховує всі глобальні скоупи, зареєстровані для вашої моделі. Проте це може бути не те, чого ви хочете, якщо, наприклад, потрібен доступ до мʼяко видалених записів.
Щоб це обійти, перевизначте метод getEloquentQuery(), який використовує Filament:
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->withoutGlobalScopes();
}
Або ж вилучіть окремі глобальні скоупи:
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->withoutGlobalScopes([ActiveScope::class]);
}
Докладніше про вилучення глобальних скоупів читайте в документації Laravel.
Налаштування URL ресурсу
Типово Filament генерує URL на основі назви ресурсу. Ви можете змінити це, задавши властивість $slug у ресурсі:
protected static ?string $slug = 'pending-orders';
Піднавігація ресурсу
Піднавігація дозволяє користувачеві переходити між різними сторінками в межах ресурсу. Зазвичай усі сторінки піднавігації стосуються того самого запису ресурсу. Наприклад, у ресурсі Customer піднавігація може містити такі сторінки:
- View customer, сторінка
ViewRecord, яка дає перегляд даних клієнта лише для читання. - Edit customer, сторінка
EditRecord, яка дозволяє користувачеві редагувати дані клієнта. - Edit customer contact, сторінка
EditRecord, яка дозволяє редагувати контактні дані клієнта. Ви можете дізнатися, як створити більше ніж одну сторінку редагування. - Manage addresses, сторінка
ManageRelatedRecords, яка дозволяє користувачеві керувати адресами клієнта. - Manage payments, сторінка
ManageRelatedRecords, яка дозволяє користувачеві керувати платежами клієнта.
Щоб додати піднавігацію до кожної сторінки «окремого запису» в ресурсі, додайте до класу ресурсу метод getRecordSubNavigation():
use Filament\Resources\Pages\Page;
public static function getRecordSubNavigation(Page $page): array
{
return $page->generateNavigationItems([
ViewCustomer::class,
EditCustomer::class,
EditCustomerContact::class,
ManageCustomerAddresses::class,
ManageCustomerPayments::class,
]);
}
Кожен елемент піднавігації налаштовується тими самими методами навігації, що й звичайні сторінки.
Порада. Якщо вам потрібна піднавігація для перемикання між цілими ресурсами та власними сторінками, вам, імовірно, потрібні кластери, які групують їх разом. Метод
getRecordSubNavigation()призначений для побудови навігації між сторінками, що стосуються конкретного запису всередині ресурсу.
Встановлення позиції піднавігації для ресурсу
Типово піднавігація рендериться на початку сторінки. Позицію для всіх сторінок ресурсу можна змінити властивістю $subNavigationPosition у ресурсі. Значення може бути SubNavigationPosition::Start, SubNavigationPosition::End або SubNavigationPosition::Top, щоб відрендерити піднавігацію як вкладки:
use Filament\Pages\Enums\SubNavigationPosition;
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::End;
Варіант SubNavigationPosition::Top рендерить піднавігацію як вкладки над вмістом сторінки.
Видалення сторінок ресурсу
Якщо ви хочете видалити сторінку зі свого ресурсу, просто видаліть файл сторінки з теки Pages ресурсу та її запис у методі getPages().
Наприклад, у вас може бути ресурс, записи якого ніхто не може створювати. Видаліть файл сторінки Create, а потім вилучіть її з getPages():
public static function getPages(): array
{
return [
'index' => ListCustomers::route('/'),
'edit' => EditCustomer::route('/{record}/edit'),
];
}
Видалення сторінки не видалить дії, які на неї посилаються. Такі дії відкриють модальне вікно замість того, щоб надсилати користувача на неіснуючу сторінку. Наприклад, CreateAction на сторінці списку, EditAction у таблиці або на сторінці перегляду, чи ViewAction у таблиці або на сторінці редагування. Якщо ви хочете прибрати ці кнопки, дії теж треба видалити.
Безпека
Авторизація
Для авторизації Filament враховує будь-які політики моделей, зареєстровані у вашому застосунку. Використовуються такі методи:
viewAny()повністю ховає ресурси з меню навігації й не дає користувачеві доступу до жодної сторінки.create()контролює створення нових записів.update()контролює редагування запису.view()контролює перегляд запису.delete()не дає видалити окремий запис.deleteAny()не дає видаляти записи масово. Filament використовує методdeleteAny(), бо перебирати кілька записів і перевіряти політикуdelete()не дуже продуктивно. Якщо під час використанняDeleteBulkActionви все ж хочете викликати методdelete()для кожного запису, використайтеDeleteBulkAction::make()->authorizeIndividualRecords(). Записи, які не пройдуть перевірку авторизації, не будуть опрацьовані.forceDelete()не дає остаточно видалити окремий мʼяко видалений запис.forceDeleteAny()не дає остаточно видаляти записи масово. Filament використовує методforceDeleteAny(), бо перебирати кілька записів і перевіряти політикуforceDelete()не дуже продуктивно. Якщо під час використанняForceDeleteBulkActionви все ж хочете викликати методforceDelete()для кожного запису, використайтеForceDeleteBulkAction::make()->authorizeIndividualRecords(). Записи, які не пройдуть перевірку авторизації, не будуть опрацьовані.restore()не дає відновити окремий мʼяко видалений запис.restoreAny()не дає відновлювати записи масово. Filament використовує методrestoreAny(), бо перебирати кілька записів і перевіряти політикуrestore()не дуже продуктивно. Якщо під час використанняRestoreBulkActionви все ж хочете викликати методrestore()для кожного запису, використайтеRestoreBulkAction::make()->authorizeIndividualRecords(). Записи, які не пройдуть перевірку авторизації, не будуть опрацьовані.reorder()контролює зміну порядку записів у таблиці.
Пропуск авторизації
Якщо ви хочете пропустити авторизацію для ресурсу, задайте властивості $shouldSkipAuthorization значення true:
protected static bool $shouldSkipAuthorization = true;
Захист атрибутів моделі
Filament надає JavaScript усі атрибути моделі, крім тих, що перелічені у $hidden вашої моделі. Так поводиться Livewire під час привʼязки моделі. Ми зберігаємо цю поведінку, щоб полегшити динамічне додавання та вилучення полів форми після її початкового завантаження, зберігаючи потрібні їм дані.
Небезпека. Хоча атрибути можуть бути видимі в JavaScript, користувач може реально редагувати лише ті, для яких є поле форми. Це не проблема масового призначення.
Щоб вилучити певні атрибути з JavaScript на сторінках редагування та перегляду, перевизначте метод mutateFormDataBeforeFill():
protected function mutateFormDataBeforeFill(array $data): array
{
unset($data['is_admin']);
return $data;
}
У цьому прикладі ми вилучаємо атрибут is_admin з JavaScript, бо форма його не використовує.
Попередження. Додавати стовпець до
$hiddenобовʼязково, а не просто рекомендовано, якщо він містить двійкові дані, які не є валідним UTF-8: наприклад, стовпціgeometry,pointчиblob. Оскільки Filament надає атрибути моделі JavaScript, ці значення відправляються в браузер як частина Livewire-запиту, але їх неможливо серіалізувати в JSON. Через це сторінка не завантажується, часто з пустим екраном і без жодної помилки в лозі Laravel.Додавання таких стовпців до масиву
$hiddenвашої моделі виключає їх із її масивного та JSON-представлення й вирішує проблему:protected $hidden = ['location'];Якщо вам потрібно працювати зі значенням, віддавайте його через аксесор, а не сирий стовпець.
Перекладаємо з офіційної документації, розділ за розділом, і не ховаємо недоперекладене. Помітили неточність у терміні чи реченні: напишіть, виправимо.