Разработка кастомного модуля ProcessWire

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

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

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

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

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

Разработка кастомного модуля ProcessWire

Модульная система ProcessWire — один из главных аргументов в пользу этой CMS. Любой модуль — это PHP-класс, наследующий Wire или один из его потомков. Модуль может быть хуком на системные события, кастомным типом поля, процессом (страницей в админке) или просто набором функций, доступных через $modules->get("MyModule").

Типы модулей

Тип Базовый класс Назначение
Обычный WireData / Wire Хелперы, утилиты, интеграции API
Fieldtype Fieldtype Новый тип поля
Inputfield Inputfield Виджет редактирования поля
Process Process Страница в админке
Textformatter Textformatter Обработка текста при выводе
Module (hookable) WireData + хуки Расширение поведения

Минимальная структура модуля

/site/modules/
  MyModule/
    MyModule.module.php   # основной файл (или MyModule.php)
    MyModule.info.php     # метаданные (опционально, можно инлайн)
<?php
// MyModule.module.php

class MyModule extends WireData implements Module {

    public static function getModuleInfo(): array {
        return [
            'title'    => 'My Module',
            'version'  => '1.0.0',
            'summary'  => 'Описание модуля',
            'author'   => 'Dev Name',
            'requires' => ['ProcessWire>=3.0.0'],
            'autoload' => true,   // загружать при каждом запросе
            'singular' => true,   // один экземпляр на запрос
        ];
    }

    public function init(): void {
        // Хуки и инициализация
        $this->addHookAfter('Pages::saved', $this, 'onPageSaved');
    }

    protected function onPageSaved(HookEvent $event): void {
        $page = $event->arguments(0);
        if ($page->template->name !== 'product') return;
        // логика после сохранения страницы типа product
    }
}

Хуки: до и после

ProcessWire позволяет перехватывать практически любой метод API:

// Хук "до" — можно изменить аргументы или отменить выполнение
$this->addHookBefore('Pages::delete', function(HookEvent $e) {
    $page = $e->arguments(0);
    if ($page->template->name === 'protected') {
        $e->replace = true; // отменить выполнение оригинального метода
        throw new WireException("Удаление запрещено для шаблона protected");
    }
});

// Хук "after" — можно изменить возвращаемое значение
$this->addHookAfter('Page::render', function(HookEvent $e) {
    $html = $e->return;
    // добавить метрику в конец страницы
    $e->return = str_replace('</body>', '<script>analytics();</script></body>', $html);
});

Конфигурация модуля

Модули с настройками реализуют интерфейс ConfigurableModule:

class MyModule extends WireData implements Module, ConfigurableModule {

    protected static array $defaults = [
        'api_key'    => '',
        'cache_ttl'  => 3600,
        'debug_mode' => 0,
    ];

    public static function getDefaultConfig(): array {
        return self::$defaults;
    }

    public static function getModuleConfigInputfields(array $data): InputfieldWrapper {
        $modules = wire('modules');
        $fields  = new InputfieldWrapper();

        $data = array_merge(self::$defaults, $data);

        $f = $modules->get('InputfieldText');
        $f->attr('name', 'api_key');
        $f->label = 'API Key';
        $f->value = $data['api_key'];
        $fields->add($f);

        $f = $modules->get('InputfieldInteger');
        $f->attr('name', 'cache_ttl');
        $f->label = 'Время кэша (секунды)';
        $f->value = $data['cache_ttl'];
        $fields->add($f);

        return $fields;
    }
}

Значения читаются внутри модуля через $this->api_key, $this->cache_ttl.

Пример: модуль интеграции с внешним API

class ExternalApiSync extends WireData implements Module {

    public static function getModuleInfo(): array {
        return [
            'title'    => 'External API Sync',
            'version'  => '1.2.0',
            'autoload' => false,
            'singular' => true,
        ];
    }

    public function syncProducts(): int {
        $apiUrl  = "https://api.supplier.com/v2/products";
        $headers = ["Authorization: Bearer {$this->api_key}"];

        $http     = $this->wire('modules')->get('WireHttp');
        $response = $http->getJSON($apiUrl, true, [], $headers);

        if (!$response) {
            $this->wire('log')->error("ExternalApiSync: не удалось получить данные");
            return 0;
        }

        $count = 0;
        foreach ($response['items'] as $item) {
            $p = $this->wire('pages')->get("template=product, external_id={$item['id']}");
            if (!$p->id) {
                $p = new Page();
                $p->template = 'product';
                $p->parent   = $this->wire('pages')->get('/catalog/');
            }
            $p->title       = $item['name'];
            $p->external_id = $item['id'];
            $p->price       = $item['price'];
            $p->save();
            $count++;
        }
        return $count;
    }
}

Вызов из шаблона или CLI:

$sync  = $modules->get('ExternalApiSync');
$count = $sync->syncProducts();
echo "Синхронизировано: $count товаров";

Process-модуль: страница в админке

class ProcessMyAdmin extends Process {

    public static function getModuleInfo(): array {
        return [
            'title'    => 'My Admin Page',
            'version'  => '1.0.0',
            'requires' => ['ProcessWire>=3.0.0'],
            'page'     => [
                'name'   => 'my-admin',
                'title'  => 'My Admin',
                'parent' => 'admin',
            ],
        ];
    }

    public function execute(): string {
        $out  = "<h2>Панель управления</h2>";
        $out .= "<p>Количество товаров: " . $this->pages->count("template=product") . "</p>";
        return $out;
    }
}

При установке модуль автоматически создаёт страницу в административном дереве по указанному пути.

Тестирование и отладка

// В _init.php при debug-режиме
if ($config->debug) {
    $log = $modules->get('MyModule');
    // ProcessWire логирует в /site/assets/logs/
    wire('log')->save('my-module', "Модуль загружен: " . date('H:i:s'));
}

bd() — встроенная функция дебага (аналог var_dump с форматированием в Tracy Debugger):

bd($page->getArray(), 'page fields'); // выводит в Tracy bar

Сроки разработки модулей

Тип модуля Сложность Срок
Хук + утилита низкая 2–6 ч
Интеграция с API средняя 1–3 дня
Кастомный Fieldtype + Inputfield высокая 3–7 дней
Process (полноценная admin-страница) средняя–высокая 2–5 дней