Плагины

Плагин добавляет сайту возможности, не трогая ядро: свои типы записей и блоки, страницы в админке, вывод на сайте, обработку заявок. Плагин — это папка в plugins/; включают и выключают его в разделе «Плагины».

Плагин за 10 минут

php artisan cms:make-plugin otzyvy --name="Отзывы" --author="Студия ARGELIT"

Появится папка plugins/otzyvy с готовым к включению плагином. Включите его в «Плагины» и дописывайте код в src/Otzyvy.php. Полный рабочий пример — плагин «Отзывы», а плагин «Объявление на сайте» (plugins/announcement) показывает страницу настроек в админке.

Из чего состоит плагин

plugins/
└── otzyvy/                 ← папка = имя плагина: латиница, цифры, «-», «_» (нельзя «cms» и «theme»)
    ├── plugin.json         ← описание (обязательно)
    ├── plugin.php          ← код, который подключается на каждом запросе, пока плагин включён (обязательно)
    ├── src/                ← классы плагина (подключаются сами, см. «autoload»)
    ├── views/              ← шаблоны Blade: view('otzyvy::settings')
    ├── assets/             ← стили, скрипты, картинки: plugin_asset('otzyvy', 'otzyvy.css')
    ├── routes/web.php      ← свои адреса на сайте
    ├── database/migrations/← свои таблицы
    └── uninstall.php       ← уборка при удалении плагина

plugin.json

{
    "name": "Отзывы",
    "description": "Отзывы клиентов с оценкой и блок для страниц.",
    "version": "1.0.0",
    "author": "Студия ARGELIT",
    "uri": "https://argelit.com",
    "requires_cms": "0.13",
    "requires_php": "8.3",
    "autoload": { "Studio\\Otzyvy\\": "src" }
}
Поле Зачем
name, description, version, author, uri показываются в списке плагинов
requires_cms минимальная версия CMS; на более старой плагин нельзя установить и включить
requires_php минимальная версия PHP
autoload пространство имён классов → папка (как PSR-4 в Composer): класс Studio\Otzyvy\Reviews — файл src/Reviews.php
sandbox false — плагин не показывается в демо-сайтах посетителей (режим CMS_SANDBOX); по умолчанию — показывается

plugin.php

Подключается на каждом запросе, пока плагин включён. Внутри доступна переменная $plugin — описание плагина ($plugin->slug — папка, $plugin->name, $plugin->version, $plugin->path). Лучшая практика — держать здесь только подписки на события, а работу — в классах:

<?php

use Studio\Otzyvy\Reviews;

(new Reviews($plugin))->register();

Не объявляйте в plugin.php функции (function my_func() {}): файл может подключаться повторно, и PHP упадёт с ошибкой «функция уже объявлена». Используйте классы или замыкания.

Включение, отключение, удаление

register_activation_hook($plugin, fn () => update_option('otzyvy_version', $plugin->version));
register_deactivation_hook($plugin, fn () => cache()->forget('otzyvy_list'));
  • При включении CMS подключает классы, выполняет миграции из database/migrations/ и вызывает событие activate_{папка}. Если на любом шаге ошибка — плагин остаётся выключенным, а текст ошибки показывается в «Плагинах».
  • При отключении — событие deactivate_{папка}; настройки и таблицы плагина остаются.
  • При удалении (кнопка «Удалить» у выключенного плагина) выполняется uninstall.php — здесь плагин удаляет свои настройки, таблицы и записи, затем удаляется папка:
<?php // uninstall.php
use Illuminate\Support\Facades\Schema;

Schema::dropIfExists('otzyvy_votes');
delete_option('otzyvy_version');

Шаблоны, стили, скрипты

  • Шаблоны из views/ доступны как папка::имя: view('otzyvy::blocks.list') — файл views/blocks/list.blade.php.
  • Файлы из assets/ отдаются по адресу /plugin-assets/{папка}/{файл} (только у включённых плагинов):
add_action('site_head', function () {
    echo '<link rel="stylesheet" href="'.e(plugin_asset('otzyvy', 'otzyvy.css')).'">';
});
add_action('site_footer', function () {
    echo '<script src="'.e(plugin_asset('otzyvy', 'otzyvy.js')).'" defer></script>';
});

К адресу добавляется время изменения файла — после правки браузер сразу возьмёт новую версию.

Вывод на сайте

Куда Как
в <head> событие site_head($context)
сразу после <body> (полоса объявления, счётчики) событие site_body_open($context)
перед </body> событие site_footer($context)
в текст записей фильтр the_content($html, $post)
свой блок, который редактор ставит куда угодно register_block_type() — см. Блоки
в шаблон темы функция вашего класса, которую вызывает тема, или своё событие

$context — что открыто на сайте (см. Темы).

Страница в админке

add_admin_page('otzyvy', 'Настройки отзывов', [Settings::class, 'page'], [
    'parent' => 'settings',          // подпункт раздела «Настройки»; без parent — свой пункт в меню
    'menu_title' => 'Отзывы',
    'capability' => 'manage_options',
]);
Параметр По умолчанию Что значит
menu_title заголовок страницы текст в меню
capability manage_options какое право нужно, чтобы открыть страницу
parent — раздел, в котором страница будет подпунктом: settings, tools, plugins, appearance, users, dashboard или раздел типа записей по его адресу в админке (posts, pages, reviews…)
icon plug значок, если страница — свой пункт меню
position 100 место в меню
show_in_menu true false — страница без пункта меню (открывается по ссылке)

Страница открывается по адресу /admin/page/otzyvy (в коде — admin_page_url('otzyvy')) и на GET, и на POST. Функция страницы получает Illuminate\Http\Request и возвращает шаблон (его содержимое встанет в админку с заголовком и уведомлениями) или ответ — например, перенаправление после сохранения:

public static function page(Request $request): View|RedirectResponse
{
    if (! $request->isMethod('post')) {
        return view('otzyvy::settings', ['perPage' => get_option('otzyvy_per_page', 3)]);
    }

    $data = $request->validate(['per_page' => ['required', 'integer', 'between:1,24']]);
    update_option('otzyvy_per_page', (int) $data['per_page']);

    return redirect(admin_page_url('otzyvy'))->with('status', 'Сохранено.');
}

В шаблоне — обычная форма на admin_page_url('otzyvy') с @csrf; ошибки и сообщение status админка покажет сама. Ссылку «Настройки» под названием плагина в списке плагинов добавляет фильтр:

add_filter('plugin_action_links_otzyvy', fn (array $links) => ['Настройки' => admin_page_url('otzyvy')] + $links);

Свои адреса на сайте

routes/web.php подключается раньше адресов ядра, с обычной защитой Laravel (сессии, CSRF):

<?php
use Illuminate\Support\Facades\Route;
use Studio\Otzyvy\Http\VoteController;

Route::post('otzyvy/{review}/vote', VoteController::class)->middleware('throttle:10,1')->name('otzyvy.vote');

Не занимайте адреса, которые могут совпасть с записями сайта: берите префикс плагина (otzyvy/…).

Свои таблицы

Миграции Laravel в database/migrations/ выполняются при включении плагина, а для уже включённых — при обновлении CMS и команде php artisan migrate. Таблицы называйте с префиксом плагина:

return new class extends Migration {
    public function up(): void
    {
        Schema::create('otzyvy_votes', function (Blueprint $table) {
            $table->id();
            $table->foreignId('post_id')->constrained('posts')->cascadeOnDelete();
            $table->string('ip', 45);
            $table->timestamps();
        });
    }
};

Типы записей, поля в редакторе, блоки, формы

  • Свой тип записей (отзывы, услуги, вакансии), таксономии, метаполя и блоки полей в редакторе — Контент.
  • Свои блоки для редактора — Блоки.
  • Заявки с форм: событие form_submitted($form, $entry), фильтры form_fields, form_recipients — Контент.

Если плагин сломался

Если плагин падает с ошибкой на любом запросе, CMS отключает его сама, сохраняет текст ошибки (с файлом и строкой) и показывает его в «Плагинах» — остальной сайт продолжает работать. После исправления плагин включают снова.

Публикация

Плагин распространяется как zip-архив его папки: внутри — папка otzyvy/ с plugin.json (или plugin.json прямо в корне архива — тогда папка получит имя архива: otzyvy-1.2.0.zip → otzyvy). Ставят его в «Плагины → Добавить плагин». Новая версия ставится так же, с галочкой «Заменить установленную версию»: включённый плагин остаётся включённым, его новые миграции выполняются сразу.

Перед публикацией:

  • поднимите version и проверьте requires_cms / requires_php;
  • проверьте, что uninstall.php убирает всё, что создаёт плагин;
  • не кладите в архив .git, node_modules, файлы с паролями.

На сайтах с CMS_FILE_MODS=false в .env (например, на демо) плагины из админки не устанавливаются — только копированием папки.