Розробка кастомного шаблону ProcessWire
Шаблон в ProcessWire — це PHP-файл у /site/templates/, ім'я якого збігається з іменем шаблону в базі. Файл отримує об'єкт $page з даними поточної сторінки та повний доступ до $pages, $config, $user та решти API. Жодного рушія шаблонів за замовчуванням — чистий PHP, хоча можна підключити Twig через модуль TemplateEngineTwig.
Архітектура шаблонів: варіанти розділення
Варіант 1: _init.php + _main.php
Найпоширеніший підхід. ProcessWire автоматично включає _init.php перед кожним шаблоном та _main.php після, якщо включена опція «Prepend/Append template file» у налаштуваннях шаблону.
// _init.php — глобальні змінні, хелпери
$baseUrl = $config->urls->root;
$homePage = $pages->get("/");
$mainNav = $pages->find("parent=/,template!=error404,sort=sort");
function formatDate(int $ts): string {
return date("d.m.Y", $ts);
}
// _main.php — layout-обгортка
?><!DOCTYPE html>
<html lang="<?= $user->language->name ?>">
<head>
<meta charset="utf-8">
<title><?= $page->title ?> | <?= $homePage->title ?></title>
<link rel="stylesheet" href="<?= $config->urls->templates ?>css/main.css">
</head>
<body>
<?php include("partials/nav.php"); ?>
<main><?= $content ?></main>
<?php include("partials/footer.php"); ?>
</body>
</html>
У шаблоні сторінки $content накопичується через буферизацію виводу:
// services.php — ProcessWire автоматично буферизує вивід
$items = $pages->find("template=service, parent={$page}, sort=sort");
foreach ($items as $item) {
echo "<article>";
echo "<h2><a href='{$item->url}'>{$item->title}</a></h2>";
echo "<p>{$item->summary}</p>";
echo "</article>";
}
Варіант 2: контролер + вид
Для складних шаблонів логіку виносять у окремий файл-контролер:
templates/
controllers/
services.php # тільки логіка, без echo
views/
services.view.php # тільки розмітка
services.php # точка входу, зв'язує controller+view
// templates/services.php
require __DIR__ . '/controllers/services.php';
extract($viewData); // змінні з контролера
require __DIR__ . '/views/services.view.php';
// templates/controllers/services.php
$viewData = [
'items' => $pages->find("template=service, sort=sort, limit=12"),
'categories' => $pages->find("template=service-category, sort=sort"),
'pagination' => $modules->get("MarkupPagerNav"),
];
Робота з зображеннями
ProcessWire обробляє зображення на льоту через метод size():
// Головне зображення сторінки
$img = $page->images->first();
if ($img) {
// Ресайз з кадруванням 800×600
$thumb = $img->size(800, 600, ['cropping' => 'center', 'quality' => 85]);
echo "<img src='{$thumb->url}' alt='{$img->description}' loading='lazy'>";
// WebP через опцію suffix
$webp = $img->size(800, 600, ['suffix' => 'webp', 'webpAdd' => true]);
}
Варіантні розміри кешуються у /site/assets/files/{id}/. Повторний запрос повертає вже готовий файл.
Повторно використовувані часткові шаблони
// partials/card.php — отримує $item з include
?>
<div class="card">
<?php if ($item->image): ?>
<img src="<?= $item->image->size(400,300)->url ?>" alt="<?= $item->title ?>">
<?php endif; ?>
<h3><?= $item->title ?></h3>
<p><?= $item->summary ?></p>
<a href="<?= $item->url ?>">Детальніше</a>
</div>
// Використання у services.php
foreach ($items as $item) {
include("./partials/card.php");
}
Мета-дані та SEO
SEO-поля звичайно додають на рівні шаблону через окремий fieldset:
// У _main.php
$metaTitle = $page->meta_title ?: $page->title;
$metaDescription = $page->meta_description ?: $page->summary;
$ogImage = $page->og_image ? $page->og_image->size(1200, 630)->httpUrl : '';
?>
<title><?= htmlspecialchars($metaTitle) ?></title>
<meta name="description" content="<?= htmlspecialchars($metaDescription) ?>">
<meta property="og:image" content="<?= $ogImage ?>">
Кастомні 404 та редиректи
// templates/basic-page.php
// Редирект застарілого URL
if ($page->redirect_url) {
$session->redirect($page->redirect_url, 301);
}
// Примусовий 404
if (!$user->isLoggedin() && $page->requires_login) {
throw new Wire404Exception();
}
Продуктивність: ленива загрузка зв'язків
За замовчуванням FieldtypePage завантажує пов'язані об'єкти при звертанні. При великих списках використовують $pages->findMany() — потокова обробка без завантаження всього в пам'ять:
// findMany() для великих наборів (1000+ сторінок)
foreach ($pages->findMany("template=product, sort=title") as $product) {
echo $product->title . "\n";
// ProcessWire автоматично звільняє пам'ять
}
Сроки розробки шаблонів
| Завдання | Оцінка |
|---|---|
| Базовий шаблон (layout + nav + footer) | 4–8 ч |
| Шаблон списку з пагінацією та фільтром | 8–16 ч |
| Детальна сторінка зі зв'язками | 4–10 ч |
| Система шаблонів для 10+ типів контенту | 3–6 днів |
| Інтеграція Twig + компонентний підхід | 2–4 дні |







