Разработка кастомного плагина Kirby

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

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

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка кастомного плагина Kirby
Средняя
~3-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

Разработка кастомного плагина Kirby

Плагин в Kirby — это PHP-пакет, который регистрирует расширения через Kirby::plugin(). Одним вызовом можно добавить новые типы полей, блоков, маршруты, хуки, методы страниц и многое другое. Плагины распространяются через Composer или как папки в site/plugins/.

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

site/plugins/my-plugin/
├── composer.json
├── index.php          # точка входа
├── lib/               # PHP-классы
├── templates/         # шаблоны блоков
└── assets/
    ├── css/
    └── js/
<?php
// site/plugins/my-plugin/index.php

use Kirby\Cms\App;
use Kirby\Cms\Page;

App::plugin('vendor/my-plugin', [
    'options' => [
        'cache' => true,
        'apiKey' => null,
    ],
    'fields'    => require __DIR__ . '/lib/fields.php',
    'methods'   => require __DIR__ . '/lib/methods.php',
    'routes'    => require __DIR__ . '/lib/routes.php',
    'hooks'     => require __DIR__ . '/lib/hooks.php',
    'blueprints' => require __DIR__ . '/lib/blueprints.php',
    'translations' => [
        'ru' => require __DIR__ . '/lib/translations/ru.php',
        'en' => require __DIR__ . '/lib/translations/en.php',
    ],
]);

Кастомные методы страниц

<?php
// lib/methods.php
return [
    'pages' => [
        'publishedAndFeatured' => function (): Pages {
            return $this->listed()->filter(
                fn($p) => $p->featured()->isTrue()
            );
        },
    ],
    'page' => [
        'readingTime' => function (): int {
            $words = str_word_count(
                strip_tags($this->text()->kirbytext())
            );
            return max(1, (int) ceil($words / 200));
        },

        'schemaOrg' => function (): string {
            $schema = [
                '@context' => 'https://schema.org',
                '@type'    => 'Article',
                'headline' => $this->title()->value(),
                'datePublished' => $this->date()->toDate('c'),
                'url'      => $this->url(),
            ];
            return '<script type="application/ld+json">'
                . json_encode($schema, JSON_UNESCAPED_UNICODE)
                . '</script>';
        },
    ],
    'file' => [
        'isWebOptimized' => function (): bool {
            return in_array($this->extension(), ['webp', 'avif', 'svg']);
        },
    ],
];

В шаблонах:

echo $page->readingTime(); // 5
echo $page->schemaOrg();   // <script type="application/ld+json">...</script>
$page->siblings()->publishedAndFeatured();

Кастомные маршруты

<?php
// lib/routes.php
return [
    [
        'pattern' => 'api/v1/sitemap',
        'method'  => 'GET',
        'action'  => function () {
            $pages = kirby()->site()->index()->listed()->filter(
                fn($p) => $p->intendedTemplate()->name() !== 'error'
            );

            $urls = $pages->map(fn($p) => [
                'url'      => $p->url(),
                'modified' => $p->modified('Y-m-d'),
                'priority' => $p->isHomePage() ? '1.0' : '0.8',
            ])->values();

            return \Kirby\Http\Response::json(['urls' => $urls]);
        },
    ],
    [
        'pattern' => 'api/v1/search',
        'method'  => 'GET',
        'action'  => function () {
            $query = get('q', '');
            if (strlen($query) < 2) {
                return \Kirby\Http\Response::json(['results' => []]);
            }

            $results = kirby()->site()->search($query, 'title|text')
                ->listed()
                ->limit(10)
                ->map(fn($p) => [
                    'title'   => $p->title()->value(),
                    'url'     => $p->url(),
                    'excerpt' => $p->text()->excerpt(120),
                ]);

            return \Kirby\Http\Response::json(['results' => $results->values()]);
        },
    ],
];

Хуки

<?php
// lib/hooks.php
return [
    'page.create:after' => function (Page $page) {
        if ($page->intendedTemplate()->name() !== 'article') {
            return;
        }

        // ping поисковики
        $url = urlencode(kirby()->site()->url() . '/sitemap.xml');
        @file_get_contents("https://www.google.com/ping?sitemap={$url}");
    },

    'page.update:after' => function (Page $newPage, Page $oldPage) {
        // сбросить кэш конкретной страницы
        if (kirby()->cache('pages')->exists($newPage->id())) {
            kirby()->cache('pages')->remove($newPage->id());
        }
    },

    'file.create:after' => function (\Kirby\Cms\File $file) {
        if (!$file->isImage()) {
            return;
        }

        // генерировать WebP-версии превентивно
        $file->thumb(['width' => 800, 'format' => 'webp']);
        $file->thumb(['width' => 400, 'format' => 'webp']);
    },

    'kirby.render:before' => function (string $template, array $data) {
        // можно добавить глобальные данные
    },
];

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

Поля панели пишутся на Vue.js и регистрируются вместе с PHP-серверной частью:

<?php
// lib/fields.php
return [
    'colorpicker' => [
        'props' => [
            'value' => function ($value = '#000000') {
                return $value;
            },
            'presets' => function (array $presets = []) {
                return $presets;
            },
        ],
        'save' => function ($value): string {
            // валидация перед сохранением
            if (!preg_match('/^#[0-9A-Fa-f]{6}$/', $value)) {
                throw new \Exception('Invalid color format');
            }
            return $value;
        },
    ],
];
// assets/js/fields.js
panel.plugin('vendor/my-plugin', {
  fields: {
    colorpicker: {
      template: `
        <k-field v-bind="$props">
          <input type="color" :value="value" @input="$emit('input', $event.target.value)">
          <div class="presets">
            <button
              v-for="color in presets"
              :key="color"
              :style="{ background: color }"
              @click="$emit('input', color)"
            />
          </div>
        </k-field>
      `,
      props: {
        value:   String,
        presets: Array,
      },
    },
  },
});

Регистрация ассета:

App::plugin('vendor/my-plugin', [
    'fields' => require __DIR__ . '/lib/fields.php',
    'assets' => [
        'js/fields.js' => __DIR__ . '/assets/js/fields.js',
    ],
]);

Опции плагина

// получение опции в коде плагина
$apiKey = option('vendor/my-plugin.apiKey');
$useCache = option('vendor/my-plugin.cache', true);

Пользователь переопределяет в site/config/config.php:

return [
    'vendor/my-plugin' => [
        'apiKey' => 'sk-...',
        'cache'  => false,
    ],
];

Сроки

Плагин с методами и хуками без UI-части: 1–3 дня. Плагин с кастомным полем панели (Vue + PHP): 3–6 дней. Сложный плагин с кастомным разделом панели, внешним API и кэшированием: 1–2 недели.