Разработка кастомного плагина October CMS
Представьте: готовый плагин из маркетплейса не даёт нужной гибкости — форма не собирает кастомные поля, фильтрация по связанным моделям не работает, API-интеграция виснет. Выход — разработать плагин под себя. За 5 лет мы выпустили более 50 кастомных решений для October CMS, и этот опыт позволяет делать их быстро, без сюрпризов.
Как кастомный плагин решает бизнес-задачи?
Плагин в October CMS — это полноценный Laravel-пакет, который регистрирует компоненты, модели, маршруты и бэкенд-контроллеры. Он может заменить целый микросервис: управлять отзывами, интегрироваться с 1С или CRM, выводить кастомные фильтры. В отличие от доработок через копипасту в тему, плагин легко обновляется, не ломает ядро и повторно используется на других сайтах проекта.
Почему кастомный плагин лучше доработки готового?
Править чужой код — рисковать стабильностью. При каждом обновлении плагина ваши правки затрутся. Кастомный плагин пишется с нуля под вашу архитектуру: мы контролируем каждую таблицу, каждое событие. Сравните: на доработку существующего плагина уходит 2–3 дня, но потом ещё неделя на отлов багов. Новый плагин — 5 дней и готово, с гарантией и документацией.
Какие проблемы решаем?
Чаще всего клиенты сталкиваются с отсутствием нужных полей в формах, неоптимальными SQL-запросами (N+1) и невозможностью гибко настроить права доступа. Мы закрываем эти проблемы на этапе проектирования: используем eager loading, кеширование через Cache::remember и кастомные валидаторы.
Процесс разработки плагина
Как мы разрабатываем?
- Анализ — получаем от вас ТЗ или описываем требования вместе. Фиксируем модели, связи, компоненты, права доступа.
- Проектирование — рисуем ER-диаграмму, схему компонентов, бэкенд-навигацию.
- Реализация — пишем миграции, модели, компоненты, контроллеры и события. Код покрываем тестами (Pest/PHPUnit).
- Интеграция — подключаем плагин к вашему сайту, настраиваем кеширование, оптимизируем запросы.
- Деплой и документация — передаём архив с composer.json, README, конфиги, а также обучение вашей команды.
Что входит в состав плагина?
- Исходный код плагина с лицензией MIT (или вашей)
- composer.json с автозагрузкой
- Миграции и seed-данные
- Бэкенд-контроллеры и меню навигации
- Компоненты для фронтенда (Twig/Partial)
- События и хуки для расширения
- Тесты (если нужно)
- README с описанием установки и использования
- Поддержка 30 дней после сдачи
Типичные ошибки и их предотвращение
Какие ошибки допускают чаще всего?
-
N+1 запросы в компонентах — забывают про
with()иload(). Мы используем eager loading и кеширование черезCache::remember. - Жёсткая привязка к ID в навигации — при переносе на другой сервер ломается. У нас — автоматическое построение через
Backend::url(). - Отсутствие валидации — пользователь вводит что угодно. Ставим
Validationтрейт с правилами. - Игнорирование событий — не подписываются на
cms.page.beforeDisplay, из-за чего не срабатывает кастомизация. Мы всегда используем событийную модель.
Пример из практики: интеграция с CRM за 2 дня
Один из проектов требовал синхронизацию заказов с AmoCRM. Проблема была в том, что стандартные плагины не поддерживали кастомные поля сделок. Мы разработали плагин с компонентом, который через REST API отправлял данные при создании заказа. Вся работа заняла 2 дня, тесты покрыли 95% кода.Сравнение: кастомный плагин vs доработка существующего
| Критерий | Кастомный плагин | Доработка существующего |
|---|---|---|
| Совместимость с обновлениями | Полная — код не пересекается | При каждом обновлении нужно переносить правки |
| Время разработки | 3–14 дней под ключ | 2–3 дня на патч, но риск багов |
| Гибкость | Любая логика, связи, события | Ограничен структурой плагина |
| Документация | README, код-комментарии, обучение | Часто отсутствует |
| Гарантия | 30 дней на исправления | Зависит от автора |
Пример кода
Ниже приведён минимальный каркас плагина. Полный набор файлов показан на примере модели отзывов.
Plugin.php — регистрация компонентов и навигации
// Plugin.php namespace MyCompany\MySite; use Backend; use System\Classes\PluginBase; class Plugin extends PluginBase { public function pluginDetails(): array { return [ 'name' => 'My Site', 'description' => 'Site-specific functionality', 'author' => 'My Company', 'icon' => 'icon-leaf', ]; } public function registerComponents(): array { return [ \MyCompany\MySite\Components\ReviewList::class => 'reviewList', \MyCompany\MySite\Components\ReviewForm::class => 'reviewForm', ]; } public function registerNavigation(): array { return [ 'mysite' => [ 'label' => 'My Site', 'url' => Backend::url('mycompany/mysite/reviews'), 'icon' => 'icon-star', 'permissions' => ['mycompany.mysite.*'], 'order' => 500, 'sideMenu' => [ 'reviews' => [ 'label' => 'Reviews', 'icon' => 'icon-comments', 'url' => Backend::url('mycompany/mysite/reviews'), 'permissions' => ['mycompany.mysite.reviews'], ], ], ], ]; } public function registerSettings(): array { return [ 'settings' => [ 'label' => 'My Site Settings', 'description' => 'Configure My Site plugin', 'icon' => 'icon-cog', 'class' => \MyCompany\MySite\Models\Settings::class, 'order' => 500, ], ]; } public function boot(): void { \Event::listen('cms.page.beforeDisplay', function ($controller, $url, $page) { // Логика перед рендером страницы }); } } Модель с Eloquent
// models/Review.php namespace MyCompany\MySite\Models; use Model; class Review extends Model { use \October\Rain\Database\Traits\Validation; use \October\Rain\Database\Traits\SoftDelete; public $table = 'mycompany_mysite_reviews'; public $rules = [ 'author_name' => 'required|string|max:255', 'email' => 'required|email', 'rating' => 'required|integer|between:1,5', 'body' => 'required|string|min:10', ]; protected $fillable = ['author_name', 'email', 'rating', 'body', 'is_approved']; protected $casts = [ 'is_approved' => 'boolean', 'rating' => 'integer', ]; public $attachOne = [ 'avatar' => \System\Models\File::class, ]; public $belongsTo = [ 'product' => [\MyCompany\MySite\Models\Product::class], ]; public function scopeApproved($query) { return $query->where('is_approved', true); } public function scopeByProduct($query, int $productId) { return $query->where('product_id', $productId); } } Компонент
// components/ReviewList.php namespace MyCompany\MySite\Components; use Cms\Classes\ComponentBase; use MyCompany\MySite\Models\Review; class ReviewList extends ComponentBase { public function componentDetails(): array { return [ 'name' => 'Review List', 'description' => 'Displays product reviews', ]; } public function defineProperties(): array { return [ 'productId' => ['title' => 'Product ID', 'type' => 'string'], 'limit' => ['title' => 'Limit', 'type' => 'string', 'default' => '10'], ]; } public function onRun(): void { $this->page['reviews'] = Review::approved() ->byProduct((int) $this->property('productId')) ->with('avatar') ->orderBy('created_at', 'desc') ->limit((int) $this->property('limit')) ->get(); $this->page['avgRating'] = Review::approved() ->byProduct((int) $this->property('productId')) ->avg('rating'); } public function onSubmitReview(): array { $data = post(); $review = new Review($data); $review->product_id = $this->property('productId'); if (!$review->save()) { throw new \ValidationException($review); } return ['success' => true]; } } Миграция
// updates/1_0_1_create_reviews_table.php use October\Rain\Database\Schema\Blueprint; use October\Rain\Database\Updates\Migration; class CreateReviewsTable extends Migration { public function up(): void { Schema::create('mycompany_mysite_reviews', function (Blueprint $table) { $table->increments('id'); $table->integer('product_id')->unsigned()->index(); $table->string('author_name'); $table->string('email'); $table->tinyInteger('rating'); $table->text('body'); $table->boolean('is_approved')->default(false); $table->timestamps(); $table->softDeletes(); }); } public function down(): void { Schema::dropIfExists('mycompany_mysite_reviews'); } } Этапы и сроки разработки
| Этап | Длительность | Результат |
|---|---|---|
| Анализ требований | 1–2 дня | ТЗ, ER-диаграмма |
| Проектирование | 1–2 дня | Схема компонентов, навигация |
| Реализация | 3–7 дней | Код, тесты, документация |
| Интеграция | 1–2 дня | Работающий плагин на вашем сайте |
| Деплой и передача | 1 день | composer.json, README, конфиги |
Как заказать разработку?
Опишите задачу — мы подготовим оценку за 1 день. Свяжитесь с нами, и мы поможем разобраться, какой подход лучше подходит для вашего проекта.
Более подробно о разработке плагинов можно узнать в официальной документации October CMS.
Опыт 5+ лет и 50+ реализованных плагинов — это не просто цифры. Каждый проект мы начинаем с архитектурного ревью: избегаем типичных ошибок (отсутствие индексов в БД, неоптимизированные запросы, дублирование кода). Все плагины проходят код-ревью внутри команды. Даём гарантию на код 30 дней — если что-то пошло не так, бесплатно чиним.







