Разработка кастомного плагина 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 дней — если что-то пошло не так, бесплатно чиним.







