- Опубликовано: 16 апр 2025
- 10
Гайд: Кастомная страница в Filament 3 с использованием класса Page
Создание страницы через Artisan
Начнем с генерации новой страницы с помощью специальной Artisan-команды Filament. В Filament 3 доступна команда make:filament-page
, которая автоматически создает класс страницы и соответствующее представление (Blade-шаблон). Например, чтобы создать страницу настроек, выполните команду:
php artisan make:filament-page Settings
После выполнения команды будут сгенерированы два файла:
-
Класс страницы – обычно помещается в каталог
app/Filament/Pages
. В нашем примере будет создан файлapp/Filament/Pages/Settings.php
. Этот класс наследуетFilament\Pages\Page
и будет содержать базовую заготовку страницы. -
Blade-шаблон страницы – файл представления размещается в директории
resources/views/filament/pages
. Для страницы Settings будет создан шаблонresources/views/filament/pages/settings.blade.php
. Этот шаблон отвечает за разметку страницы в интерфейсе Filament.
Структура класса Page и шаблона представления
Откройте сгенерированный класс страницы (например, Settings.php
). Он должен наследовать базовый класс Filament Page и может содержать некоторые свойства для настройки страницы. Важные моменты структуры класса:
-
Имя класса и наследование: класс должен расширять
Filament\Pages\Page
. Например:class Settings extends Page { // ... }
-
Свойство
$view
: обычно в классе указывается, какой Blade-шаблон использовать для этой страницы. Статическое свойство$view
содержит путь к представлению. Для страницы Settings по умолчанию будет:protected static string $view = 'filament.pages.settings';
Это соответствует файлу
resources/views/filament/pages/settings.blade.php
. Filament будет рендерить этот шаблон, когда пользователь переходит на страницу. -
Иконка и название в навигации (опционально): вы можете указать значок и метку для меню панели админки:
protected static ?string $navigationIcon = 'heroicon-o-cog'; protected static ?string $navigationLabel = 'Настройки';
Если эти свойства не указать, Filament сформирует название автоматически на основе имени класса, а иконка по умолчанию может быть пустой. В нашем примере мы устанавливаем иконку "шестеренка" и явно задаем название пункта меню "Настройки". После настройки класса, убедитесь, что Blade-шаблон (например,
filament/pages/settings.blade.php
) существует. Изначально он может содержать базовую обертку страницы, например:
<x-filament-panels::page>
<!-- Контент страницы будет здесь -->
</x-filament-panels::page>
Эта обертка <x-filament-panels::page>
обеспечивает оформление страницы согласно стилям Filament.
Подключение формы ($form) на странице
Теперь перейдем к добавлению формы на нашу кастомную страницу. Filament предоставляет мощный конструктор форм (Filament Forms), который можно использовать внутри кастомных страниц. Однако, просто определив метод getFormSchema()
в классе, форму вы не увидите – необходимо подключить поддержку форм к странице.
1. Подключение трейта и интерфейса HasForms: В классе страницы нужно реализовать интерфейс Filament\Forms\Contracts\HasForms
и подключить трейт Filament\Forms\Concerns\InteractsWithForms
. Это обеспечит класс всеми необходимыми свойствами и методами для работы с формой. Пример начала класса:
use Filament\Forms\Contracts\HasForms;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Pages\Page;
class Settings extends Page implements HasForms
{
use InteractsWithForms;
// ...
}
После этих изменений класс страницы становится Livewire-компонентом с формой, и у него появляется свойство $form
для работы с формой. Если не реализовать HasForms/InteractsWithForms, методы формы (например, getFormSchema()
) вызваны не будут, и форма не отобразится. Именно по этой причине часто "не работает" getFormSchema()
– просто Page сам по себе не знает про форму, пока мы не подключим HasForms.
2. Определение схемы формы: Есть два основных способа описать поля формы в Filament:
-
Через метод
getFormSchema()
: Этот способ заключается в определении защищенного метода, возвращающего массив компонентов формы. Filament вызовет его внутри трейтаInteractsWithForms
и сформирует объект формы. Например, добавим в класс Settings метод:use Filament\Forms\Components\TextInput; class Settings extends Page implements HasForms { use InteractsWithForms; // ... protected function getFormSchema(): array { return [ TextInput::make('site_name')->label('Название сайта')->required(), TextInput::make('site_email')->label('Email сайта')->email(), ]; } }
Здесь мы используем компоненты Filament Forms – два поля ввода (
TextInput
) для примера настроек: название сайта и email. МетодgetFormSchema()
возвращает массив таких полей ,TextInput%3A%3Amake('site_name')%2C ]%3B)). Filament автоматически обернет эти поля в объект формы и свяжет с Livewire-свойствами. -
Через метод
form()
с объектом Form: Новый способ, появившийся в Filament 3 – определение методаpublic function form(Form $form): Form
. Вы можете строить форму через предоставленный объект$form
. Результат аналогиченgetFormSchema()
, но позволяет настроить дополнительные параметры формы (например, state path). Пример альтернативного определения: -
use Filament\Forms\Form; use Filament\Forms\Components\TextInput; class Settings extends Page implements HasForms { use InteractsWithForms; public ?array $data = []; // массив для состояния формы (optional) public function form(Form $form): Form { return $form ->schema([ TextInput::make('site_name')->label('Название сайта')->required(), TextInput::make('site_email')->label('Email сайта')->email(), ]) ->statePath('data'); } }
В этом подходе мы настраиваем форму используя Fluent API: вызываем
$form->schema([...])->statePath('data')
. СвойствоstatePath
указывает, куда сохранять состояние формы (в данном случае в массив$data
внутри компонента). Этот метод является предпочтительным в Filament 3, и он работает так же, как в ресурсах Filament.
Оба подхода допустимы. Если вы используете getFormSchema()
, Filament сам создаст форму и сохранит ее состояние во внутреннем свойстве $form
. Если используете метод form()
, вы более явно строите объект формы. Для простоты в нашем гайде будем считать, что используем getFormSchema()
, но важно понимать, что необходимо реализовать HasForms + InteractsWithForms независимо от выбранного способа.
Рендеринг формы в шаблоне
После того, как мы описали схему формы в классе, нужно отобразить форму на странице через Blade-шаблон. Filament не рендерит форму автоматически на кастомной странице, поэтому мы вручную добавим форму в шаблон.
Откройте файл resources/views/filament/pages/settings.blade.php
(созданный Artisan’ом). Внутри обертки <x-filament-panels::page>...</x-filament-panels::page>
добавьте компоненты для формы. Filament предоставляет специальный Blade-компонент <x-filament-panels::form>
для оформления формы.
Также можно использовать стандартный HTML-формат. Рассмотрим вариант с Blade-компонентом:
<x-filament-panels::page>
<x-filament-panels::form wire:submit.prevent="save">
{{ $this->form }}
<div class="mt-4">
<x-filament::button type="submit">
Сохранить
</x-filament::button>
</div>
</x-filament-panels::form>
</x-filament-panels::page>
Разберем этот шаблон:
-
<x-filament-panels::form>
– обертка для формы, которая автоматически применяет стили Filament. Атрибутwire:submit.prevent="save"
навешивает на форму событие Livewire для обработки отправки (методsave
в классе компонента). -
{{ $this->form }}
– выводит саму форму, сгенерированную Filament (согласно нашей схеме изgetFormSchema()
илиform()
метода). Переменная$this->form
доступна благодаря трейту InteractsWithForms и содержит готовую форму для рендеринга. - Блок с кнопкой
<x-filament::button type="submit">Сохранить</x-filament::button>
– отображает кнопку отправки формы. Мы используем Filament Blade-компонентbutton
для конституентного стиля (можно указать стили, размер и т.д.). Типsubmit
обеспечивает отправку формы по нажатию.
Обратите внимание: без вывода $this->form
в шаблоне форма не появится. Многие начинающие могут определить getFormSchema()
и забыть, что на странице нужно явно отрисовать форму. Здесь мы это сделали – поместив {{ $this->form }}
внутри формы.
Альтернативный способ рендеринга – не использовать <x-filament-panels::form>
, а написать обычный <form>
с нужными атрибутами. Результат будет аналогичный. Главное, чтобы внутри формы был вывод $this->form
и кнопка для отправки. Использование же компонентов Filament упрощает стилизацию и делает код чище.
Кнопка "Сохранить" на кастомной странице
В ресурсах Filament (например, на странице создания/редактирования записи) кнопки сохранения появляются автоматически. Однако для кастомной Page-страницы разработчик должен сам добавить кнопку в интерфейс. В примере выше мы уже добавили кнопку "Сохранить" вручную в Blade-шаблоне. Этот подход правильный: Filament не добавит кнопку автоматически, так как он не знает, что и как вы хотите сохранить на вашей странице.
Еще один подход – использовать Filament Actions (действия) для формы. Например, можно определить метод getFormActions()
в классе страницы, возвращающий массив из Filament\Actions\Action::make('save')
. Но для простоты мы используем прямую кнопку в шаблоне. Это совершенно нормально для кастомной страницы.
Итого: чтобы отобразить кнопку "Сохранить", убедитесь, что в вашем Blade-шаблоне внутри формы есть кнопка с type="submit"
(либо Blade-компонент x-filament::button
, либо обычный <button>
). Это позволит пользователю отправить данные формы.
Обработка отправки формы: сохранение данных в базу
Теперь реализуем логику сохранения введенных данных. В нашем случае, мы хотим сохранить значения нескольких TextInput
в таблицу настроек (модель Setting
) и обновить конфигурацию приложения config()
.
Предположим, модель Setting
хранит пары ключ-значение (например, key = 'site_name'
, value = 'My Site'
). Мы будем сохранять настройки по этим ключам. В классе страницы (Settings) добавим метод, указанный в атрибуте wire:submit
нашей формы – например, метод save()
:
use App\Models\Setting;
use Filament\Support\Exceptions\Halt;
use Filament\Notifications\Notification;
class Settings extends Page implements HasForms
{
use InteractsWithForms;
// ... схема формы определена выше ...
public function save(): void
{
// Получаем данные формы
$data = $this->form->getState();
try {
// Сохраняем каждое поле в таблицу настроек
Setting::updateOrCreate(
['key' => 'site_name'],
['value' => $data['site_name']]
);
Setting::updateOrCreate(
['key' => 'site_email'],
['value' => $data['site_email']]
);
// Обновляем runtime-конфигурацию приложения
config(['app.site_name' => $data['site_name']]);
config(['app.site_email' => $data['site_email']]);
} catch (Halt $exception) {
// Если Filament отклоняет сохранение (например, валидация), прерываем процесс
return;
}
// Отправляем уведомление об успешном сохранении
Notification::make()
->title('Настройки сохранены')
->success()
->send();
}
}
Разберем этот код:
-
$data = $this->form->getState();
– получаем все значения полей формы в виде массива. МетодgetState()
возвращает ассоциативный массив['имя_поля' => значение, ...]
. - С помощью метода Eloquent
updateOrCreate
мы сохраняем каждую настройку. Для поляsite_name
ищем запись с ключом'site_name'
и обновляем ее значение. Аналогично дляsite_email
. Вы можете использовать и другие подходы (например, если у вас модель настроек реализована иначе), главное – сохранить новые значения. - Обновляем конфигурацию приложения через хелпер
config([...])
. Здесь мы предполагаем, что в конфиге (например, config/app.php или другом) есть соответствующие параметры, которые нужно обновить. Вызовconfig(['app.site_name' => ...])
динамически меняет значение в текущем сеансе. Это полезно, если другие части приложения читают эти настройки черезconfig()
. Учтите: если конфигурация закеширована, такие изменения носят временный характер. В рамках работы Livewire-компонента они будут применены. - Блок
try { ... } catch (Halt $exception)
используется на случай, если Filament выбросит исключениеHalt
. Такое может произойти, например, если сработает валидация, и Filament решит прервать дальнейшее выполнение метода. Мы перехватываемHalt
и простоreturn
, чтобы избежать ненужных действий после прерывания. (В данном коде валидация не показана, но её можно настроить через$rules
или->required()
в полях, Filament сам валидирует поля, и при ошибках методsave
не выполнит содержимое до конца.) - После успешного сохранения вызываем отправку уведомления (рассмотрено ниже).
Таким образом, при нажатии на кнопку "Сохранить" форма отправится, вызовется метод save()
, и данные запишутся в базу (таблицу settings
). Если у вас другая логика (например, нужно сохранять в связанную модель или вызывать сервис), реализуйте её внутри метода.
Отображение сообщения об успехе
После сохранения настроек, хорошо бы уведомить пользователя, что операция прошла успешно. Filament имеет встроенную систему уведомлений (Notifications). В коде выше мы уже задействовали её через фасад Notification::make()
. Разберем, как это работает.
Чтобы показать уведомление, достаточно добавить:
use Filament\Notifications\Notification;
// ... внутри метода save() после сохранения ...
Notification::make()
->title('Настройки сохранены')
->success()
->send();
Этот fluent-вызов формирует и отображает всплывающее уведомление. Мы указываем заголовок уведомления (title
), помечаем его как успешное (success()
– обычно окрашивает в зеленый цвет) и вызываем send()
для отправки на фронтенд.
Filament позаботился о том, чтобы в панели админки Laravel уже был подключен компонент Livewire для уведомлений (обычно @livewire('notifications')
встроен в layout Filament Panel). Поэтому вызов Notification::make()->send()
мгновенно покажет сообщение пользователю.
Вы можете настроить и другие свойства уведомления (текст сообщения, иконку, длительность показа и т.д.), но для нашего гайда достаточно показать простой кейс – уведомление об успешном сохранении.
Теперь, когда пользователь нажмет "Сохранить", после записи данных он увидит всплывающее сообщение "Настройки сохранены".
Расширение страницы: вкладки, повторители и прочее
Наша текущая страница настроек довольно простая – два текстовых поля. Одним из преимуществ подхода с кастомной Page и Filament Forms является легкость масштабирования формы. Вы можете добавлять новые поля, группы полей и даже сложные компоненты без изменения общей структуры страницы.
Вот несколько способов расширения функциональности:
-
Вкладки (Tabs): Если настроек много, имеет смысл разделить форму на вкладки. Filament Forms предоставляет компонент
Tabs
. Вы можете обернуть части схемы формы во вкладки. Например, разделим настройки на "Общие" и "Безопасность":use Filament\Forms\Components\Tabs; use Filament\Forms\Components\Tabs\Tab; protected function getFormSchema(): array { return [ Tabs::make('SettingsTabs')->tabs([ Tab::make('Общие')->schema([ TextInput::make('site_name')->label('Название сайта')->required(), TextInput::make('site_email')->label('Email сайта')->email(), ]), Tab::make('Безопасность')->schema([ // здесь можно добавить поля безопасности, например пароль админа или ключи API TextInput::make('admin_password')->label('Пароль администратора')->password(), ]), ]), ]; }
Такой подход поместит поля в две вкладки внутри единой формы. По умолчанию открыта будет первая вкладка ("Общие"), между ними можно переключаться. Это удобно для организации большого количества опций.
-
Повторители (Repeater): Если требуется вводить множественные повторяющиеся данные (например, список контактных телефонов, адресов, социальных ссылок и т.д.), можно использовать компонент
Repeater
. Repeater позволяет вложить набор полей, которые пользователь может добавлять или удалять динамически. Пример добавления повторителя:use Filament\Forms\Components\Repeater; use Filament\Forms\Components\TextInput; protected function getFormSchema(): array { return [ // ... другие поля ... Repeater::make('contacts') ->label('Контакты') ->schema([ TextInput::make('phone')->label('Телефон'), TextInput::make('email')->label('Email'), ]) ->columns(2), // показывать поля повторителя в две колонки ]; }
В этом примере поле
contacts
будет представлять собой список элементов, каждый из которых содержит два подполя: телефон и email. Пользователь сможет нажать "Add" (добавить) несколько контактов. Данныеcontacts
в итоге будут массивом, который можно сохранить (часто в JSON поле базы). -
Другие компоненты: Filament Forms предлагает много готовых компонентов – выпадающие списки Select, переключатели Toggle, поле DatePicker для дат, FileUpload для загрузки файлов, и др. Добавлять их можно так же, как мы добавляли TextInput, изменяя схему формы. Например, можно добавить
Select::make('timezone')
для выбора часового пояса, или группу переключателей черезCheckboxList::make('roles')
, и т.д. Эти компоненты настраиваются через методы (options, multiple, reactive и пр.) согласно документации Filament. -
Расширение логики: Класс Page не ограничивается одной формой. Можно добавить виджеты (Widgets) на страницу, дополнительные действия (Actions) в шапке, или даже несколько форм. Если в будущем вам понадобится, например, вторую форму на этой же странице (скажем, отдельный блок настроек, который сохраняется отдельно), вы можете реализовать это, хотя в Filament 3 есть ограничения на несколько форм в одном Livewire-компоненте. Часто вместо этого делают просто одну форму с разделами или вкладками. Также Page-класс можно расширять, используя Livewire хуки (
mount
,render
и т.д.) для загрузки данных, применения фильтров и пр.
В итоге, кастомная страница на Filament 3, созданная через Page
, предоставляет гибкость: вы получаете полноэкранную страницу в админ-панели, полностью контролируете какие элементы на ней находятся. В нашем гайде мы разобрали:
- Создание страницы и ее шаблона.
- Подключение Filament Forms и описание полей.
- Рендеринг формы и ручное добавление кнопки сохранения.
- Обработку сохранения: запись в базу (модель Setting) и обновление
config()
. - Отображение уведомления об успехе пользователю.
- Возможности по расширению формы (Tabs, Repeater и др.).
Следуя этим шагам, вы можете создать свою страницу настроек (или любую другую кастомную страницу) без использования Resource или SettingsPage, полностью контролируя логику. Получившаяся форма проста, и при этом ее легко масштабировать под будущие требования. Enjoy! 😉
Была статья полезной: