Створення власних фільтрів
Фільтри — це потужні інструменти для форматування та
модифікації даних безпосередньо в шаблонах Latte. Вони пропонують чистий
синтаксис за допомогою символу вертикальної риски (|
) для
перетворення змінних або результатів виразів у бажаний вихідний
формат.
Що таке фільтри?
Фільтри в Latte — це, по суті, PHP-функції, розроблені спеціально для
перетворення вхідного значення на вихідне значення. Вони
застосовуються за допомогою запису з вертикальною рискою (|
)
всередині виразів шаблону ({...}
).
Зручність: Фільтри дозволяють інкапсулювати поширені завдання форматування (наприклад, форматування дат, зміна регістру, скорочення) або маніпуляції з даними в багаторазові одиниці. Замість повторення складного PHP-коду у ваших шаблонах, ви можете просто застосувати фільтр:
{* Замість складного PHP для скорочення: *}
{$article->text|truncate:100}
{* Замість коду для форматування дати: *}
{$event->startTime|date:'Y-m-d H:i'}
{* Застосування кількох перетворень: *}
{$product->name|lower|capitalize}
Читабельність: Використання фільтрів робить шаблони зрозумілішими та більш орієнтованими на презентацію, оскільки логіка перетворення переміщується до визначення фільтра.
Контекстна чутливість: Ключовою перевагою фільтрів у Latte є їхня здатність бути контекстно чутливими. Це означає, що фільтр може розпізнавати тип вмісту, з яким він працює (HTML, JavaScript, простий текст тощо), і застосовувати відповідну логіку або екранування, що є критично важливим для безпеки та правильності, особливо при генерації HTML.
Інтеграція з логікою застосунку: Так само, як і власні функції, PHP-об'єкт, що викликається, за фільтром може бути замиканням (closure), статичним методом або методом екземпляра. Це дозволяє фільтрам отримувати доступ до сервісів застосунку або даних, якщо це необхідно, хоча їхньою основною метою залишається перетворення вхідного значення.
Latte за замовчуванням надає багатий набір стандартних фільтрів. Власні фільтри дозволяють розширити цей набір форматуваннями та перетвореннями, специфічними для вашого проекту.
Якщо вам потрібно виконати логіку, засновану на кількох входах, або у вас немає основного значення для перетворення, ймовірно, краще використовувати власну функцію. Якщо вам потрібно згенерувати складну розмітку або керувати потоком шаблону, розгляньте власний тег.
Створення та реєстрація фільтрів
Існує кілька способів визначення та реєстрації власних фільтрів у Latte.
Пряма реєстрація за допомогою
addFilter()
Найпростіший спосіб додати фільтр — це використання методу
addFilter()
безпосередньо на об'єкті Latte\Engine
. Ви вказуєте назву
фільтра (як він буде використовуватися в шаблоні) та відповідний
PHP-об'єкт, що викликається.
$latte = new Latte\Engine;
// Простий фільтр без аргументів
$latte->addFilter('initial', fn(string $s): string => mb_substr($s, 0, 1) . '.');
// Фільтр з необов'язковим аргументом
$latte->addFilter('shortify', function (string $s, int $len = 10): string {
return mb_substr($s, 0, $len);
});
// Фільтр, що обробляє масиви
$latte->addFilter('sum', fn(array $numbers): int|float => array_sum($numbers));
Використання в шаблоні:
{$name|initial} {* Виведе 'J.' якщо $name 'John' *}
{$description|shortify} {* Використає стандартну довжину 10 *}
{$description|shortify:50} {* Використає довжину 50 *}
{$prices|sum} {* Виведе суму елементів у масиві $prices *}
Передача аргументів:
Значення ліворуч від вертикальної риски (|
) завжди передається
як перший аргумент функції фільтра. Будь-які параметри, вказані
після двокрапки (:
) в шаблоні, передаються як наступні
аргументи.
{$text|shortify:30}
// Викликає PHP-функцію shortify($text, 30)
Реєстрація за допомогою розширення
Для кращої організації, особливо при створенні багаторазових наборів фільтрів або їх поширенні як пакетів, рекомендованим способом є реєстрація їх у межах розширення Latte:
namespace App\Latte;
use Latte\Extension;
class MyLatteExtension extends Extension
{
public function getFilters(): array
{
return [
'initial' => $this->initial(...),
'shortify' => $this->shortify(...),
];
}
public function initial(string $s): string
{
return mb_substr($s, 0, 1) . '.';
}
public function shortify(string $s, int $len = 10): string
{
return mb_substr($s, 0, $len);
}
}
// Реєстрація
$latte = new Latte\Engine;
$latte->addExtension(new App\Latte\MyLatteExtension);
Цей підхід збереже логіку вашого фільтра інкапсульованою, а реєстрацію — простою.
Використання завантажувача фільтрів
Latte дозволяє реєструвати завантажувач фільтрів за допомогою
addFilterLoader()
. Це єдиний об'єкт, що викликається, який Latte запитає про
будь-яку невідому назву фільтра під час компіляції. Завантажувач
повертає PHP-об'єкт, що викликається, фільтра або null
.
$latte = new Latte\Engine;
// Завантажувач може динамічно створювати/отримувати викликані фільтри
$latte->addFilterLoader(function (string $name): ?callable {
if ($name === 'myLazyFilter') {
// Уявіть тут складну ініціалізацію...
$service = get_some_expensive_service();
return fn($value) => $service->process($value);
}
return null;
});
Цей метод був переважно призначений для лінивого завантаження фільтрів з дуже складною ініціалізацією. Однак сучасні практики впровадження залежностей (dependency injection) зазвичай ефективніше справляються з лінивими сервісами.
Завантажувачі фільтрів додають складності і загалом не
рекомендуються на користь прямої реєстрації за допомогою
addFilter()
або в межах розширення за допомогою getFilters()
.
Використовуйте завантажувачі лише якщо у вас є серйозна, специфічна
причина, пов'язана з проблемами продуктивності при ініціалізації
фільтрів, які неможливо вирішити інакше.
Фільтри, що використовують клас з атрибутами
Ще один елегантний спосіб визначення фільтрів — це використання
методів у вашому класі
параметрів шаблону. Достатньо додати атрибут
#[Latte\Attributes\TemplateFilter]
до методу.
use Latte\Attributes\TemplateFilter;
class TemplateParameters
{
public function __construct(
public string $description,
// інші параметри...
) {}
#[TemplateFilter]
public function shortify(string $s, int $len = 10): string
{
return mb_substr($s, 0, $len);
}
}
// Передача об'єкта до шаблону
$params = new TemplateParameters(description: '...');
$latte->render('template.latte', $params);
Latte автоматично розпізнає та зареєструє методи, позначені цим
атрибутом, коли об'єкт TemplateParameters
передається до шаблону. Назва
фільтра в шаблоні буде такою ж, як назва методу (shortify
у цьому
випадку).
{* Використання фільтра, визначеного в класі параметрів *}
{$description|shortify:50}
Контекстні фільтри
Іноді фільтр потребує більше інформації, ніж просто вхідне значення. Йому може знадобитися знати тип вмісту рядка, з яким він працює (наприклад, HTML, JavaScript, простий текст), або навіть змінити його. Це ситуація для контекстних фільтрів.
Контекстний фільтр визначається так само, як звичайний фільтр, але
його перший параметр повинен бути типізований як
Latte\Runtime\FilterInfo
. Latte автоматично розпізнає цей підпис і при
виклику фільтра передає об'єкт FilterInfo
. Наступні параметри
отримують аргументи фільтра як зазвичай.
use Latte\Runtime\FilterInfo;
use Latte\ContentType;
$latte->addFilter('money', function (FilterInfo $info, float $amount): string {
// 1. Перевірте вхідний тип вмісту (необов'язково, але рекомендовано)
// Дозвольте null (змінний вхід) або простий текст. Відхиліть, якщо застосовано до HTML тощо.
if (!in_array($info->contentType, [null, ContentType::Text], true)) {
$actualType = $info->contentType ?? 'mixed';
throw new \RuntimeException(
"Filter |money used in incompatible content type $actualType. Expected text or null."
);
}
// 2. Виконайте перетворення
$formatted = number_format($amount, 2, '.', ',') . ' EUR';
$htmlOutput = '<i>' . htmlspecialchars($formatted) . '</i>'; // Забезпечте правильне екранування!
// 3. Декларуйте вихідний тип вмісту
$info->contentType = ContentType::Html;
// 4. Поверніть результат
return $htmlOutput;
});
$info->contentType
— це рядкова константа з Latte\ContentType
(наприклад, ContentType::Html
, ContentType::Text
, ContentType::JavaScript
тощо) або null
, якщо фільтр застосовано до змінної ({$var|filter}
).
Ви можете читати це значення, щоб перевірити вхідний контекст, і
записувати в нього, щоб оголосити тип вихідного контексту.
Встановлюючи тип вмісту на HTML, ви повідомляєте Latte, що рядок, повернутий вашим фільтром, є безпечним HTML. Latte тоді не буде застосовувати до цього результату своє стандартне автоматичне екранування. Це критично важливо, якщо ваш фільтр генерує HTML-розмітку.
Якщо ваш фільтр генерує HTML, ви несете відповідальність за
правильне екранування будь-яких вхідних даних, використаних у цьому
HTML (як у випадку виклику htmlspecialchars($formatted)
вище). Нехтування цим
може створити XSS-вразливості. Якщо ваш фільтр повертає лише простий
текст, вам не потрібно встановлювати $info->contentType
.
Фільтри на блоках
Усі фільтри, застосовані до блоків, повинні бути контекстними. Це тому, що вміст блоку має визначений тип вмісту (зазвичай HTML), про який фільтр повинен знати.
{block heading|money}1000{/block}
{* Фільтр 'money' отримає '1000' як другий аргумент
і $info->contentType буде ContentType::Html *}
Контекстні фільтри надають потужний контроль над тим, як дані обробляються на основі їхнього контексту, дозволяють розширені функції та забезпечують правильну поведінку екранування, особливо при генерації HTML-вмісту.