Багато Laravel-проєктів потребують маркетингового сайту поруч із застосунком - зі сторінками, які може оновлювати редактор, меню, яке можна переупорядковувати, та місцем для зберігання зображень. Filament покриває адмін-панель, а контентний шар зазвичай доводиться писати вручну. MKSine, розроблений Міраном Салехі, надає готове рішення: сторінки, пости, категорії, конструктор сторінок на основі блоків, теми, меню, медіабібліотеку та систему плагінів із життєвим циклом встановлення та активації. Пакет орієнтований на Filament 4 та 5.
Встановлення та налаштування
Встановіть пакет і налаштуйте панель Filament:
composer require miran/mksine
php artisan filament:install --panels
Після цього потрібно вручну відредагувати два файли. У AdminPanelProvider зареєструйте MksinePlugin::make() на панелі та видаліть блок ->pages([Dashboard::class]), оскільки MKSine реєструє власну панель керування за адресою /admin, і виконання обох призводить до помилки 500. У routes/web.php видаліть стандартний обробник Route::get('/'), оскільки MKSine обслуговує головну сторінку через активну тему. Тільки після цього запустіть інсталятор і створіть перший обліковий запис:
php artisan mksine:install --migrate
php artisan mksine:create-super-admin
Команда mksine:install публікує конфігурацію, виконує міграції та запускає shield:generate --all, який сканує панель у тому стані, в якому вона зареєстрована на той момент. Якщо MksinePlugin ще не додано до панелі, він не створить жодних дозволів MKSine, і для відновлення доведеться вручну запустити php artisan shield:generate --panel=admin --all.
Авторизація та права доступу
Авторизація працює через Filament Shield, який пакет підключає як залежність разом із перемикачем мов та полем select-tree. Shield записує класи політик у хост-застосунок під App\Policies, а MKSine прив'язує їх до власних моделей через Gate::policy(). Прив'язка захищена class_exists(), тому модель, політика якої ніколи не генерувалася, залишається без контролю, і всі операції з нею дозволені.
Інсталятор також переписує app/Models/User.php, додаючи контракт FilamentUser від Filament та трейт InteractsWithMksine від MKSine. Спочатку створюється резервна копія з міткою часу, і якщо патч не вдається застосувати, меню адміністратора показує лише панель керування.
Система плагінів
Плагін MKSine - це директорія під plugins/{id}/ з маніфестом plugin.php та класом, що реалізує PluginInterface. Маніфест визначає id, версію, namespace та клас плагіна:
return [
'id' => 'notes',
'name' => 'Notes',
'description' => 'Demo plugin',
'version' => '0.1.0',
'author' => 'Your Name',
'namespace' => 'Plugins\\Notes',
'plugin_class' => Plugins\Notes\NotesPlugin::class,
];
Генераторні команди створюють каркас плагіна, реєструють його та додають модель і ресурс Filament:
php artisan mks-plugin:make notes --author="Your Name"
php artisan mks-plugin:discover
php artisan mks-plugin:install notes
php artisan mks-plugin:activate notes
php artisan mks-plugin:make-model notes Note --migration
php artisan mks-plugin:migrate notes
php artisan mks-plugin:make-resource notes Note --model=Note
Виявлення сканує кожен plugin.php і кешує реєстр у bootstrap/cache/mks_plugins_discovery.php, щоб наступні завантаження читали кеш замість файлової системи. Після активації плагін завантажує свої ресурси, сторінки та віджети Filament із канонічних шляхів без жодної реєстрації на панелі. Його міграції залишаються в дереві плагіна і виконуються лише через mks-plugin:migrate {id}, а скомпільовані ресурси публікуються в public/plugins/{id}/.
Таблиця mks_plugins зберігає стан кожного плагіна: discovered, installed, active, inactive або failed. Якщо boot() плагіна викидає виняток, захисник завантаження позначає його як невдалий і записує повідомлення в mks_plugins.boot_error, а решта застосунку продовжує працювати. Деактивація плагіна зупиняє boot() і приховує його екрани адміністратора, залишаючи таблиці та рядки на місці. Дані видаляються лише при видаленні з опцією --delete-data.
Хуки та точки розширення
Точки розширення поділяються на дві родини. Хуки виявлення - це класи-слухачі, які сканує php artisan mks:discover, записуючи по одному рядку на слухача в mks_hooks. Рядок також робить слухача видимим в адміністратора, де суперадмін може ввімкнути або вимкнути його чи змінити пріоритет. Слухачі, позначені як системні, завжди виконуються. Потрібно повторно запускати виявлення після кожної зміни коду.
Хуки виконання - це замикання або зворотні виклики класів, зареєстровані через фасад Hooks:: всередині boot() плагіна. Вони залишаються в пам'яті, перереєструються при кожному запиті і ніде не відображаються в адміністратора. Відношення ресурсів, віджети, дії заголовка сторінки та фільтри виконання доступні лише таким чином. Видалення плагіна прибирає його хуки виконання.
Імена слухачів з підстановочними знаками, такі як post.*, не підтримуються. Фільтри виконання, зареєстровані через Hooks::addFilter(), не потрапляють у mks_hooks, тому їх не можна перемикати. Розширення форм і таблиць завжди виконуються синхронно. Лише FormHookManager перехоплює винятки слухача; хуки таблиць, ресурсів і сторінок дозволяють винятку поширюватися. Для важчої роботи слухач може реалізувати QueueableHookEventInterface і поставити себе в чергу.
Блоки конструктора сторінок
Блоки конструктора сторінок розширюють BaseBuilderComponent. Клас описує, як блок з'являється у вибірці, схему Filament для редагування та шаблон Blade для рендерингу на сайті:
use Miran\Mksine\Core\PageBuilder\BaseBuilderComponent;
class PriceTableBlock extends BaseBuilderComponent
{
public static function getType(): string
{
return 'acme_price_table';
}
public static function getName(): string
{
return __('acme-pricing::builder.price_table.name');
}
public static function getIcon(): string
{
return 'heroicon-o-table-cells';
}
public static function getCategory(): string
{
return self::CATEGORY_SECTIONS;
}
public static function getSchema(): array
{
return [
TextInput::make('title')->maxLength(255),
Repeater::make('plans')
->schema([
TextInput::make('name')->required()->maxLength(80),
TextInput::make('price')->required()->maxLength(40),
Toggle::make('featured')->default(false),
])
->reorderable(),
];
}
public static function getDefaultData(): array
{
return ['title' => '', 'plans' => []];
}
public static function getRenderView(): string
{
return 'acme-pricing::builder.price-table';
}
}
Автоматичного виявлення блоків немає. Кожен потрібно зареєструвати в ComponentRegistry, зазвичай із boot() плагіна:
use Miran\Mksine\Core\PageBuilder\ComponentRegistry;
app(ComponentRegistry::class)->register(PriceTableBlock::class);
Редактори перетягують блок на сторінку, а дерево зберігається в колонці builder_payload сторінки - JSON-колонка, приведена до масиву на моделі Page. Шаблон отримує масив $data блоку плюс $children для блоків-контейнерів. Сторінка, остання редагована рік тому, може містити payload, написаний під старішу схему, тому шаблон повинен встановлювати значення за замовчуванням для кожного ключа. Коли тип не зареєстрований або getRenderView() вказує на неіснуючий шаблон, рендерер перевіряє View::exists() і повертається до жовтої панелі «Unknown component type:», що вказує тип, який не вдалося вирішити.
Блоки не мають окремих дозволів Shield. Будь-хто, хто може редагувати сторінку, може додати будь-який зареєстрований блок, тому блок, що відкриває щось чутливе, повинен захищати свій відрендерений вивід.
Вкладки налаштувань
Сторінка налаштувань має основні вкладки для General та Permalinks, а Geo на власній сторінці. Плагіни додають вкладки через SettingsTabManager, а не редагуванням класу сторінки:
use Miran\Mksine\Core\Hooks\SettingsTabManager;
app(SettingsTabManager::class)->registerTab(
id: 'acme_seo',
label: fn () => __('acme-seo::settings.tab_label'),
schema: [
TextInput::make('acme_seo_meta_description')->maxLength(320),
Toggle::make('acme_seo_enable_og_tags')->default(true),
],
sortOrder: 50,
);
Вкладки - це лише групування інтерфейсу. Кожне поле зберігається в таблицю settings під власним ім'ям, а mks_setting('acme_seo_meta_description') зчитує його з кешем на запит. Оскільки ключ - це ім'я поля, плагін, що визначає site_name, перезаписує основний site_name, тому документація рекомендує префіксувати кожне ім'я поля плагіна. Збереження перезаписує негайно, без чернетки та аудиту.
Теми, меню та медіа
Тема - це або пакет, або директорія під themes/{id}/. Одна тема активна одночасно, вона надає шаблони Blade для вітрини разом із опублікованими CSS і JS, які суперадмін може редагувати з панелі.
Конструктор меню обробляє вкладеність та перетягування для зміни порядку. Елементи походять зі сторінок, постів, категорій або користувацьких URL, і кожне меню призначається на місце теми, таке як header або footer.
Медіабібліотека - це власне сховище файлів пакета на одному диску, а не обгортка навколо spatie/laravel-medialibrary. Таблиця з'єднання media_attachments прикріплює файли до будь-якої моделі, а форми вибирають із бібліотеки через компонент MediaPicker. Завантаження отримують мініатюри small, medium і large, а оптимізація зображень увімкнена за замовчуванням, хоча цей крок вимагає встановлення бінарних оптимізаторів, таких як jpegoptim, на сервері.
Переклади та консоль адміністратора
Ви можете редагувати файли перекладів для застосунку, плагінів і тем на панелі за мовою та файлом.
Сторінка консолі, обмежена для суперадмінів, виконує дозволений набір команд Artisan і Composer з живим виведенням і історією виконаних команд.
Вимоги та ліцензія
MKSine вимагає PHP 8.2, Laravel 11 та Filament 4 або 5. Filament 5 підвищує вимоги до Laravel 11.28, Livewire 4 та Tailwind 4. Це плагін спільноти, не розроблений командою Filament. Поточний тег - v1.5.1, тому читайте вихідний код перед розміщенням на production-сайті. Код разом із документацією, що охоплює розробку плагінів, створення блоків, розгортання та оновлення, доступний на GitHub.