Фильтры (Pipes)

Полное руководство по фильтрам для обработки и форматирования данных

Основы фильтров

Фильтры позволяют трансформировать данные перед выводом. Они применяются с помощью символа | (pipe):

<!-- Базовый синтаксис -->
{{ variable | filter }}

<!-- Фильтр с параметрами -->
{{ variable | filter(param1, param2) }}

<!-- Цепочка фильтров -->
{{ variable | filter1 | filter2 | filter3 }}

Текстовые фильтры

Изменение регистра

<!-- Данные для примеров -->
{% set greeting = "привет мир" %}
{% set shout = "ПРИВЕТ МИР" %}
{% set sentence = "привет. как дела?" %}

<!-- Верхний регистр -->
{{ greeting | upper }}        <!-- Результат: ПРИВЕТ МИР -->
{{ "привет мир" | upper }}    <!-- Альтернативный способ -->

<!-- Нижний регистр -->
{{ shout | lower }}            <!-- Результат: привет мир -->

<!-- Первая буква каждого слова заглавная -->
{{ greeting | title }}        <!-- Результат: Привет Мир -->

<!-- Первая буква предложения заглавная -->
{{ sentence | capitalize }}    <!-- Результат: Привет. как дела? -->

Обрезка и очистка

<!-- Данные для примеров -->
{% set messy_text = "  текст с пробелами  " %}
{% set user = {
    'bio': 'Я занимаюсь веб-разработкой уже более 10 лет. Специализируюсь на PHP, JavaScript и современных фреймворках. В свободное время изучаю новые технологии.'
} %}
{% set article = {
    'text': 'Веб-разработка сегодня требует знания множества технологий и инструментов. От базового HTML и CSS до сложных фреймворков и систем управления состоянием.'
} %}

<!-- Удаление пробелов -->
{{ messy_text | trim }}        <!-- Результат: "текст с пробелами" -->

<!-- Обрезка по длине символов -->
{{ user.bio | truncate(50) }}          <!-- Первые 50 символов + "..." -->
{{ user.bio | truncate(100, '…') }}    <!-- Первые 100 символов + "…" -->
{{ user.bio | truncate(80, ' →') }}     <!-- Первые 80 символов + " →" -->

<!-- Обрезка по количеству слов -->
{{ article.text | truncate_words(10) }}  <!-- Первые 10 слов + "..." -->
{{ article.text | truncate_words(15, '…') }} <!-- Первые 15 слов + "…" -->

HTML и экранирование

<!-- Данные для примеров -->
{% set user = {
    'description': "Первая строка\nВторая строка\nТретья строка",
    'input': '<script>alert("XSS");</script>Безопасный текст'
} %}
{% set article = {
    'content': '<p>Статья с <strong>форматированием</strong></p><script>alert("bad");</script>'
} %}
{% set search_query = "поиск с пробелами & символами" %}

<!-- Преобразование переносов строк в <br> -->
{{ user.description | nl2br }}
<!-- Результат: Первая строка<br>Вторая строка<br>Третья строка -->

<!-- HTML экранирование (защита от XSS) -->
{{ user.input | escape }}
<!-- Результат: &lt;script&gt;alert("XSS");&lt;/script&gt;Безопасный текст -->

<!-- Удаление HTML тегов -->
{{ article.content | strip_tags }}
<!-- Результат: Статья с форматированиемalert("bad"); -->

<!-- URL кодирование для использования в ссылках -->
{{ search_query | urlencode }}
<!-- Результат: поиск+с+пробелами+%26+символами -->

<!-- URL декодирование -->
{{ "поиск+с+пробелами" | urldecode }}
<!-- Результат: поиск с пробелами -->

Работа с текстом

<!-- Данные для примеров -->
{% set user = { 'name': 'Иван Петров' } %}
{% set test_string = "Тестовая строка" %}
{% set tags = ['php', 'javascript', 'html', 'css'] %}
{% set csv_string = "яблоко,банан,апельсин,груша" %}

<!-- Длина строки -->
{{ user.name | length }}              <!-- Результат: 11 символов -->
{{ test_string | length }}           <!-- Результат: 14 символов -->

<!-- Замена текста -->
{{ "Hello World" | replace("World", "Universe") }}  
<!-- Результат: Hello Universe -->
{{ user.name | replace("Иван", "Александр") }}
<!-- Результат: Александр Петров -->

<!-- Объединение массива в строку -->
{{ tags | join(', ') }}             <!-- Результат: php, javascript, html, css -->
{{ tags | join(' | ') }}            <!-- Результат: php | javascript | html | css -->

<!-- Разделение строки на массив -->
{{ csv_string | split(',') }}       <!-- Результат: ['яблоко', 'банан', 'апельсин', 'груша'] -->

<!-- Использование split с foreach -->
{% foreach csv_string | split(',') as fruit %}
    <li>{{ fruit | trim }}</li>   <!-- Убираем лишние пробелы -->
{% endforeach %}

Числовые фильтры

Форматирование чисел

<!-- Данные для примеров -->
{% set price = 1234.567 %}
{% set big_number = 1234567.89 %}
{% set money_amount = 1234.50 %}

<!-- Базовое форматирование чисел -->
{{ price | number }}                   <!-- Результат: 1,235 (округление до целого) -->
{{ price | number(2) }}                <!-- Результат: 1,234.57 (2 знака после запятой) -->
{{ price | number(0) }}                <!-- Результат: 1,235 (без дробной части) -->
{{ price | number(3) }}                <!-- Результат: 1,234.567 (3 знака) -->

<!-- Кастомные разделители: number(знаки_после_запятой, десятичный_разделитель, разделитель_тысяч) -->
{{ big_number | number(2, ',', ' ') }}   <!-- Результат: 1 234 567,89 (европейский формат) -->
{{ big_number | number(2, '.', "'") }}   <!-- Результат: 1'234'567.89 (швейцарский формат) -->

<!-- Форматирование валюты -->
{{ money_amount | money }}             <!-- Результат: $1,234.50 (доллары по умолчанию) -->
{{ money_amount | money('₽') }}         <!-- Результат: ₽1,234.50 (рубли) -->
{{ money_amount | money('€') }}         <!-- Результат: €1,234.50 (евро) -->

<!-- Кастомное форматирование валюты: money(символ, знаки, десятичный_разделитель, разделитель_тысяч) -->
{{ money_amount | money('€', 2, ',', '.') }} <!-- Результат: €1.234,50 (немецкий формат) -->

Математические операции

<!-- Данные для примеров -->
{% set pi = 3.14159 %}
{% set positive = 3.8 %}
{% set negative = -42 %}
{% set exact = 3.2 %}

<!-- Округление -->
{{ pi | round }}                       <!-- Результат: 3 (до ближайшего целого) -->
{{ pi | round(2) }}                    <!-- Результат: 3.14 (2 знака после запятой) -->
{{ pi | round(4) }}                    <!-- Результат: 3.1416 (4 знака) -->

<!-- Округление вниз (к меньшему целому) -->
{{ positive | floor }}                 <!-- Результат: 3 -->
{{ pi | floor }}                       <!-- Результат: 3 -->

<!-- Округление вверх (к большему целому) -->
{{ exact | ceil }}                     <!-- Результат: 4 -->
{{ pi | ceil }}                        <!-- Результат: 4 -->

<!-- Абсолютное значение (модуль числа) -->
{{ negative | abs }}                   <!-- Результат: 42 -->
{{ positive | abs }}                   <!-- Результат: 3.8 (число уже положительное) -->

Проценты

<!-- Данные для примеров -->
{% set discount_rate = 0.15 %}
{% set precise_rate = 0.1567 %}
{% set total_amount = 1000 %}
{% set discount_amount = 150 %}

<!-- Форматирование процентов (из десятичной дроби) -->
{{ discount_rate | percent }}          <!-- Результат: 15% -->
{{ precise_rate | percent(2) }}        <!-- Результат: 15.67% (2 знака после запятой) -->

<!-- Расчет процентов из данных -->
{{ (discount_amount / total_amount) | percent }}
<!-- Результат: 15% (150/1000 = 0.15 = 15%) -->

<!-- Практический пример: скидка на товар -->
{% set product = {
    'price': 2000,
    'sale_price': 1600
} %}

{% set discount_percent = ((product.price - product.sale_price) / product.price) | percent %}
<p>Скидка: {{ discount_percent }}</p>  <!-- Результат: Скидка: 20% -->

Фильтры дат

Базовое форматирование

<!-- Данные для примеров -->
{% set user = {
    'created_at': '2024-01-15 14:30:45',
    'birth_date': '1990-05-20'
} %}
{% set event = {
    'start_time': '2024-02-01 14:30:00',
    'end_time': '2024-02-01 18:45:00'
} %}
{% set order = {
    'created_at': '2024-01-15 14:30:45'
} %}

<!-- Стандартные форматы дат -->
{{ user.created_at | date('Y-m-d') }}          <!-- Результат: 2024-01-15 -->
{{ user.created_at | date('d.m.Y') }}          <!-- Результат: 15.01.2024 -->
{{ user.created_at | date('d.m.Y H:i') }}      <!-- Результат: 15.01.2024 14:30 -->
{{ user.birth_date | date('F j, Y') }}         <!-- Результат: May 20, 1990 -->

<!-- Форматирование времени -->
{{ event.start_time | date('H:i') }}           <!-- Результат: 14:30 (24-часовой формат) -->
{{ event.start_time | date('g:i A') }}         <!-- Результат: 2:30 PM (12-часовой формат) -->

<!-- Полная дата и время с текстом -->
{{ order.created_at | date('d.m.Y в H:i:s') }}  <!-- Результат: 15.01.2024 в 14:30:45 -->

<!-- Русские названия месяцев (если поддерживается локализацией) -->
{{ user.created_at | date('j F Y г.') }}       <!-- Результат: 15 January 2024 г. -->

Работа с текущим временем

<!-- Данные для примеров -->
{% set user = {
    'last_login': 1705332000
} %}

<!-- Текущее время -->
{{ 'now' | date('Y-m-d H:i:s') }}            <!-- Результат: 2024-01-15 14:30:45 -->
{{ 'now' | date('d.m.Y') }}                  <!-- Результат: 15.01.2024 -->

<!-- Время для разных задач -->
<p>Сегодня: {{ 'now' | date('d.m.Y') }}</p>
<p>Сейчас: {{ 'now' | date('H:i') }}</p>

<!-- Работа с временными интервалами -->
{% set time_diff = time() - user.last_login %}
<p>Не был в сети: {{ time_diff }} секунд</p>

<!-- Форматирование интервала времени как время -->
{% if time_diff < 86400 %}  <!-- если меньше суток -->
    <p>Прошло времени: {{ time_diff | date('H:i:s', true) }}</p>
{% endif %}

Фильтры массивов

Основные операции

<!-- Данные для примеров -->
{% set users = [
    {'name': 'Иван', 'age': 25},
    {'name': 'Мария', 'age': 30},
    {'name': 'Петр', 'age': 22}
] %}
{% set numbers = [3, 1, 4, 1, 5, 9, 2, 6] %}
{% set fruits = ['яблоко', 'банан', 'яблоко', 'апельсин', 'банан'] %}

<!-- Длина массива (количество элементов) -->
{{ users | length }}                       <!-- Результат: 3 -->
{{ numbers | length }}                     <!-- Результат: 8 -->

<!-- Первый и последний элементы -->
{{ users | first }}                        <!-- Результат: {'name': 'Иван', 'age': 25} -->
{{ users | last }}                         <!-- Результат: {'name': 'Петр', 'age': 22} -->

<!-- Срез массива: slice(начало, количество) -->
{{ numbers | slice(0, 3) }}                <!-- Результат: [3, 1, 4] (первые 3 элемента) -->
{{ numbers | slice(2, 4) }}                <!-- Результат: [4, 1, 5, 9] (с 3-го элемента, 4 штуки) -->
{{ users | slice(1, 2) }}                  <!-- Результат: [{'name': 'Мария', 'age': 30}, {'name': 'Петр', 'age': 22}] -->

Сортировка и фильтрация

<!-- Данные для примеров -->
{% set users = [
    {'name': 'Петр', 'age': 22, 'salary': 50000},
    {'name': 'Иван', 'age': 25, 'salary': 60000},
    {'name': 'Мария', 'age': 30, 'salary': 75000}
] %}
{% set products = [
    {'name': 'Ноутбук', 'price': 80000, 'category': 'electronics'},
    {'name': 'Мышь', 'price': 1500, 'category': 'electronics'},
    {'name': 'Книга', 'price': 500, 'category': 'books'}
] %}
{% set numbers = [3, 1, 4, 1, 5, 9, 2, 6] %}
{% set tags = ['php', 'javascript', 'php', 'html', 'css', 'javascript'] %}

<!-- Простая сортировка массива -->
{{ numbers | sort }}                        <!-- Результат: [1, 1, 2, 3, 4, 5, 6, 9] (по возрастанию) -->
{{ numbers | sort('asc') }}                 <!-- Результат: [1, 1, 2, 3, 4, 5, 6, 9] -->
{{ numbers | sort('desc') }}                <!-- Результат: [9, 6, 5, 4, 3, 2, 1, 1] (по убыванию) -->

<!-- Сортировка объектов по полю -->
{{ users | sort_by('name') }}               <!-- Сортировка по имени (А-Я) -->
{{ users | sort_by('age', 'asc') }}         <!-- Сортировка по возрасту (младше → старше) -->
{{ products | sort_by('price', 'desc') }}    <!-- Сортировка по цене (дороже → дешевле) -->

<!-- Обратный порядок -->
{{ users | reverse }}                       <!-- Меняет порядок элементов на противоположный -->

<!-- Уникальные значения (удаление дубликатов) -->
{{ tags | unique }}                         <!-- Результат: ['php', 'javascript', 'html', 'css'] -->

<!-- Пример: отсортированные уникальные теги -->
{{ tags | unique | sort }}                 <!-- Результат: ['css', 'html', 'javascript', 'php'] -->

Ключи и значения

<!-- Данные для примеров -->
{% set config = {
    'app_name': 'Мое приложение',
    'debug': true,
    'database': {
        'host': 'localhost',
        'port': 3306
    },
    'cache_enabled': false
} %}
{% set user = {
    'name': 'Иван Петров',
    'email': 'ivan@example.com',
    'password': 'secret123',
    'role': 'admin'
} %}
{% set orders = [
    {'id': 1, 'status': 'completed', 'user_id': 10},
    {'id': 2, 'status': 'pending', 'user_id': 15},
    {'id': 3, 'status': 'completed', 'user_id': 10},
    {'id': 4, 'status': 'cancelled', 'user_id': 20}
] %}

<!-- Извлечение ключей и значений -->
{{ config | keys }}                         <!-- Результат: ['app_name', 'debug', 'database', 'cache_enabled'] -->
{{ config | values }}                       <!-- Результат: ['Мое приложение', true, {...}, false] -->

<!-- Дополнительные операции с ключами -->
{{ config | has_key('database') }}           <!-- Результат: true (проверить наличие ключа) -->
{{ config | get('app_name', 'Default App') }} <!-- Результат: 'Мое приложение' (получить с дефолтом) -->
{{ config | get('missing_key', 'Дефолт') }}   <!-- Результат: 'Дефолт' -->

<!-- Выбор/исключение полей -->
{{ user | pick('name', 'email') }}           <!-- Результат: {'name': 'Иван Петров', 'email': 'ivan@example.com'} -->
{{ user | omit('password', 'secret') }}       <!-- Исключить поля password и secret -->

<!-- Преобразование в массив -->
{{ config | to_array | keys }}               <!-- Преобразовать объект в массив и получить ключи -->

<!-- Рекурсивное получение всех ключей (включая вложенные) -->
{{ config | keys_recursive }}              <!-- Результат: ['app_name', 'debug', 'database', 'database.host', 'database.port', 'cache_enabled'] -->

<!-- Группировка элементов по полю -->
{{ orders | group_by('status') }}           <!-- Группировка заказов по статусу -->
<!-- Результат: {
    'completed': [{'id': 1, ...}, {'id': 3, ...}],
    'pending': [{'id': 2, ...}],
    'cancelled': [{'id': 4, ...}]
} -->

<!-- Практические примеры использования -->

<!-- Перебор ключей конфигурации -->
<ul>
{% foreach config | keys as key %}
    <li>{{ key }}: {{ config | get(key) }}</li>
{% endforeach %}
</ul>

<!-- Проверка наличия ключа -->
{% if config | has_key('debug') %}
    <p>Режим отладки: {{ config.debug ? 'включен' : 'выключен' }}</p>
{% endif %}

<!-- Создание отсортированного списка настроек -->
<ul>
{% foreach config | keys | sort as key %}
    <li><strong>{{ key }}:</strong> {{ config | get(key) }}</li>
{% endforeach %}
</ul>

Условные фильтры

Значения по умолчанию

<!-- Данные для примеров -->
{% set user = {
    'name': 'Иван',
    'nickname': null,           
    'bio': '',                 
    'avatar': '/path/image.jpg',
    'tags': []                  
} %}
{% set product = {
    'name': 'Товар',
    'image': null,
    'price': 0,                 
    'description': false       
} %}

<!-- БАЗОВЫЙ default - для всех "пустых" значений (null, '', [], false, 0) -->
{{ user.nickname | default('Аноним') }}       <!-- null → 'Аноним' -->
{{ user.bio | default('Нет описания') }}      <!-- '' → 'Нет описания' -->
{{ user.tags | default(['новичок']) }}        <!-- [] → ['новичок'] -->
{{ product.price | default('Цена не указана') }} <!-- 0 → 'Цена не указана' -->
{{ product.image | default('/no-image.png') }}  <!-- null → '/no-image.png' -->

<!-- default_if_null - ТОЛЬКО для NULL значений -->
{{ user.nickname | default_if_null('Аноним') }}      <!-- null → 'Аноним' -->
{{ user.bio | default_if_null('Нет описания') }}       <!-- '' остается '' -->
{{ product.price | default_if_null(0) }}              <!-- null → 0, false остается false -->

<!-- default_if_empty - ТОЛЬКО для пустых строк/массивов (НЕ null) -->
{{ user.bio | default_if_empty('Не указано') }}        <!-- '' → 'Не указано' -->
{{ user.nickname | default_if_empty('Не указано') }}   <!-- null остается null -->
{{ user.tags | default_if_empty(['общие']) }}          <!-- [] → ['общие'] -->

<!-- Практические примеры -->
<div class="user-card">
    <h3>{{ user.name }} ({{ user.nickname | default('без ника') }})</h3>
    <img src="{{ user.avatar | default('/default-avatar.png') }}" alt="Аватар">
    <p>{{ user.bio | default('Пользователь не добавил описание') }}</p>
</div>

Условное применение

<!-- Данные для примеров -->
{% set user = {
    'role': 'admin',
    'is_admin': true
} %}
{% set content = {
    'text': '<p>Безопасный <b>HTML</b> контент</p>',
    'trust_html': true
} %}
{% set sensitive_data = 'Секретная информация' %}

<!-- Условное применение фильтров (требует реализации в движке) -->
<!-- Показываем данные заглавными буквами только админам, остальным скрываем -->
{% if user.is_admin %}
    {{ sensitive_data | upper }}           <!-- СЕКРЕТНАЯ ИНФОРМАЦИЯ -->
{% else %}
    {{ '***' }}                           <!-- *** -->
{% endif %}

<!-- Условное экранирование контента -->
{% if content.trust_html %}
    {!! content.text !!}                       <!-- Выводим как HTML -->
{% else %}
    {{ content.text | escape }}            <!-- Экранируем HTML -->
{% endif %}

<!-- Альтернативный способ через тернарный оператор -->
{!! content.trust_html ? content.text : (content.text | escape) !!}

Специальные фильтры

JSON и сериализация

<!-- Данные для примеров -->
{% set user_data = {
    'id': 123,
    'name': 'Иван Петров',
    'email': 'ivan@example.com',
    'preferences': {
        'theme': 'dark',
        'language': 'ru'
    }
} %}
{% set app_config = {
    'api_url': 'https://api.example.com',
    'timeout': 5000,
    'features': ['auth', 'notifications', 'analytics']
} %}

<!-- JSON кодирование для использования в JavaScript -->
{{ user_data | json }}
<!-- Результат: {"id":123,"name":"Иван Петров","email":"ivan@example.com","preferences":{"theme":"dark","language":"ru"}} -->

<!-- Форматированный JSON (более читаемый) -->
<pre>{{ app_config | json }}</pre>
<!-- Результат (если поддерживает форматирование):
{
    "api_url": "https://api.example.com",
    "timeout": 5000,
    "features": ["auth", "notifications", "analytics"]
} -->

<!-- Практическое использование в JavaScript -->
<script>
    // Передача данных пользователя в JavaScript
    var userData = {{ user_data | json }};
    console.log('Пользователь:', userData.name);
    
    // Передача конфигурации приложения
    var config = {{ app_config | json }};
    fetch(config.api_url + '/users/' + userData.id);
    
    // Безопасная передача массивов
    var features = {{ app_config.features | json }};
    features.forEach(function(feature) {
        console.log('Доступна функция:', feature);
    });
</script>

Base64 и кодирование

<!-- Данные для примеров -->
{% set message = 'Привет, мир!' %}
{% set encoded_data = 'UHJpdmV0LCDQvNC40YAh' %}  <!-- Base64 'Привет, мир!' -->
{% set search_params = 'поиск товаров & фильтры' %}
{% set callback_url = 'https://site.com/callback?param=значение&other=тест' %}

<!-- Base64 кодирование/декодирование -->
{{ message | base64_encode }}              <!-- Результат: UHJpdmV0LCDQvNC40YAh -->
{{ encoded_data | base64_decode }}          <!-- Результат: Привет, мир! -->

<!-- URL кодирование для безопасной передачи в параметрах -->
{{ search_params | urlencode }}             <!-- Результат: поиск+товаров+%26+фильтры -->
{{ callback_url | urlencode }}              <!-- Кодирование полного URL -->

<!-- URL декодирование -->
{{ 'поиск+товаров' | urldecode }}           <!-- Результат: поиск товаров -->

<!-- Практические примеры -->
<!-- Скрытое поле с закодированными данными -->
<input type="hidden" name="data" value="{{ user_data | json | base64_encode }}">

<!-- Ссылка с параметром поиска -->
<a href="/search?q={{ search_query | urlencode }}">Поиск</a>

<!-- Redirect URL в параметрах -->
<a href="/login?redirect={{ current_url | urlencode }}">Войти</a>

Хеширование

<!-- Данные для примеров -->
{% set user = {
    'email': 'ivan@example.com',
    'id': 123
} %}
{% set sensitive_data = 'секретные данные пользователя' %}
{% set password = 'mypassword123' %}

<!-- MD5 хеш (не рекомендуется для паролей!) -->
{{ user.email | md5 }}                      <!-- Результат: MD5 хеш email -->

<!-- SHA1 хеш -->
{{ sensitive_data | sha1 }}                 <!-- Результат: SHA1 хеш данных -->

<!-- SHA256 хеш (рекомендуется) -->
{{ password | sha256 }}                     <!-- Результат: SHA256 хеш пароля -->

<!-- Практические применения -->
<!-- Уникальный ID на основе email -->
{% set user_hash = user.email | md5 %}
<div class="user-avatar" data-hash="{{ user_hash }}">
    <!-- Можно использовать для Gravatar -->
    <img src="https://www.gravatar.com/avatar/{{ user_hash }}" alt="Avatar">
</div>

<!-- Хеширование для кеширования -->
{% set cache_key = (user.id ~ user.email ~ 'profile') | sha1 %}
<!-- cache_key можно использовать как уникальный ключ кеша -->

Цепочки фильтров

Последовательная обработка

<!-- Данные для примеров -->
{% set user = {
    'bio': '   Я веб-разработчик с большим опытом работы. Специализируюсь на создании современных приложений и изучении новых технологий. В свободное время читаю книги и занимаюсь спортом.   \n\nТакже увлекаюсь фотографией.',
    'first_name': '  иван  ',
    'last_name': '  петров  '
} %}
{% set article = {
    'title': 'как создать современное веб-приложение на php'
} %}
{% set product = {
    'base_price': 1000,
    'tax_rate': 0.2
} %}

<!-- Сложная обработка биографии пользователя -->
{{ user.bio | trim | truncate(100) | nl2br }}
<!-- Результат: 
1. trim - убирает пробелы в начале и конце
2. truncate(100) - обрезает до 100 символов с "..."
3. nl2br - заменяет переносы строк на <br>
-->

<!-- Создание URL-friendly заголовка статьи -->
{{ article.title | lower | replace(' ', '-') | replace('[^a-zа-я0-9-]', '') }}
<!-- Результат: как-создать-современное-веб-приложение-на-php -->

<!-- Расчет и форматирование цены с налогом -->
{% set final_price = product.base_price * (1 + product.tax_rate) %}
{{ final_price | round(2) | money('₽') }}
<!-- Результат: ₽1,200.00 (1000 * 1.2 = 1200, округлено и отформатировано) -->

<!-- Форматирование полного имени -->
{{ (user.first_name | trim | title) ~ ' ' ~ (user.last_name | trim | title) }}
<!-- Результат: Иван Петров -->

Практические примеры

Карточка товара

<!-- Данные для примеров -->
{% set product = {
    'name': 'Игровой ноутбук ASUS ROG Strix G15',
    'description': '<p>Мощный игровой ноутбук с процессором AMD Ryzen 7 и видеокартой RTX 3060. Идеально подходит для современных игр и работы.</p><script>alert("xss");</script><p>Диагональ экрана 15.6 дюймов.</p>',
    'price': 89999,
    'sale_price': 74999,
    'rating': 4.7,
    'reviews_count': 156,
    'category': {
        'name': 'notebooks'
    },
    'tags': 'gaming, asus, laptop, rtx, amd,gaming,laptop',
    'availability': 'in_stock'
} %}

<div class="product-card">
    <!-- Заголовок товара: обрезаем до 50 символов, делаем красивым -->
    <h3>{{ product.name | title | truncate(50) }}</h3>
    
    <div class="price">
        {% if product.sale_price %}
            <!-- Цена со скидкой -->
            <span class="sale">{{ product.sale_price | money('₽') }}</span>
            <span class="original">{{ product.price | money('₽') }}</span>
            
            <!-- Вычисляем процент скидки -->
            {% set discount = ((product.price - product.sale_price) / product.price * 100) | round %}
            <span class="discount">-{{ discount }}%</span>
            <!-- Результат: Скидка 17% (89999-74999)/89999*100 = 16.67 ≈ 17 -->
        {% else %}
            <span class="price">{{ product.price | money('₽') }}</span>
        {% endif %}
    </div>
    
    <!-- Описание: удаляем HTML теги, обрезаем до 20 слов -->
    <p class="description">
        {{ product.description | strip_tags | truncate_words(20) }}
    </p>
    
    <div class="meta">
        <!-- Категория заглавными буквами -->
        <span class="category">{{ product.category.name | upper }}</span>
        
        <!-- Рейтинг с одним знаком после запятой -->
        <span class="rating">
            {{ product.rating | number(1) }}★ 
            ({{ product.reviews_count | number }} отзывов)
            <!-- Результат: 4.7★ (156 отзывов) -->
        </span>
    </div>
    
    <!-- Обработка тегов: разбиваем, чистим, убираем дубли, берем первые 3 -->
    <div class="tags">
        {% set formatted_tags = product.tags | split(',') | map('trim') | unique | slice(0, 3) %}
        {% foreach formatted_tags as tag %}
            <span class="tag">{{ tag | lower }}</span>
        {% endforeach %}
        <!-- Результат: gaming, asus, laptop -->
    </div>
    
    <!-- Статус доступности -->
    <div class="availability">
        {% set status_text = product.availability | replace('_', ' ') | title %}
        <span class="status {{ product.availability }}">{{ status_text }}</span>
        <!-- Результат: In Stock -->
    </div>
</div>

Профиль пользователя

<!-- Данные для примеров -->
{% set user = {
    'avatar': null,
    'first_name': '  иван  ',
    'last_name': '  ПЕТРОВ  ',
    'username': 'IvanPetrov123',
    'bio': 'Веб-разработчик со стажем более 5 лет.\n\nСпециализируюсь на PHP, JavaScript и современных фреймворках.\n\nВ свободное время изучаю новые технологии и читаю техническую литературу.',
    'posts_count': 1247,
    'followers_count': 5439,
    'created_at': '2019-03-15 10:30:00',
    'last_seen': '2024-01-15 14:25:00',
    'email': 'ivan.petrov@example.com',
    'location': 'москва, россия'
} %}

<div class="user-profile">
    <div class="avatar">
        <!-- Используем Gravatar или дефолтный аватар -->
        {% set avatar_url = user.avatar | default('https://www.gravatar.com/avatar/' ~ (user.email | md5) ~ '?d=mp&s=200') %}
        <img src="{{ avatar_url }}" 
             alt="Аватар пользователя {{ user.username | escape }}">
    </div>
    
    <div class="info">
        <!-- Форматируем полное имя: убираем пробелы, делаем красивым -->
        {% set full_name = (user.first_name | trim | title) ~ ' ' ~ (user.last_name | trim | title) %}
        <h2>{{ full_name | trim }}</h2>  <!-- Результат: Иван Петров -->
        
        <!-- Username в нижнем регистре -->
        <p class="username">@{{ user.username | lower }}</p>
        
        {% if user.bio %}
            <div class="bio">
                <!-- Биография: переносы строк в br, обрезаем до 200 символов -->
                {{ user.bio | nl2br | truncate(200) }}
            </div>
        {% endif %}
        
        <!-- Локация с красивым форматированием -->
        {% if user.location %}
            <p class="location">📍 {{ user.location | title }}</p>
            <!-- Результат: 📍 Москва, Россия -->
        {% endif %}
        
        <div class="stats">
            <!-- Форматируем большие числа -->
            <div class="stat">
                <span class="number">{{ user.posts_count | number }}</span>
                <span class="label">{{ user.posts_count | pluralize('пост', 'поста', 'постов') }}</span>
            </div>
            <div class="stat">
                <span class="number">{{ user.followers_count | number }}</span>
                <span class="label">{{ user.followers_count | pluralize('подписчик', 'подписчика', 'подписчиков') }}</span>
            </div>
        </div>
        
        <div class="meta">
            <!-- Дата регистрации -->
            <p class="joined">
                📅 Регистрация: {{ user.created_at | date('d.m.Y') }}
                <!-- Результат: 📅 Регистрация: 15.03.2019 -->
            </p>
            
            {% if user.last_seen %}
                <p class="last-seen">
                    <!-- Показываем когда был в последний раз -->
                    🕐 Последняя активность: {{ user.last_seen | date('d.m.Y H:i') }}
                    <!-- Результат: 🕐 Последняя активность: 15.01.2024 14:25 -->
                </p>
            {% endif %}
        </div>
        
        <!-- Контактная информация (частично скрытая) -->
        <div class="contact">
            {% set masked_email = user.email | replace('@', '***@') %}
            <p class="email">✉️ {{ masked_email }}</p>
            <!-- Результат: ✉️ ivan.petrov***@example.com -->
        </div>
    </div>
</div>
Следующий шаг: Изучите функции MNRFY.