Розробка кастомного модуля 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;
}
}
При встановленні модуль автоматично створює сторінку в адміністративному дереві за вказаною шляхом.
Сроки розробки модулів
| Тип модуля | Складність | Срок |
|---|---|---|
| Хук + утиліта | низька | 2–6 ч |
| Інтеграція з API | середня | 1–3 дні |
| Кастомний Fieldtype + Inputfield | висока | 3–7 днів |
| Process (повнофункціональна admin-сторінка) | середня–висока | 2–5 днів |







