Twig — мощный, но простой в освоении шаблонизатор. Это также мой личный фаворит, поскольку вся моя веб-разработка основана на Symfony или Silex .
Помимо основного синтаксиса ( {{ ... }}
и {% ... %}
), Twig имеет встроенную поддержку различных фильтров. Фильтр похож на «конвертер». Он получает определенные исходные данные (строку, число, дату и т. Д.) И, применяя преобразование, выводит данные в новой форме (в виде строки, числа, даты и т. Д.). Например, фильтр number_format
может преобразовать число в более читаемое:
{{price|number_format(2, '.', ',')}}
Предполагая, что price
— это переменная со значением 1234567.12345
, после операции фильтрации на странице будет отображаться 1 1,234,567.12
: 2 десятичных знака, «.» В качестве десятичной точки и «,» в качестве разделителя тысяч. Это делает его намного более читабельным.
В качестве другого примера, capitalize
будут делать каждую первую букву слова в верхнем регистре предложения, а другие строчные:
{{title|capitalize}}
Если предположить, что title
— это переменная со значением, this tutorial is nice
, то после операции фильтрации вывод будет « This Tutorial Is Nice
this tutorial is nice
.
Twig имеет ряд встроенных фильтров. Полный список можно найти в его официальной документации .
Зачем выбирать фильтр?
Некоторые могут утверждать, что вышеуказанная функциональность также выполнима в PHP; это правда. Мы также часто находим функции, предоставляемые встроенными фильтрами, довольно ограниченными и недостаточными. Итак, зачем использовать фильтр?
В среде MVC уровень модели отвечает за предоставление данных (например, цена книги или название статьи). Слой представления отвечает за отображение данных. Выполнение преобразования данных в стиле фильтра в контроллере не рекомендуется, поскольку это противоречит роли конструктора контроллера, и выполнение этого в модели эффективно изменяет данные, что не очень хорошо. На мой взгляд, мнение является единственным жизнеспособным вариантом.
Кроме того, поскольку конкретное преобразование данных может запрашиваться во многих местах в шаблоне (а также в различных шаблонах) для одних и тех же данных из различных источников, лучше вызывать этот фильтр в шаблоне каждый раз, когда требуется такое преобразование. , чем вызывать функцию в контроллере. Код будет намного аккуратнее.
Рассмотрим сравнение следующих сегментов кода с использованием фильтра и вызова функции PHP (с использованием Symfony 2 + Doctrine). Мы можем легко увидеть различия в элегантности и удобстве использования.
Версия фильтра:
... <em>{{ book.title|capitalize }}</em> has {{book.pages|number_format(2, ".", ",")}} and costs ${{ book.price|number_format(2, ".", ",")}}. ...
И для этого подхода то, что мы делаем в контроллере, будет:
$book=$repo->findById('00666'); ... return $this->render('A_Template', ['book'=>$book]);
Найдите книгу (данные) и передайте ее для просмотра.
Но если мы используем вызовы функций PHP, код может выглядеть так:
//Using PHP function within a Symfony framework and Doctrine $book=$repo->findById('00666'); $book['tmpTitle'] = ucwords($book->getTitle); $book['tmpPage'] = number_format($book->getPages(), 2, ".", ","); $book['tmpPrice'] = number_format($book->getPrice(), 2, ".", ","); ... return $this->render('A_Template', ['book'=>$book]);
.. а потом в шаблоне
<em>{{ book.tmpTitle }}</em> has {{book.tmpPages}} and costs ${{ book.tmpPrice}}.
Вы можете видеть, что подход фильтра гораздо чище и проще в управлении, без каких-либо неуклюжих временных переменных между ними.
Давайте построим фильтр
Мы создадим фильтр для более удобного отображения даты / времени публикации. То есть вместо того, чтобы говорить что-то вроде «Опубликовано в 2015-03-14 13:34
», эта метка времени будет преобразована в нечто вроде « Только сейчас », « Несколько часов назад », « Несколько дней назад », « Довольно некоторое время назад »,« Давным-давно »и т. д.
Мы построим это способом TDD. Чтобы познакомиться с TDD, просмотрите этот пост и ссылки в нем, но подходы, которые мы используем в этом руководстве, должны быть достаточно простыми для понимания даже без предварительного изучения TDD.
Сначала установите PHPUnit
, выполнив следующую команду Composer:
composer global require phpunit/phpunit
Это установит самую последнюю версию PHPUnit во всем мире, делая ее доступной на всей вашей машине из любой папки. Мы будем использовать PHPUnit для запуска тестов и утверждать, что все ожидания оправданы.
Установить ожидания
Вариант использования понятен: мы хотим преобразовать объект даты / времени ( 2014-03-19 12:34
) в нечто вроде « Только сейчас », « Несколько часов назад », « Несколько дней назад », « Довольно время назад »,« давно, давно »и т. д., в зависимости от того, как далеко дата / время от текущего момента.
Не существует установленного правила, определяющего, насколько далеко должна быть дата / время, чтобы она могла отображаться как « Довольно давно ». Это субъективный вопрос, поэтому мы определим настроенный набор правил для нашего приложения, и эти правила будут отражены в наших ожиданиях:
Как давно | Быть расцененным как |
---|---|
< 1 minute |
Прямо сейчас |
< 10 minutes |
Несколько минут назад |
< 1 hour |
В течение часа |
< 16 hours |
Несколько часов назад |
< 24 hours |
В течение одного дня |
< 3 days |
Некоторое время назад |
< 10 days |
Давным-давно |
> 10 days |
С Марса |
Давайте переведем эти ожидания в тестовый скрипт, чтобы мы могли протестировать его с помощью PHPUnit. Этот скрипт сохраняется в src/AppBundle/Tests/Twig/timeUtilTest.php
:
<?php namespace AppBundle\Tests\Twig; use AppBundle\Twig\AppExtension; class timeUtilTest extends \PHPUnit_Framework_TestCase { /** * @dataProvider tsProvider */ public function testtssFilter($testTS, $expect) { $tu=new AppExtension(); $output=$tu->tssFilter($testTS); $this->assertEquals($output, $expect); } public static function tsProvider() { return [ [date_sub(new \DateTime(), new \DateInterval("PT50S")), "Just now"], [date_sub(new \DateTime(), new \DateInterval("PT2M")), "Minutes ago"], [date_sub(new \DateTime(), new \DateInterval("PT57M")), "Within an hour"], [date_sub(new \DateTime(), new \DateInterval("PT13H1M")), "A few hours ago"], [date_sub(new \DateTime(), new \DateInterval("PT21H2M")), "Within one day"], [date_sub(new \DateTime(), new \DateInterval("P2DT2H2M")), "Some time back"], [date_sub(new \DateTime(), new \DateInterval("P6DT2H2M")), "Ages ago"], [date_sub(new \DateTime(), new \DateInterval("P13DT2H2M")), "From Mars"], ]; } }
Если мы запустим этот тест сейчас:
phpunit -c app/
Тест не будет запущен, потому что мы еще не определили AppBundle\Twig\AppExtension
. Мы можем быстро создать файл скелета: src/AppBundle/Twig/AppExtension.php
. Это может быть так просто, как это:
namespace AppBundle\Twig; class AppExtension extends \Twig_Extension { public function getFilters() { return [ new \Twig_SimpleFilter('tss', [$this, 'tssFilter']), ]; } public function getName() { return 'app_extension'; } public function tssFilter(\DateTime $timestamp) { // to be implemented } }
Теперь мы можем запустить тестовый скрипт. Все тесты (ожидания) не пройдут, потому что мы ничего не сделали для реализации функции tssFilter
.
ПРИМЕЧАНИЕ: Symfony2 очень хорошо работает с PHPUnit. При стандартной настройке Symfony2 в папке app
проекта есть файл phpunit.xml.dist
. Приведенная выше команда автоматически использует этот файл в качестве файла конфигурации для PHPUnit. Как правило, дальнейшая настройка не требуется.
Полный код функции tssFilter
приведен ниже:
public function tssFilter(\DateTime $timestamp) { $TSS=['Just now','Minutes ago','Within an hour','A few hours ago','Within one day','Some time back','Ages ago', 'From Mars']; $i=-1; $compared = new \DateTime(); $ts1=$timestamp->getTimestamp(); $co1=$compared->getTimestamp(); $diff=$ts1-$co1; if($diff<0 ) // Diff is always <0, so always start from index 0 { $i++; } if($diff<-1*60 ) //within one minute { $i++; } if($diff<-10*60) // within ten minues { $i++; } if($diff<-60*60) { $i++; } if($diff<-16*60*60) { $i++; } if($diff<-24*60*60) { $i++; } if($diff<-3*24*60*60) { $i++; } if($diff<-10*24*60*60) { $i++; } return $TSS[$i]; }
Код будет находиться в tssFilter
. Он принимает объект DateTime
чтобы программа могла определить, какая строка в $TSS
должна быть возвращена, основываясь на далекой timestamp
с этого момента.
Это оно! Запустите тест, и все должно пройти!
Интегрировать его в Symfony
tssFilter
по-прежнему изолирован от платформы Symfony. Чтобы использовать его в нашем шаблоне, нам нужно зарегистрировать его в файле services.yml
:
services: app.twig_extension: class: AppBundle\Twig\AppExtension tags: - { name: twig.extension }
Мы должны предоставить полное имя класса фильтра: AppBundle\Twig\AppExtension
.
Наконец, мы можем использовать это так в нашем шаблоне Twig:
{{post.author|capitalize}} posted "{{post.title|capitalize}}" (posted {{post.creation|tss}})
Имя фильтра ( tss
) является производным от имени функции tssFilter()
файла src/AppBundle/Twig/AppExtension.php
и, как и для других компонентов Symfony, «Фильтр» удаляется.
Завершение
В этом быстром уроке мы рассмотрели несколько вещей:
- Фильтры Twig и почему их лучше использовать, чем чистые вызовы PHP.
- Как создать собственный фильтр TDD с помощью PHPUnit.
- Как интегрировать фильтр в платформу Symfony.
Оставьте свои комментарии и мысли ниже и поделитесь своими достижениями!