Створення плагіна
Створення плагіна
Пройдемо шлях від порожньої теки до робочого плагіна з маршрутом, контролером і пунктом в адмінці. За основу зручно тримати перед очима plugins/leads/.
Швидкий старт: порожній плагін
Щоб не починати з нуля, завантажте мінімальний робочий плагін:
Завантажити порожній плагін (zip)
Усередині: manifest.json, plugin.php (публічний маршрут /starter, сторінка адмінки /admin/starter, пункт меню, міграція), контролери, шаблон адмінки, мови та docs/. Встановіть через Плагіни → Встановити із zip і натисніть «Увімкнути» — або розпакуйте теку starter/ у plugins/. Потім перейменуйте starter у свій slug.
Slug плагіна не повинен містити дефіс: із нього будується PHP-namespace (Celena\Plugin\<Slug>), а дефіс у namespace неприпустимий. Використовуйтеmypluginабоmy_plugin.
Нижче — як усе влаштовано, щоб зібрати своє з нуля.
З чого почати
- Придумайте
slug(латиниця, цифри, дефіс), наприкладreviews. - Створіть теку
plugins/reviews/і в ній —manifest.jsonтаplugin.php.
manifest.json
{
"slug": "reviews",
"name": "Відгуки",
"version": "1.0.0",
"author": "Ви",
"description": "Відгуки клієнтів: приймання з фронту і модерація в адмінці.",
"requires": { "php": ">=8.3", "celena": ">=1.0" },
"migrations": "migrations/"
}
plugin.php — точка входу
plugin.php підключається під час завантаження активного плагіна. Тут реєструють маршрути, хуки й теги. Контейнер доступний через Kernel::container().
<?php
declare(strict_types=1);
use Celena\Core\Http\Router;
use Celena\Core\Kernel;
use Celena\Core\Database\Connection;
use Celena\Core\Plugin\Hook;
use Celena\Plugin\Reviews\Controllers\ReviewsController;
use Celena\Plugin\Reviews\Admin\ReviewsAdminController;
$container = Kernel::container();
// 1. Ідемпотентний прогін міграцій (якщо є).
try {
$dir = __DIR__ . '/migrations';
if (is_dir($dir)) {
(new \Celena\Core\Database\MigrationRunner(
$container->make(Connection::class),
$container->make(\Celena\Core\Logger::class),
))->run($dir);
}
} catch (\Throwable) {}
// 2. Маршрути.
$router = $container->make(Router::class);
$router->post('/api/reviews/submit', ReviewsController::class . '@submit'); // публічний
$router->get('/admin/reviews', ReviewsAdminController::class . '@index'); // адмінка
// 3. Пункт у бічному меню адмінки.
Hook::addFilter('admin.sidebar', function (array $items): array {
$icon = new \Celena\Core\Template\Tags\IconTag();
$items[] = [
'svg' => $icon(['name' => 'star', 'size' => 22, 'class' => 'sb-ico']),
'label' => 'Відгуки',
'url' => '/admin/reviews',
'slug' => 'reviews',
];
return $items;
});
Маршрути/admin/реєструються БЕЗ спільного middleware — авторизацію перевіряє сам контролер (див. нижче). Публічні/api/доступні всім.
Контролери
PSR-4: клас Celena\Plugin\Reviews\Admin\ReviewsAdminController лежить у plugins/reviews/src/Admin/ReviewsAdminController.php.
Адмінський контролер
Успадковує Celena\Module\AdminBase\AdminController — він дає view(), redirect(), доступ до $this->auth, $this->options, і спільний layout адмінки:
<?php
declare(strict_types=1);
namespace Celena\Plugin\Reviews\Admin;
use Celena\Core\Database\Connection;
use Celena\Core\Http\Request;
use Celena\Core\Http\Response;
use Celena\Core\Template\Engine;
use Celena\Core\Security\Auth;
use Celena\Module\Settings\Options;
use Celena\Module\AdminBase\AdminController;
final class ReviewsAdminController extends AdminController
{
public function __construct(Engine $engine, Auth $auth, Options $options, private readonly Connection $conn)
{
parent::__construct($engine, $auth, $options);
}
public function index(Request $request): Response
{
if ($this->auth->user() === null) {
return Response::redirect('/admin/login');
}
$items = $this->conn->builder('reviews')->orderBy('created_at', 'desc')->get();
return $this->view('plugins/reviews/admin/index', ['items' => $items], 'reviews', 'Відгуки');
}
}
$this->view($template, $vars, $activeMenu, $title) рендерить шаблон у темі адмінки з бічним меню, сповіщеннями та поточним користувачем.
Публічний контролер
Фронт-контролер не успадковує AdminController; залежності приходять через конструктор (авто-впровадження):
namespace Celena\Plugin\Reviews\Controllers;
use Celena\Core\Database\Connection;
use Celena\Core\Http\Request;
use Celena\Core\Http\Response;
use Celena\Core\Security\Csrf;
final class ReviewsController
{
public function __construct(private readonly Connection $conn) {}
public function submit(Request $request): Response
{
if (!Csrf::validate((string) $request->input('_csrf'))) {
return Response::json(['error' => 'csrf'], 419);
}
$this->conn->builder('reviews')->insert([
'author' => \Celena\Core\Security\Filter::string((string) $request->input('author')),
'text' => \Celena\Core\Security\Filter::string((string) $request->input('text')),
'created_at' => date('Y-m-d H:i:s'),
]);
return Response::json(['ok' => true]);
}
}
Шаблони
Шаблони плагіна лежать у plugins/reviews/templates/. Рушій резолвить їх за префіксом plugins/<slug>/:
$this->view('plugins/reviews/admin/index', …);
// → plugins/reviews/templates/admin/index.tpl
Активна тема може перебити шаблон, створивши файл templates/<theme>/plugins/reviews/admin/index.tpl.
Асети, мови, документація
assets/reviews.cssіassets/reviews.js(імʼя = slug) підключаться автоматично тегом{plugin_assets}. Інші файли — прямим посиланням/public/assets/plugins/reviews/....languages/ru.json,languages/en.jsonпідхопляться автоматично; використовуйте{lang key="reviews.x"}.- Покладіть
docs/README.md— він відкриється в адмінці кнопкою документації плагіна.
Увімкнення
Зайдіть у Плагіни, натисніть «Увімкнути» біля вашого плагіна — застосуються міграції, підключиться plugin.php, скопіюються асети.
Далі: Хуки та події, Міграції та база даних, Безпека.