Представления (Views) и шаблоны

Принцип разделения

Пользовательский интерфейс, такой как HTML-страница, не должен зависеть от базового кода PHP, связанного с маршрутизацией и бизнес-логикой. Это фундаментально для парадигмы MVC. Базовые теги, такие как <h3>, <p> не должны требовать изменения кода вашего приложения. Таким же образом, превращая простой маршрут , как GET /about к GET /about-us не должны иметь никакого влияния на ваш пользовательский интерфейс и бизнес - логику, (вид и модель в MVC, или представление и метод в RMR).

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

PHP как шаблонизатор

F3 поддерживает PHP как шаблонизатор. Взгляните на этот фрагмент HTML, сохраненный как template.htm:

<p>Hello, <?php echo $name; ?>!</p>

Независимо от того, включены ли короткие теги на вашем сервере или нет, это тоже должно работать:

<p>Hello, <?= $name ?></p>

Следующий код PHP использует View класс для рендеринга шаблона PHP, хранящегося в template.htm:

$f3=require('lib/base.php');
$f3->route('GET /',
    function($f3) {
        $f3->set('name','world');
        $view=new View;
        echo $view->render('template.htm');
        // Previous two lines can be shortened to:
        // echo View::instance()->render('template.htm');
    }
);
$f3->run();

Язык шаблонов F3

В качестве альтернативы шаблонам PHP вы можете использовать собственный шаблонизатор F3 представленный классом Template. Приведенный выше фрагмент HTML можно переписать как:

<p>Hello, {{ @name }}!</p>

и код, необходимый для просмотра этого шаблона:

$f3=require('lib/base.php');
$f3->route('GET /',
    function($f3) {
        $f3->set('name','world');
        echo \Template::instance()->render('template.htm');
    }
);
$f3->run();

Примечание: рекомендуется получить экземпляр шаблона с помощью \Template::instance(), что гарантирует, что вы всегда получите тот же экземпляр (например, синглтон). Это может быть важно для правильной работы дополнительных плагинов, которые могут изменять глобальный экземпляр в реестре. Тем не менее, вы также можете создать новый экземпляр с помощью new \Template();, но он не будет включать никаких зарегистрированных фильтров, тегов или надстроек событий.

Подобно токенам маршрутизации, используемым для перехвата переменных в URL-адресах (все еще помните GET /brew/@countпример из предыдущего раздела?), токены шаблона F3 начинаются с символа @, за которым следует серия букв и цифр, заключенных в фигурные скобки. Первый символ должен быть буквой. Маркеры шаблона имеют однозначное соответствие с переменными фреймворка. F3 автоматически заменяет токен значением, хранящимся в переменной с тем же именем.

В нашем примере F3 заменяет токен @name в нашем шаблоне значением, которое мы присвоили переменной name. Во время выполнения вывод приведенного выше кода будет:

<p>Hello, world</p>

Беспокоитесь о производительности шаблонов F3? Во время выполнения F3 анализирует и компилирует / преобразует шаблон F3 в код PHP при первом отображении через $template→render(). Затем структура использует этот скомпилированный код во всех последующих вызовах. Следовательно, производительность должна быть такой же, как у шаблонов PHP, если не лучше из-за оптимизации кода, выполняемой компилятором шаблона, когда задействованы более сложные шаблоны.

Независимо от того, используете ли вы механизм шаблонов PHP или собственный F3, рендеринг шаблонов может быть значительно быстрее, если на вашем сервере доступны APC, WinCache или XCache, которые поддерживаются фреймворком.

Как упоминалось ранее, переменные фреймворка могут содержать любой тип данных PHP. Однако использование нескалярных типов данных в шаблонах F3 может привести к странным результатам, если вы не будете осторожны. Выражения в фигурных скобках всегда будут вычисляться и преобразовываться в строку. Вы должны ограничить переменные пользовательский интерфейс для простых типов данных: string, integer, boolean или float.

Fat-Free распознает массивы, и вы можете использовать их в своих шаблонах. У вас может быть что-то вроде:

<p>{{ @buddy[0] }}, {{ @buddy[1] }}, and {{ @buddy[2] }}</p>

Заполните массив @buddy в своем PHP-коде перед обслуживанием шаблона:

$f3->set('buddy',array('Tom','Dick','Harry'));

Однако, если вы просто вставите

{{ @buddy }} 

в свой шаблон, PHP сгенерирует уведомление Array to string conversion во время выполнения.

F3 позволяет вставлять выражения в шаблоны. Эти выражения могут принимать различные формы, например арифметические вычисления, логические выражения, константы PHP и т.д. Вот несколько примеров:

{{ 2*(@page-1) }}
{{ (int)765.29+1.2e3 }}
<option value="F" {{ @active?'selected="selected"':'' }}>Female</option>
{{ var_dump(@xyz) }}
<p>That is {{ preg_match('/Yes/i',@response)?'correct':'wrong' }}!</p>
{{ @obj->property }}

Дополнительное примечание о выражениях массива: обратите внимание, что @foo.@bar это конкатенация строк ( $foo.$bar), а @foo.bar переводится в $foo['bar'].

Переменные F3 также могут содержать анонимные функции:

$f3->set('func',
    function($a,$b) {
        return $a.', '.$b;
    }
);

Механизм шаблонов F3 будет интерпретировать токен, как ожидалось, если вы укажете следующее выражение:

{{ @func('hello','world') }}

Примечание. Если у вас есть ошибка НЕОПРЕДЕЛЕННАЯ ПЕРЕМЕННАЯ или НЕОПРЕДЕЛЕННЫЙ ИНДЕКС во время рендеринга ваших шаблонов F3, это означает, что переменная или индекс массива не был ранее объявлен в вашей функции, которая вызывает действие шаблона render (). Два решения: Предпочтительно: определите все переменные в вашем коде, даже если NULL; $ f3→ set ('myVar', NULL) $ f3→ set ('myArray.myIndex', 'Первый элемент') Быстро, но не красиво: используйте дополнительный символ AT @ перед токеном;

{{@@ myArray.myIndex}}

с целью подавления отображения сообщений об ошибке

Шаблоны в шаблонах

Простая подстановка переменных - это то, что есть во всех шаблонизаторах. У Fat-Free есть еще кое-что:

<include href="header.htm" />

В Директива вставит содержимое шаблона header.htm в то место, где она указана.

<include href="{{ @content }}" />
// OR
<include href="{{ 'templates/layout/'.@content }}" />
 
// WRONG
<include href="templates/layout/{{ @content }}" />

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

// switch content to your blog sub-template
$f3->set('content','blog.htm');
// in another route, switch content to the wiki sub-template
$f3->set('content','wiki.htm');

Подшаблон может, в свою очередь, содержать любое количество директив. F3 допускает неограниченное количество вложенных шаблонов.

Вы можете указать имена файлов с расширениями файлов, отличными от .htm или .html, хотя их и легче предварительно просмотреть в веб-браузере на этапе разработки и отладки. Механизм шаблонов не ограничивается визуализацией файлов HTML. Фактически, вы можете использовать механизм шаблонов для рендеринга других типов файлов.

Директива <include> имеет необязательный атрибут if, чтобы указать условие, которое должно быть выполнено для суб-шаблона при вставке:

<include if="{{ count(@items) >= 2 }}" href="items.htm" />

Текущий набор данных передается в подшаблон. Однако вы можете передавать новые переменные или перезаписывать существующие переменные с помощью атрибута with:

<!-- pass $a=2 to sub.htm -->
<include href="sub.htm" with="a=2" />
 
<!-- pass $b='something' and $c='something quoted' to sub.htm -->
<include href="sub.htm" with="b=something,c='something quoted'" />
 
<!-- pass uppercased value of $d to sub.htm -->
<set d="abc" />
<include href="sub.htm" with="d={{strtoupper($d)}}" /> // $d='ABC'
{{@d}} // $d='abc'

Примечание: в настоящее время невозможно использовать в качестве переменной в with что-ли кроме строки, поскольку все атрибуты анализируются как строка.

Исключение сегментов

В ходе написания / отладки программ на базе F3 и разработки шаблонов могут быть случаи, когда отключение отображения блока HTML может оказаться удобным. Для этого можно использовать директиву <exclude>:

<exclude>
    <p>A chunk of HTML we don't want displayed at the moment</p>
</exclude>

Это похоже на <!– comment –>тег комментария HTML, но <exclude> делает блок HTML полностью невидимым после визуализации шаблона.

Вот еще один способ исключения содержимого шаблона или добавления комментариев:

{* <p>A chunk of HTML we don't want displayed at the moment</p> *}

Условные сегменты

Еще одна полезная функция шаблона - это директива <check>. Она позволяет встроить фрагмент HTML в зависимости от оценки определенного условия. Вот несколько примеров:

<check if="{{ @page=='Home' }}">
    <false><span>Inserted if condition is false</span></false>
</check>
<check if="{{ @gender=='M' }}">
    <true>
        <div>Appears when condition is true</div>
    </true>
    <false>
        <div>Appears when condition is false</div>
    </false>
</check>

Вы можете иметь столько вложенных директив <check>, сколько вам нужно.

Выражение F3 внутри атрибута if, которое приравнивается к NULL пустой строке, логическому значению FALSE, пустому массиву или нулю, вызывает автоматически <false>. Если в вашем шаблоне нет <false> блока, открывающие и закрывающие теги <true> не обязательны:

<check if="{{ @loggedin }}">
    <p>HTML chunk to be included if condition is true</p>
</check>

Повторяющиеся сегменты

Fat-Free также может обрабатывать повторяющиеся блоки HTML:

<repeat group="{{ @fruits }}" value="{{ @fruit }}">
    <p>{{ trim(@fruit) }}</p>
</repeat>

Атрибут group @fruits внутри директивы <repeat> должен быть массивом и должен быть объявлен в вашем PHP коде:

$f3->set('fruits',array('apple','orange ',' banana'));

Результатом вышеупомянутого фрагмента шаблона HTML и соответствующего кода PHP становится:

<p>apple</p>
<p>orange</p>
<p>banana</p>

Фреймворк позволяет размещать <repeat> блоки без ограничений :

<repeat group="{{ @div }}" key="{{ @ikey }}" value="{{ @idiv }}">
    <div>
        <p><span><b>{{ @ikey }}</b></span></p>
        <p>
        <repeat group="{{ @idiv }}" value="{{ @ispan }}">
            <span>{{ @ispan }}</span>
        </repeat>
        </p>
    </div>
</repeat>

Напишите код:

$f3->set('div',
    array(
        'coffee'=>array('arabica','barako','liberica','kopiluwak'),
        'tea'=>array('darjeeling','pekoe','samovar')
    )
);

В результате вы получите следующий HTML-фрагмент:

<div>
    <p><span><b>coffee</b></span></p>
    <p>
        <span>arabica</span>
        <span>barako</span>
        <span>liberica</span>
        <span>kopiluwak</span>
    <p>
</div>
<div>
    <p><span><b>tea</b></span></p>
    <p>
        <span>darjeeling</span>
        <span>pekoe</span>
        <span>samovar</span>
    </p>
</div>

В шаблонной директиве <repeat> атрибут value возвращает значение текущего элемента в итерации. Если вам нужно получить ключ массива текущего элемента, используйте key вместо value. Атрибут key является необязательным.

<repeat> также имеет необязательный атрибут счетчика, который можно использовать следующим образом:

<repeat group="{{ @fruits }}" value="{{ @fruit }}" counter="{{ @ctr }}">
    <p class="{{ @ctr%2?'odd':'even' }}">{{ trim(@fruit) }}</p>
</repeat>

Внутренне механизм шаблонов F3 записывает количество итераций цикла и сохраняет это значение в переменной / токене @ctr, который используется в нашем примере для определения нечетной / четной записи массива.

Встраивание Javascript и CSS

Если вам нужно вставить токены F3 в раздел <script> или <style>вашего шаблона, фреймворк все равно заменит их обычным способом:

<script type="text/javascript">
    function notify() {
        alert('You are logged in as: {{ @userID }}');
    }
</script>

Встраивание директив шаблона в теги <script> или <style> не требует специальной обработки:

<script type="text/javascript">
	var discounts=[];
    <repeat group="{{ @rates }}" value="{{ @rate }}">
        // whatever you want to repeat in Javascript, e.g.
        discounts.push("{{ @rate }}");
    </repeat>
</script>

Пути файлов

Пример относительных путей файлов веб-страницы:

<link href="ui/css/base.css" type="text/css" rel="stylesheet" />
<script src="ui/js/base.css"></script>
<a href="category-abc/article-xyz">read more</a>
<img src="ui/img/img1.jpg" alt="Image 1" />

Эти пути могут работать на вашей индексной странице GET /, но вызовут проблемы при переходе GET /@category/@article или запуске приложения из подкаталога в общедоступном корне вашего веб-пространства (т.е. http://domain.com/my-F3-App/). Чтобы решить эту проблему, вы должны либо добавить все свои пути к общедоступному базовому пути следующим образом:

<link href="{{@BASE}}/ui/css/base.css" type="text/css" rel="stylesheet" />
<script src="{{@BASE}}/ui/js/base.css"></script>
<a href="{{@BASE}}/category-abc/article-xyz">read more</a>
<img src="{{@BASE}}/ui/img/img1.jpg" alt="Image 1" />

Или используйте базовый тег HTML, чтобы указать базовый путь по умолчанию для относительных ссылок на уровне документа:

<base href="{{@SCHEME.'://'.@HOST.@BASE.'/'}}">

Есть некоторые побочные эффекты при использовании <base> тега в вашем документе: необходимо добавить префикс ко всем ссылкам привязки страниц

<a href="{{@PATH}}#top">go to top</a>

Кодировка документа

Fat-Free по умолчанию использует набор символов UTF-8. Вы можете переопределить кодировку, установив системную переменную ENCODING следующим образом:

$f3->set('ENCODING','ISO-8859-1');

После того, как вы проинформируете F3 о желаемом наборе символов, он будет использовать его для всех шаблонов HTML и XML, пока кодировка не будет изменена снова.

Все типы шаблонов

Как упоминалось ранее в этом разделе, структура не ограничивается шаблонами HTML. Вы также можете обрабатывать шаблоны XML. Механика во многом схожа. У вас еще есть токены

 {{ @variable }}и {{ expression }}

, и директивы <repeat>, <check>, <include>, и <exclude>. Просто скажите F3, что вы передаете XML-файл вместо HTML:

echo Template::instance()->render('template.xml','application/xml');

Второй аргумент представляет тип MIME визуализируемого документа.

Компонент View MVC охватывает все, что не попадает под Модель и Контроллер, что означает, что ваш интерфейс может и должен включать все виды пользовательских интерфейсов, таких как RSS, электронная почта, RDF, FOAF, текстовые файлы и т.д. Пример ниже показано, как отделить вывод электронной почты от бизнес-логики приложения:

MIME-Version: 1.0
Content-type: text/html; charset={{ @ENCODING }}
From: {{ @from }}
To: {{ @to }}
Subject: {{ @subject }}
 
<p>Welcome, and thanks for joining {{ @site }}!</p>

Сохраните указанный выше шаблон электронного письма как welcome.txt. Соответствующий код F3 будет:

$f3->set('from','<no-reply@mysite.com>');
$f3->set('to','<slasher@throats.com>');
$f3->set('subject','Welcome');
ini_set('sendmail_from',$f3->get('from'));
mail(
    $f3->get('to'),
    $f3->get('subject'),
    Template::instance()->render('email.txt','text/html')
);

Совет: замените функцию SMTP mail () на imap_mail (), если ваш сценарий взаимодействует с сервером IMAP.

Конечно, если у вас есть группа получателей электронной почты, вы должны использовать базу данных для заполнения токенов firstName, lastName и электронной почты.

Вот альтернативное решение с использованием подключаемого модуля SMTP F3:

$mail=new SMTP('smtp.gmail.com',465,'SSL','account@gmail.com','secret');
$mail->set('from','<no-reply@mysite.com>');
$mail->set('to','"Slasher" <slasher@throats.com>');
$mail->set('subject','Welcome');
$mail->send(Template::instance()->render('email.txt'));

Мультиязычность

F3 поддерживает мультиязычность прямо из коробки.

Сначала создайте файл словаря со следующей структурой (по одному файлу на каждый язык):

<?php
return array(
    'love'=>'I love F3',
    'today'=>'Today is {0,date}',
    'pi'=>'{0,number}',
    'money'=>'Amount remaining: {0,number,currency}'
);

Сохраните его как dict/en.php. Создадим еще один словарь, на этот раз немецкий. Сохраните файл как dict/de.php:

<?php
return array(
    'love'=>'Ich liebe F3',
    'today'=>'Heute ist {0,date}',
    'money'=>'Restbetrag: {0,number,currency}'
);

Словари - это не что иное, как пары ключ-значение. F3 автоматически создает экземпляры переменных F3 на основе ключей в языковых файлах. Таким образом, эти переменные легко встроить в ваши шаблоны в качестве токенов.

Примечание: пары ключ-значение словаря после подключения становятся переменными F3. Убедитесь, что ключи не конфликтуют с какой-либо переменной фреймворка, созданной через `$ f3→ set ()`, `$ f3→ mset ()` или `$ f3→ config ()`. Кроме того, вы также можете использовать переменную PREFIX, чтобы установить общий ключ, который предшествует каждому языку ключ.

Пример использования нескольких языков:

<h1>{{ @love }}</h1>
<p>
    {{ @today,time() | format }}.<br />
    {{ @money,365.25 | format }}<br />
    {{ @pi, 3.1415 | format }}
</p>

И более длинная версия, которая использует PHP в качестве механизма шаблонов:

<?php $f3=Base::instance(); ?>
<h1><?php echo $f3->get('love'); ?></h1>
<p>
    <?php echo $f3->get('today',time()); ?>.<br />
    <?php echo $f3->get('money',365.25); ?><br />
    <?php echo $f3->get('pi', 3.1415); ?>
</p>

Далее говорим F3 искать словари в dict/папке:

$f3->set('LOCALES','dict/');

Но как фреймворк определяет, какой язык использовать? F3 обнаружит это автоматически, сначала просмотрев заголовки HTTP-запроса, в частности Accept-Language заголовок, отправленный браузером.

Чтобы переопределить язык, вы можете активировать F3 для использования языка, указанного пользователем или приложением:

$f3->set('LANGUAGE','de');

Основной язык вашего сайта

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

Если основной язык вашего веб-сайта не английский, и вы не перевели все строки на другие языки, вы должны указать F3 использовать справочный словарь вашего родного языка в качестве запасного. Вы легко можете это сделать благодаря FALLBACK переменной в hive:

$f3->set('FALLBACK','it');  //Italiano as default fallback language 

Языковые варианты

Вы также можете создать файлы словарей для языковых вариантов, зависящих от локали, например en-US, es-AR и т.е. В этом случае F3 сначала будет использовать языковой вариант, например es-AR. Если есть ключи, которых нет в вариантном словаре, F3 будет искать ключ в основном языке, например es, и, если он все еще не найден, использовать определенный файл резервного языка.

Вы заметили странный 'Today is {0,date}' код в нашем предыдущем примере? Многоязычные возможности F3 зависят от правил форматирования строк / сообщений проекта ICU. Платформа использует собственное подмножество реализации форматирования строк ICU. Нет необходимости активировать расширение PHP intl на сервере.

F3 также может загружать файлы в формате .ini как словари:

love = I love F3
today = Today is {0,date}
pi = {0,number}
money = Amount remaining: {0,number,currency}
multiline = Its also possible to have language keys \
            spread over multiple lines
 
[module.user.validation]
name.required = Please enter your name.
mail.invalid = This mail address is not valid.

Сохраните его как dict/en.in iтак, чтобы фреймворк мог загрузить его автоматически.

Переменная LANGUAGE принимает тот же тип строки, что и заголовок HTTP Accept-Language, который представляет собой список двухбуквенных языковых кодов, разделенных запятыми, за которым, по желанию, следует символ и двухбуквенный код страны.

Примечание: даже если в локали POSIX в качестве разделителя используется символ подчеркивания (es_BR.UTF-8), вы должны определить переменную LANGUAGE с дефисом (es-BR). Файлы словарей подчиняются тому же правилу (es-BR.php / es-BR.ini).

Фильтрация данных

По умолчанию и обработчик представления, и механизм шаблонов экранируют все отображаемые переменные, т.е. преобразуются в объекты HTML, чтобы защитить вас от возможных атак XSS и внедрения кода. С другой стороны, если вы хотите передать действительные фрагменты HTML из кода приложения в шаблон, напишите следующее:

$f3->set('ESCAPE',FALSE);

Это может иметь нежелательные эффекты. Возможно, вы не хотите, чтобы все переменные проходили без экранирования. Fat-Free позволяет отменять экранирование переменных по отдельности. Для шаблонов F3 используйте фильтр raw:

{{ @html_content | raw }}

В случае шаблонов PHP используйте необработанный метод напрямую:

<?php echo $this->raw($html_content); ?>

В дополнение к автоматическому экранированию переменных F3, фреймворк также дает вам свободу действий при очистке пользовательского ввода из HTML-форм:

$f3->scrub($_GET,'p; br; span; div; a');

Эта команда удалит все теги (кроме указанных во втором аргументе) и небезопасные символы из указанной переменной. Если переменная содержит массив, каждый элемент массива подвергается рекурсивной фильтрации. Если в качестве второго аргумента передается звездочка (*), $f3→scrub() все теги HTML оставит нетронутыми и просто удалит небезопасные управляющие символы.

Расширенные фильты и пользовательские теги

С помощью механизма шаблонов F3 вы также можете настраивать собственные фильтры выражений, например,

{{ @desc,100 | crop }} 

а также комбинировать их, как

{{ @desc,100 | crop,raw }}

. Поэтому вам просто нужно зарегистрировать новый фильтр с помощью метода фильтрации. Для собственных обработчиков тегов / элементов html, которые могут отображать все, что вы хотите, ознакомьтесь с методом template→extend . Более подробное описание настраиваемых фильтров и того, как работает система шаблонов в целом, можно найти в расширенной части руководства пользователя по шаблонам.

Печать/экспорт