Розробка кастомного плагіна Craft CMS

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка кастомного плагіна Craft CMS
Складна
~5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка кастомного плагіну Craft CMS

Плагіни для Craft CMS — це пакети Composer, що розширюють функціональність CMS через офіційний API: кастомні типи елементів, fieldtype-поля, віджети дашборду, утиліти, нові секції в CP. На відміну від модулів, плагіни можна публікувати в Plugin Store або переиспользовувати між проектами.

Структура плагіну

craft-myplugin/
├── composer.json
├── CHANGELOG.md
├── src/
│   ├── Plugin.php              # головний клас
│   ├── models/
│   │   └── Settings.php        # модель параметрів
│   ├── services/
│   │   └── MyService.php
│   ├── fields/
│   │   └── ColorSwatchField.php
│   ├── elements/
│   │   └── ProductElement.php
│   ├── records/
│   │   └── ProductRecord.php   # ActiveRecord Yii2
│   ├── migrations/
│   │   └── Install.php
│   ├── controllers/
│   │   └── ProductsController.php
│   ├── templates/
│   │   ├── _cp/
│   │   │   └── settings.twig
│   │   └── _fields/
│   │       └── color-swatch-input.twig
│   ├── web/
│   │   └── assets/
│   │       └── CpAsset.php
│   └── translations/
│       └── en/
│           └── myplugin.php
└── icon.svg

composer.json

{
  "name": "myvendor/craft-myplugin",
  "description": "My custom Craft CMS plugin",
  "type": "craft-plugin",
  "minimum-stability": "dev",
  "require": {
    "craftcms/cms": "^4.0.0"
  },
  "autoload": {
    "psr-4": { "myvendor\\myplugin\\": "src/" }
  },
  "extra": {
    "handle": "myplugin",
    "name": "My Plugin",
    "version": "1.0.0",
    "schemaVersion": "1.0.0",
    "class": "myvendor\\myplugin\\Plugin",
    "developer": "My Company",
    "developerUrl": "https://mycompany.com",
    "documentationUrl": "https://mycompany.com/plugins/myplugin",
    "changelogUrl": "https://raw.githubusercontent.com/myvendor/craft-myplugin/main/CHANGELOG.md",
    "hasCpSettings": true,
    "hasCpSection": true
  }
}

Головний клас плагіну

// src/Plugin.php
namespace myvendor\myplugin;

use Craft;
use craft\base\Plugin as BasePlugin;
use craft\events\RegisterComponentTypesEvent;
use craft\services\Fields;
use myvendor\myplugin\fields\ColorSwatchField;
use myvendor\myplugin\services\MyService;
use myvendor\myplugin\models\Settings;
use yii\base\Event;

class Plugin extends BasePlugin
{
    public string $schemaVersion = '1.0.0';
    public bool $hasCpSettings = true;
    public bool $hasCpSection = true;

    public static Plugin $instance;

    public function init(): void
    {
        parent::init();
        self::$instance = $this;

        $this->setComponents([
            'myService' => MyService::class,
        ]);

        // Реєстрація кастомного типу поля
        Event::on(
            Fields::class,
            Fields::EVENT_REGISTER_FIELD_TYPES,
            function (RegisterComponentTypesEvent $event) {
                $event->types[] = ColorSwatchField::class;
            }
        );

        Craft::info("Plugin {$this->name} loaded", __METHOD__);
    }

    protected function createSettingsModel(): ?Model
    {
        return new Settings();
    }

    protected function settingsHtml(): ?string
    {
        return Craft::$app->view->renderTemplate('myplugin/_cp/settings', [
            'settings' => $this->getSettings(),
        ]);
    }

    public function getCpNavItem(): ?array
    {
        return [
            ...parent::getCpNavItem(),
            'label' => 'My Plugin',
            'url'   => 'myplugin',
            'icon'  => '@myvendor/myplugin/icon.svg',
            'subnav' => [
                'dashboard' => ['label' => 'Dashboard', 'url' => 'myplugin'],
                'settings'  => ['label' => 'Settings', 'url'  => 'myplugin/settings'],
            ],
        ];
    }
}

Кастомний тип поля

// src/fields/ColorSwatchField.php
namespace myvendor\myplugin\fields;

use craft\base\Field;
use craft\base\ElementInterface;

class ColorSwatchField extends Field
{
    public static function displayName(): string
    {
        return \Craft::t('myplugin', 'Color Swatch');
    }

    public static function phpType(): string
    {
        return 'string';
    }

    public function getInputHtml(mixed $value, ?ElementInterface $element): string
    {
        return \Craft::$app->view->renderTemplate(
            'myplugin/_fields/color-swatch-input',
            [
                'id'    => $this->getInputId(),
                'name'  => $this->handle,
                'value' => $value,
                'swatches' => $this->swatches,
            ]
        );
    }

    public function normalizeValue(mixed $value, ?ElementInterface $element): mixed
    {
        // Нормалізація: додаємо # якщо немає
        return ltrim($value ?? '', '#') ? '#' . ltrim($value, '#') : null;
    }

    protected function defineRules(): array
    {
        return array_merge(parent::defineRules(), [
            [['value'], 'match', 'pattern' => '/^#[0-9A-Fa-f]{6}$/'],
        ]);
    }
}

Кастомний тип елемента

Кастомні елементи (як Entry, User, Asset) наслідують від craft\base\Element:

// src/elements/ProductElement.php — ключові методи
class ProductElement extends Element
{
    public static function displayName(): string { return 'Product'; }
    public static function pluralDisplayName(): string { return 'Products'; }

    // Умова пошуку
    public static function find(): ElementQueryInterface
    {
        return new ProductQuery(static::class);
    }

    // Поля для індексації
    protected static function defineSearchableAttributes(): array
    {
        return ['sku', 'title', 'description'];
    }

    // Збереження додаткових даних (у своїй таблиці)
    public function afterSave(bool $isNew): void
    {
        if ($isNew) {
            \Craft::$app->db->createCommand()
                ->insert('{{%myplugin_products}}', [
                    'id' => $this->id,
                    'sku' => $this->sku,
                    'price' => $this->price,
                ])
                ->execute();
        } else {
            \Craft::$app->db->createCommand()
                ->update('{{%myplugin_products}}', [
                    'sku' => $this->sku,
                    'price' => $this->price,
                ], ['id' => $this->id])
                ->execute();
        }

        parent::afterSave($isNew);
    }
}

Міграції

// src/migrations/Install.php
namespace myvendor\myplugin\migrations;

use craft\db\Migration;

class Install extends Migration
{
    public function safeUp(): bool
    {
        if (!$this->db->tableExists('{{%myplugin_products}}')) {
            $this->createTable('{{%myplugin_products}}', [
                'id'          => $this->primaryKey(),
                'sku'         => $this->string()->notNull(),
                'price'       => $this->decimal(10, 2)->notNull()->defaultValue(0),
                'stockCount'  => $this->integer()->notNull()->defaultValue(0),
                'dateCreated' => $this->dateTime()->notNull(),
                'dateUpdated' => $this->dateTime()->notNull(),
                'uid'         => $this->uid(),
            ]);

            $this->addForeignKey(
                null, '{{%myplugin_products}}', 'id',
                '{{%elements}}', 'id', 'CASCADE'
            );
        }
        return true;
    }

    public function safeDown(): bool
    {
        $this->dropTableIfExists('{{%myplugin_products}}');
        return true;
    }
}

Тестування

# Codeception для Craft
composer require craftcms/craft-pest --dev
./vendor/bin/pest

Терміни розробки

Тип плагіну Час
Просте поле (FieldType) 2–3 дні
Сервіс + CP секція 3–5 днів
Кастомний тип елемента 5–10 днів
Повнофункціональний плагін (елемент + CP + API) 2–4 тижні
Публікація в Plugin Store +1–2 дні