Как стать автором
Обновить

Комментарии 37

А если используется встроенный в Twig кэш шаблонов, который сохраняет компиляцию шаблона в файл и далее просто «дёргает» этот файл. Используя ваш метод придётся заново генерировать весь кэш шаблонов, которые используют константу.
так изменится только константа, шаблон останется прежним
Как я понял автор захотел заменить вызов constant('Users::TYPE_TROLL') на просто Users::TYPE_TROLL, что является изменением кода шаблона.
при деплое кэш обычно сбрасывают.
Я файловый кеш и имею в виду. Для того и сделано чтобы при генерировании кеша он ругнулся если константы нет, а если есть — подставил значение.
в скомпилированном классе шаблона
...
if (((isset($context["usertype"]) ? $context["usertype"] : null) == 2)) {
...


2 — это же значение константы Users::TYPE_TROLL? если да, то это строка так и будет выглядеть в кэше шаблона. и если вдруг значение Users::TYPE_TROLL изменится или в обще убрать константу то придётся сбрасывать кэш.
Я в посте описал проблему в самом начале. При выкатке в бой кеш шаблонов компилируется полностью, снуля и целиком, чтобы в бою не пришлось. Именно на этом этапе хорошо было бы ругнуться, чтобы можно было поправить ошибку до того, как она попала в бой. А если ошибки нет, то бонусом значение запилить вместо вызова в рантайме.

Надеюсь, теперь понятно расписал :]
У автора проблема довольно простая. Каждый раз когда кто-то забывает поправить шаблон после изменения констант в моделях, где-то умирает котенок во время сборки проекта в каком-нибудь CI сервере сброс и разогрев кэша не вызывает никаких ошибок, и о том что есть какие-то проблемы с шаблонами мы узнаем только после деплоя. Автор же хотел что бы на этапе сбоорки проекта ДО деплоя, любые проблемы с шаблонами вызванные изменением констант останавливали сборку и CI сервер слал бы уведомление что все плохо и сломано.
Нельзя так делать. Шаблоны перекомпилируются по времени их изменения, а не по изменению классов, которые использует шаблон.
Я бы такой шаблон написал так:

{% if user.troll %}
    fuckoff
{% else %}
    ok
{% endif %}

И соответственно метод User::isTroll()
А вот на счет проверки — да, хорошая штука. Вроде как уже PR есть скоро в ядре подобная проверка будет
В моем случае шаблоны перекомпилируются при выкатке, сделано для ругани на этом этапе, чтобы не допустить кривые константы в бой.
Ну так у вас сейчас включен дебаг в Twig_Environment.
А когда Вы его отключите, то Вам верно выше указали. В случае чего придётся сбрасывать кэш.
Ну или держать постоянно на дебаге, но в таком случае смысла с кэширования нет.
Я даже не знаю, как мне еще объяснить свою проблему и ее решение :(
Прочитайте что-ли пост.
Простите, что огорчаем)
А стоит ли вообще логикой приложения грузить шаблон?

Имхо, если у вас есть некая проверка, не является ли пользователь троллем, то правильнее было бы это обсчитать в ваших моделях, а шаблону просто передать флаг userIsTroll.
Это для наглядности. Бывают места, где таких завязок очень много и выносить это в контроллер или модель не выгодно — получится еще более трудно поддерживаемая простыня свойств и функций, используемых только в одном месте.
Как раз правильно в модель выносить подобные проверки, чтобы не сверяться с константами. Как минимум это способствует сохранности чистоты в шаблонах и выносу логики. Оставили метод isBlabla и потом проверяете. Можете убрать константы, отрефакторить 10 раз, а isBlaBla всегда будет работать, а если нет — ошибку тут же увидите
Точно так же не увижу ошибки и точно так же про шаблон забудется :)
Омг. У вас будет ошибка, что twig не нашел свойства blabla, методов has, is.
Еще раз, логика модельки остается логикой модельки, а ее свойства узнаются через методы.
Вы пишите
{{ user.troll ? 'fuck off' : 'ok' }}

А в модели это может быть хоть как:
public function isTroll()
{
    return $this->status === self::TROLL_STATUS;
}

public function isTroll()
{
    return $this->hasStatus('troll');
}

public function isTroll()
{
    return 100 > count($this->getLastMessages(60*60*6));
}
#!/usr/bin/env php
<?php

require 'vendor/autoload.php';

class JustForTest { }

$loader = new Twig_Loader_String();
$twig = new Twig_Environment($loader);

echo $twig->render('Hello, {{ test.notExists() }}', [
	'test' => new JustForTest()
] );

даже ворнинга не дает, выводит «Hello, ».

И да, я согласен, что по возможности надо выносить такое дело в модели, но это не всегда выгодно, я уже писал выше.
Вы просто твиг готовить не умеете значит. twig.sensiolabs.org/doc/api.html#environment-options — обратите внимание на strict_variables.
Вот исключение:
Twig_Error_Runtime: Method "notExists" for object "JustForTest" does not exist in "Hello, {{ test.notExists() }}" at line 1 in /Users/hell0w0rd/Desktop/test/vendor/twig/twig/lib/Twig/Template.php on line 438
Если вы умеете хорошо готовить, в том числе Twig, — приходите к нам на собеседование :)
Со strict_variables уже не так комфортно, мне кажется. Да и поздно уже его включать, когда шаблонов слишком много.

В любом случае это — уже отдельная тема для обсуждения.
Если принимаете стажеров-студентов, прийду) Для того чтобы было комфортно в симфони например есть тестовое окружение. Где все как на продакшене, но включен режим дебага, в тч strict_variables.
Или вы имеете ввиду что вам переодически нужно использовать несуществующие свойства объектов, а проверки писать не хочется? На мой взгляд выключить strict_vars, это как выключить варнинги и нотисы в php)
Да, я имею в виду что в существующих шаблонах сплошняком завязки на то, что отсутствующее свойство — аналог false. И, на самом деле, это слишком удобно чтобы от этого взять и отказаться.
Насчет стажеров-студентов не уверен, но вас никто не съест, если вы спросите это в отклике на наши вакансии.
А можно пример? Вы бы кстати это в статье указали, что подобное нужно когда отключены strict_vars потому-то потому-то)
Да вот пример — {% if errors %}...{% endif %}.
strict_variables не влияет на константы, а топик про них. Да и даже не особо про них, сколько поверхностно о системе расширений.
Ну тут опять же если везде в шаблонах возможно есть errors, а может и нет — я бы определил глобальную переменную, по умолчанию пустую, а если ошибки есть — переопределять ее в скопе шаблона при передаче аргументов.
Но конкретно в таком виде мне не понятно как у вас собираются аргументы для шаблонов. Обычно это как-то так:
$errors = [];
foreach($form as $field) {
    $errors[] = $field->getError();
}
// Или
$errors = $form->getErrors();

То есть в шаблон и так и так передадутся errors, но в хорошем случае массив будет пуст, и проверка нормально отработает, или же там что-то будет и проверка опять же нормально отработает.
Так что мое мнение уже озвучил — отключать strict равносильно отключению ошибок в php. Вы же вот так:
foreach($foo as $bar) {
    $arr[] = $bar;
}

Не пишите? Вот в шаблонах тоже на мой взгляд так не стоит.
Такой путь ведёт к результирующей каше.

Конечно, заманчиво ввести глобальный вывод ошибок, а для тех контроллеров, для которых это пока что не удалось внедрить, просто ничего не выводить. В итоге логика, которая должна везде работать одинаково, работает по-разному. Вывод ошибок нужен только там, где про ошибки вообще знают. А если про ошибки знают, то результат может быть только один — ошибок нет (errors = null, errors = [], etc.) или ошибки есть (errors = [...]). Это говорит в том числе и о том, что ошибки не забыли импортнуть в шаблон, ошибок действительно нет.

Если бы интерпретатор php изначально шёл в strict-режиме без возможности устраивать хороводы, то никто бы не говорил, что «неинициализированная переменная равна null — это удобно». Нестрогий режим может использоваться только на продакшне (и то только из-за того, что PHP изначально допускает различные режимы). На девелопменте — только strict со всем вытекающим (написал шаблон, шаблон развалился — исправляй).
Это совершенно некорректно и ведет к тем же проблемам, что и в старых версиях php делал register_globals и не выставленный в E_ALL уровень репортинга. Умом понимаю, почему Twig по умолчанию не включает стрикт по умолчанию, но сердце отказывается верить. Да и согласитесь,

{% if errors is defined %}

выглядят куда круче. В крайнем случае это может быть

{% if errors|default('') is empty %}

Когда у нас логика сложнее/ненормальнее.
Тема-то не отдельная. Какой смысл предлагать велосипеды из-за плохо прочитанной документации?
Если у вас в отображении есть логика, не касающаяся самого отображения, то можете перестать употреблять слова вроде «модель», «контроллер», «отображение».

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

С этой точки зрения верстальщику нужно знать только некий «контракт», например:
user — объект, данные и параметры текущего пользователя;
user.firstName — строка, имя пользователя;
user.lastName — строка, фамилия пользователя;
user.troll — булевый флаг, является ли пользователь троллем;
users — список объектов user.

Как именно реализуется user.troll и остальные вещи верстальщику знать не обязательно. Для решения задачи: «Вывести в таблице список пользователей с указанием имени и фамилии. Строки пользователей-троллей помечать классом warning.» верстальщику вы не нужны.
Вы живете в идеальном мире, где все просто и удобно, но на самом деле все не так.

Это — действительно отдельная тема для обсуждения.
На самом деле, это всего лишь ваше неправильное мнение.

Нет никого идеального мира. Контроллер разделяет логику и отображение. К контроллеру нужно относиться так же, как к API сервиса — контекст, который он задаёт шаблону — это все данные, которые шаблону нужно и можно знать.

Вы хотели написать о расширении Twig, но в итоге изначально привели пример того, для чего расширения Twig использовать не нужно.

С этой точки зрения вообще сложно понять fabpot. Кто-кто, а он должен понимать, что предоставление таких расширений по умолчанию — это очень плохо.

В итоге получаем следующее: github.com/fabpot/Twig/issues/1149

Как результат, имеем аналог strict-mode для PHP — sandbox-mode. Это для тех, кто не привык использовать PHP в стиле «ай, пофиг, можно же», а привык думать и разделять.
Нет правильного или неправильного, есть задачи, которые нужно решать.
Если вы думаете что я внезапно решу переписать все шаблоны и контроллеры — вы ошибаетесь.

Предлагаю на этом дискуссию закончить.
Я могу представить только пару кейсов в которых нужно использовать константы в шаблонах:
проверка на то, может ли пользователь делать что-то (тут лучше сделать карту правил для пользователей и там определить что им можно а что нельзя и проверять в шаблоне без привязки к модели и константам)
в зависимости от типа контента нужно что-то другое вывести. тут опять же лучше уж использовать разные шаблоны и общие части вынести в базовый шаблон (слава наследованию шаблонов).

Но сама идея правильная, не только отсутствующие константы (в теории) могут сломать шаблоны не вызывая ошибок при их компиляции. Ну и хороший пример расширения функционала Twig-а.
проверка на то, может ли пользователь делать что-то

user.hasPermission(constant(User::PERM_KILL_CAT)) или acl.subjectHasPermission(user, constant(Acl::PERM_KILL_CAT'))

Вводить для каждого разрешения методы типа user.canKillCat (даже если использовать _call) по-моему избыточно.
Если тролль или не тролль пользователь определяется в результате анализа некоего статуса, представленного «перечислением», то для каждого статуса иметь флаг userIs<Status> (или метод user.Is<Status>()) несколько напряжно может быть. Использование констант в шаблоне тут вполне оправдано, если они используются для определения что пользователю показывать, а что нет (как инфу, так и интерфейсы). А собственно что ещё в шаблоне может быть :)
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории