Бэкенд / If-блоки, многоуровневое наследование в twig

Так давайте еще немного потерзаем наш второй проект.

Добавление активного класса

Помните вам в 3-ем задании надо было подсвечивать активный пункт в меню и там мы использовали <?= $is_image ? "active" : '' ?>.

Теперь посмотрим как это делать в Twig. Можно, в принципе, использовать условный оператор.

Например, я хочу, чтобы на главной страницы был добавлен класс “active”. Делается это так:

<div>
    <a href="/" class="{% if title == 'Главная' %}active{% endif %}">Главная</a>
    <a href="/mermaid">Русалка</a>
    <a href="/uranus">Уран</a>
</div>

выглядит немного проще чем на чистом php, но все равно громоздко.

К счастью в twig эту конструкцию можно записать в упрощённом виде с использование тернарного оператора, вот так:

<div>
    <a href="/" class="{{ title == 'Главная' ? 'active' : '' }}">Главная</a>
    <a href="/mermaid">Русалка</a>
    <a href="/uranus">Уран</a>
</div>

так как это однострочная операция тут используются двойные фигурные скобки {{ ... }}

А на самом деле есть еще более простая форма записи, вот так:

<div>
    <a href="/" class="{{ title == 'Главная' ? 'active' }}">Главная</a>
    <a href="/mermaid">Русалка</a>
    <a href="/uranus">Уран</a>
</div>

то есть, если вы после двоеточия выдаете пустую строку, то прописывать это не обязательно.

Формируем навигацию динамически

Сейчас у нас навигация делается вручную, то есть мы прописываем каждую ссылку прямо в разметке. Это нормально, когда пунктов меню мало, но создает проблемы, когда их становится много и еще если каждый надо стилизовать как-нибудь.

Для этих целей в twig встроена возможность создавать элементы в цикле. То есть у вас условно есть список элементов меню, а в шаблоне вы просто проходитесь по этому списку и динамически создаете элементы. И даже если у вас 100 пунктов меню вы разметку прописываете только для одного.

Давайте сделаем список под меню в index.php, вот так:

// ...
$title = "";
$template = "";

$context = [];
$menu = [ // добавил список словариков
    [
        "title" => "Главная",
        "url" => "/",
    ],
    [
        "title" => "Русалка",
        "url" => "/mermaid",
    ],
    [
        "title" => "Уран",
        "url" => "/uranus",
    ]
];
// ...

теперь запихаем это в контекст:

// ...
$context['title'] = $title;
$context['menu'] = $menu; // передаем меню в контекст

echo $twig->render($template, $context);

и обновим шаблон __layout.twig чтобы он создавал меню на основании этого списка:

<body>
    <!-- ЭТО УБИРАЕМ
    <div>
        <a href="/">Главная</a>
        <a href="/mermaid">Русалка</a>
        <a href="/uranus">Уран</a>
    </div>-->

    <div> <!-- добавляем цикл по всем элементам меню -->
        {% for item in menu %}
            <!--так как item это словарик с двумя ключами, используем их для вывода ссылки -->
            <a href="{{item.url}}">{{item.title}}</a>
        {% endfor %}
    </div>

    {% block content %}
        пустота
    {% endblock %}
</body>

плюс можно и на активный элемент тут сразу проверять:

<div>
    {% for item in menu %}
        <a href="{{item.url}}" class="{{ title == item.title ? 'active' }}">{{item.title}}</a>
    {% endfor %}
</div>

добавим какой-нибудь стиль для активной ссылки, чтобы лучше было видно:

<!DOCTYPE html>
<html lang="en">
<head>
     <!-- ... -->
    <style>
        /* добавил стиль */
        a.active {
            background-color: yellow;
        }
    </style>
</head>
<body>
    <div>
        {% for item in menu %}
            <a href="{{item.url}}" class="{{ title == item.title ? 'active' }}">{{item.title}}</a>
        {% endfor %}
    </div>

    <!-- ... -->
</body>
</html>

и протестим:

работает! =)

Многоуровневое наследование шаблонов

Согласно заданию у вас есть главный шаблон, а есть еще шаблон объекта, который наследует главный шаблон. И этот шаблон объекта должен наследоваться шаблонами, уточняющими отображение объекта шаблона.

И допустим я хочу, чтобы для объектов писалась какая-нибудь фраза которая использует $title ну как-то так:

давайте попробуем наследовать шаблон base_image от __object ну типа чтобы у нас надпись была сверху и плюс еще картинка. Попробуем сначала так:

{% extends "__object.twig" %}

{% block content %}
<img src="{{ image }}" style="width: 300px;"/>
{% endblock %}

смотрим:

хм, почему-то ничего не поменялось…

В чем же дело?

Дело в том что наш шаблон base_image переопределяет содержимое блока {% block content %}{% endblock %}, и из-за этого фраза, которая там присутствует в шаблоне __object теряется.

Поэтому, когда вы делаете двухуровненвое наследование надо внутри нового шаблона, в нашем случае __object, определять новый блок внутри блока content, с другим именем. Вот так:

{% extends "__layout.twig" %}

{% block content %}
    <div>
        {{title}} -- чудо природы
    </div>

    {% block objectContent %}
    {% endblock %}
{% endblock %}

в качестве имени objectContent можно использовать что угодно, я вот решил так назвать, вы можете по-другому. Но суть в том, что теперь в base_image.twig вы должны переопределять содержимое не {% block content %}{% endblock %} а {% block objectContent %}{% endblock %}, следующим образом

{% extends "__object.twig" %}

{% block objectContent %}<!-- поменял тут content на objectContent -->
<img src="{{ image }}" style="width: 300px;"/>
{% endblock %}

смотрим:

то что надо)

Понятно, что в данном примере такая иерархия избыточна. Но если у вас у того же Урана есть какой-то набор своих пунктов меню, это именно то что вам надо.

Что примерно должно получится

Можно теперь вернуться к основному проекту. Возникнет сразу вопрос, как туда все подключать и как там писать код. Должно получится что-то такое:

6

Переписать сайт из 2-го задания на рельсы Twig

  • Должно быть минимум два базовых шаблона шаблона, всего шаблонов будет порядка 6 штук,
    • общий шаблон
    • под главную страницу (наследует общий),
    • под страницу объекта (наследует общий),
    • под страницу с картинкой объекта (наследует шаблон объекта)
    • и под страницу с информацией о первом экземпляре объекта (наследует шаблон объекта)
    • и под страницу с информацией о втором экземпляре объекта (наследует шаблон объекта)
  • Использовать цикл для вывода пунктов меню на главной странице
  • [не обязательно] Использовать цикл для вывода пунктов в навигации