Содержание


Ajax и Smarty

Часть 1. Разрабатываем Ajax-приложения с помощью Smarty

Создаем Ajax-приложения с помощью PHP, обработчика шаблонов Smarty и инфраструктуры jQuery

Comments

Серия контента:

Этот контент является частью # из серии # статей: Ajax и Smarty

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Ajax и Smarty

Следите за выходом новых статей этой серии.

В первой статье этой серии мы узнаем, как использовать шаблоны Smarty для генерации Ajax-ответов в форматах JSON, XML и HTML. Эти приемы позволят вам сосредоточиться на написании PHP-кода логики приложения, не зависящей от формата данных, используемого при взаимодействии между клиентом и сервером посредством Ajax.

Также вы узнаете, как сделать форму, имеющую две версии, одна из которых позволяет пользователю заполнять данными поля ввода, а другая использует скрытые поля и представляет данные в недоступном для редактирования виде. Нажимая кнопку на форме, пользователь может переключаться между двумя ее версиями, отправляя данные на сервер в Ajax-запросе и извлекая HTML-содержимое, необходимое для обновления страницы. Более того, эта форма будет работать и при выключенной в Web-браузере поддержке JavaScript.

В последнем разделе статьи приведены инструкции по конфигурированию Smarty, а также пример приложения. Процесс конфигурирования немного усложняется, если на вашем сервере или рабочей станции включен SELinux. Дополнительные команды, которые необходимо вводить, являются небольшой ценой за расширенные средства безопасности, предоставляемые SELinux. Эта информация будет полезна для любого Web-приложения, которое должно изменять свои публично доступные файлы, например, для систем управления контентом или Web-сайтов, позволяющих пользователям загружать на них файлы. Неважно, используете ли вы Smarty, популярную CMS или собственную систему, вы столкнетесь с одинаковыми проблемами конфигурирования SELinux, когда ваш код будет пытаться изменить Web-файлы. В этой статье мы покажем, как их решать, используя Linux-команды restorecon, chcon, и setsebool.

Генерируем Ajax-ответы с помощью Smarty

В этом разделе мы узнаем, как создавать шаблоны Smarty, генерирующие ответы на Ajax-запросы. Ответы можно генерировать в любом распространенном формате, например JSON, XML или HTML. Синтаксис Smarty был разработан в первую очередь для HTML, поэтому он также очень хорошо подходит для XML. А вот создавать JSON-ответы с помощью Smarty немного сложнее, так как в конструкциях запросов используются символы { и }, которые при использовании в JSON приходится экранировать. Однако мы увидим, что для исключения синтаксических конфликтов можно поменять используемые Smarty разделители. Также мы узнаем, как создавать пользовательские модификаторы и функции, которые можно зарегистрировать в инфраструктуре Smarty и использовать в своих шаблонах.

Генерируем XML-документы с помощью Smarty

Начнем с простого примера (показанного в листинге 1), генерирующего XML-ответ, который может быть использован Ajax-клиентом. Во-первых, PHP-код должен задать тип содержимого (заголовок Content-Type) и заголовки, запрещающие кэширование, которые гарантируют, что Web-браузер не будет кэшировать Ajax-ответы. На случай, если вы не работали со Smarty раньше, приведем краткое объяснение того, что делается в файле demo_xml.php. С помощью инструкции require в нем подключается файл Smarty, создается новый экземпляр этого класса, задаются флаги компиляции и отладки, с помощью метода assign() создаются две переменные с именами root_attr и elem_data, а затем вызывается метод display(), который генерирует на основе шаблона demo_xml.tpl XML-ответ.

Листинг 1. Пример demo_xml.php
<?php
header("Content-Type: text/xml");
header("Cache-Control: no-cache");
header("Pragma: no-cache");

require 'libs/Smarty.class.php';

$smarty = new Smarty;

$smarty->compile_check = true;
$smarty->debugging = false;
$smarty->force_compile = 1;

$smarty->assign("root_attr", "< abc & def >");
$smarty->assign('elem_data', array("111", "222", "333"));

$smarty->display('demo_xml.tpl');

?>

В шаблоне demo_xml.tpl (см. листинг 2) генерируется элемент <root>, имеющий атрибут, значение которого извлекается из переменной root_attr, созданной в файле demo_xml.php. С помощью модификатора Smarty зарезервированные символы <, >, ", ' и & заменяются на &lt;, &gt;, &quot;, &apos;, и &amp; соответственно. Внутри элемента root шаблон с помощью функции Smarty {section} обходит элементы массива elem_data - второй переменной, назначенной в файле demo_xml.php. Для каждого элемента массива шаблон demo_xml.tpl генерирует XML-элемент, содержащий извлеченное из массива значение.

Листинг 2. Шаблон demo_xml.tpl
<root attr="{$root_attr|escape}">
    {section name="d" loop=$elem_data}
        <elem>{$elem_data[d]|escape}</elem>
    {/section}
</root>

В листинге 3 представлен XML-код, сгенерированный файлом demo_xml.php и шаблоном demo_xml.tpl.

Листинг 3. Полученный XML
<root attr="&lt; abc &amp; def &gt;">
            <elem>111</elem>
            <elem>222</elem>
            <elem>333</elem>
</root>

Конструируем JSON-ответ с помощью Smarty

В файле demo_json.php (показанном в листинге 4) задаются заголовки, запрещающие кэширование, а затем, как и в примере из листинга 3, создается и конфигурируется объект Smarty. Помимо этого в нем определяются функции json_modifier() и json_function(), в которых вызывается функция PHP json_encode(). Эти две функции регистрируются в Smarty, чтобы их можно было использовать в шаблонах; далее в этом разделе мы увидим, как они используются. После этого в файле demo_json.php создается несколько переменных Smarty различных типов: строка, число, булева переменная и массив. Затем на основе шаблона demo_json.tpl генерируется JSON-ответ.

Листинг 4. Пример demo_json.php
<?php
header("Content-Type: application/json");
header("Cache-Control: no-cache");
header("Pragma: no-cache");

require 'libs/Smarty.class.php';

$smarty = new Smarty;

$smarty->compile_check = true;
$smarty->debugging = false;
$smarty->force_compile = 1;

function json_modifier($value) {
    return json_encode($value);
}

function json_function($params, &$smarty) {
    return json_encode($params);
}

$smarty->register_modifier('json', 'json_modifier');
$smarty->register_function('json', 'json_function');

$smarty->assign('str', "a\"b\"c");
$smarty->assign('num', 123);
$smarty->assign('bool', false);
$smarty->assign('arr', array(1,2,3));

$smarty->display('demo_json.tpl');

?>

Вместо регистрации в Smarty расширений (таких как модификаторы или функции), можно следовать специальным соглашениям именования, описанным в документации Smarty (смотри Ресурсы). Если затем поместить свой код в директории расширений, то ваши модификаторы и функции Smarty можно будет использовать на любой Web-странице приложения.

Поскольку символы { и } используются в синтаксисе как JSON, так и Smarty, для генерации этих символов JSON-ответа необходимо использовать в шаблонах Smarty разделители {ldelim} и {rdelim}. Также можно помещать символы { и } между {literal} и {/literal}. Как мы увидим в другом примере, этого неудобства можно избежать, изменив используемые в Smarty разделители.

В шаблоне demo_json.tpl (см. листинг 5) с помощью модификатора json кодируются значения четырех переменных, заданных в файле demo_json.php. Это может быть полезно, например, для экранирования кавычек и других специальных символов, таких как табуляция и символ новой строки. SSmarty вызывает функцию json_modifier() из файла demo_json.php каждый раз, когда в шаблоне используется |json. При передаче в функцию json_modifier() массива перед модификатором json надо указать символ @. Без символа @ функция-модификатор будет вызываться для каждого элемента массива по отдельности.

Конструкция {json ... } в шаблоне demo_json.tpl транслируется в вызов функции json_function() из файла demo_json.php. Функция получает из шаблона атрибуты в массиве с именем params, который передается в функцию json_encode(), возвращающую JSON-представление ассоциативного массива PHP.

Листинг 5. Шаблон demo_json.tpl
{ldelim}
	s: {$str|json},
	n: {$num|json},
	b: {$bool|json},
	a: {$arr|@json},
	o: {json os=$str on=$num ob=$bool oa=$arr},
	z: {literal}{ x: 1, y: 2 }{/literal}
{rdelim}

В листинге 6 приведены данные в формате JSON, сгенерированные файлом demo_json.php на основе шаблона demo_json.tpl.

Листинг 6. Ответ в формате JSON
{
	s: "a\"b\"c",
	n: 123,
	b: false,
	a: [1,2,3],
	o: {"os":"a\"b\"c","on":123,"ob":false,"oa":[1,2,3]},
	z: { x: 1, y: 2 }
}

Чтобы не использовать в шаблонах Smarty конструкции {ldelim} и {rdelim}, можно изменить используемые Smarty разделители, как показано в листинге 7.

Листинг 7. Изменяем разделитель Smarty
$smarty->left_delimiter = '<%';
$smarty->right_delimiter = '%>';

Шаблон из листинга 8 генерирует JSON-ответ, используя в качестве разделителей в синтаксисе Smarty конструкции <% и %>.

Листинг 8. Шаблон demo_json2.tpl
{
	s: <% $str|json %>,
	n: <% $num|json %>,
	b: <% $bool|json %>,
	a: <% $arr|@json %>,
	o: <% json os=$str on=$num ob=$bool oa=$arr %>,
	z: {  x: 1, y: 2 }
}

Создаем с помощью Smarty Ajax-страницу

В этом разделе мы покажем пример использования Smarty для генерации HTML-содержимого ответов на Ajax-запросы. Помимо этого, на Web-странице имеется HTML-форма, данные которой передаются на сервер вместе с Ajax-запросом с помощью инфраструктуры jQuery. Если в Web-браузере выключен JavaScript, форма все равно будет исправно работать и Smarty сможет по-прежнему генерировать на сервере содержимое ответов.

Используем Smarty и HTML-формы

Шаблон demo_form.tpl (см. листинг 9) содержит HTML-форму, поля которой могут быть редактируемыми или нет в зависимости от значения переменной edit_mode. Как мы увидим далее в этом разделе, эта переменная задается в PHP-коде, вызывающем шаблон. Значение edit_mode также хранится в скрытом поле формы.

Листинг 9. HTML-форма шаблона demo_form.tpl
<form method="POST" name="demo_form">

<input type="hidden" name="edit_mode" 
    value="{if $edit_mode}true{else}false{/if}">

<table border="0" cellpadding="5" cellspacing="0">
    ...
</table>

</form>

В листинге 10 показано первое поле формы, являющееся полем ввода, если edit_mode равен true, или скрытым полем, если edit_mode равен false. В последнем случае выводится недоступное для редактирования значение {$smarty.post.demo_text|escape}. Когда пользователь отправляет данные редактируемой формы, параметр demo_text содержит введенную пользователем информацию. Когда форма недоступна для редактирования, этот параметр тоже доступен благодаря скрытому полю. Поэтому значение параметра POST-запроса $smarty.post.demo_text можно получить независимо от того, доступна ли форма для редактирования.

Листинг 10. Текстовое поле в шаблоне demo_form.tpl
    <tr>
        <td>Demo Text:</td>
        <td>
            {if $edit_mode}
                <input type="text" name="demo_text" size="20"
                    value="{$smarty.post.demo_text|escape}">
            {else}
                <input type="hidden" name="demo_text" 
                    value="{$smarty.post.demo_text|escape}">
                {$smarty.post.demo_text|escape}
            {/if}
        </td>
    </tr>

Следующее поле ввода на форме – это флажок (checkbox, см. листинг 11). В доступной для редактирования версии формы элемент input имеет атрибут checked только при наличии параметра demo_checkbox. Аналогично, недоступная для редактирования версия формы содержит скрытый элемент, только если в отправленных данных формы имеется POST-параметр с именем demo_checkbox.

Листинг 11. Элемент checkbox в шаблоне demo_form.tpl
    <tr>
        <td>Demo Checkbox:</td>
        <td>
            {if $edit_mode}
                <input type="checkbox" name="demo_checkbox" 
                    {if $smarty.post.demo_checkbox}checked{/if}>
            {else}
                {if $smarty.post.demo_checkbox}
                    <input type="hidden" name="demo_checkbox" value="On">
                {/if}
                {if $smarty.post.demo_checkbox}On{else}Off{/if}
            {/if}
        </td>
    </tr>

В следующей строке таблицы формы содержатся три переключателя (см. листинг 12). Переключатель, который должен быть выбран, определяется в коде шаблона посредством сравнения значения каждой кнопки с параметром demo_radio. В недоступной для редактирования форме используется скрытое поле ввода, в котором хранится значение параметра. В недоступной для редактирования версии формы используется скрытое поле ввода, хранящее значение $smarty.post.demo_radio, это же значение выводится пользователю.

Листинг 12. Переключатели в шаблоне demo_form.tpl
    <tr>
        <td>Demo Radio:</td>
        <td>
            {if $edit_mode}
                <input type="radio" name="demo_radio" value="1"
                    {if $smarty.post.demo_radio == '1'}checked{/if}>1
                <input type="radio" name="demo_radio" value="2"
                    {if $smarty.post.demo_radio == '2'}checked{/if}>2
                <input type="radio" name="demo_radio" value="3"
                    {if $smarty.post.demo_radio == '3'}checked{/if}>3
            {else}
                <input type="hidden" name="demo_radio" 
                    value="{$smarty.post.demo_radio|escape}">
                {$smarty.post.demo_radio|escape}
            {/if}
        </td>
    </tr>

Параметры списка формы генерируются в цикле с помощью инструкции {section}, как показано в листинге 13. Текущее значение индекса цикла назначается переменной шаблона с именем demo_counter, которое сравнивается со значением генерируемого элемента option, чтобы определить, следует ли выбрать данный элемент.

Листинг 13. Список в шаблоне demo_form.tpl
    <tr>
        <td>Demo Select:</td>
        <td>
            {if $edit_mode}
                <select name="demo_select" size="1">
                    {section name="demo_section" start=10 loop=100 step="10"}
                        {assign var="demo_counter"
                            value=$smarty.section.demo_section.index}
                        <option {if $smarty.post.demo_select == $demo_counter}
                                selected{/if} value="{$demo_counter}">
                            {$demo_counter}
                        </option>
                    {/section}
                </select>
            {else}
                <input type="hidden" name="demo_select" 
                    value="{$smarty.post.demo_select|escape}">
                {$smarty.post.demo_select|escape}
            {/if}
        </td>
    </tr>

Надпись на кнопке отправки формы может быть разной (Save или Edit) в зависимости от значения флага edit_mode flag (см. листинг 14). В атрибуте onclick кнопки содержится вызов функции JavaScript с именем submitDemoForm(). Как мы увидим далее в этой статье, эта функция посредством Ajax отправляет данные формы на сервер и возвращает false, чтобы Web-браузер не отправлял те же самые данные еще раз при повторном нажатии на кнопку. Однако, если JavaScript выключен, функция submitDemoForm() не будет вызвана и Web-браузер отправит данные формы на сервер. Поэтому форма будет работать правильно независимо от того, включен ли в браузере JavaScript.

Листинг 14. Кнопка submit в шаблоне demo_form.tpl
    <tr>
        <td>&nbsp;</td>
        <td>
            <button type="submit" onclick="return submitDemoForm()">
                {if $edit_mode}Save{else}Edit{/if}
            </button>
        </td>
    </tr>

Разрабатываем шаблон страницы

В файле demo_page.tpl (см. листинг 15) содержится два элемента <script>: один для jQuery, а другой – для JavaScript-файла нашего приложения. Шаблон формы подключается внутри элемента <div> шаблона страницы с помощью функции Smarty {include}.

Листинг 15. Шаблон demo_page.tpl
<html>
<head>
    <title>Demo</title>
    <script type="text/javascript" 
        src="//ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js">
    </script>
    <script type="text/javascript" src="demo_js.js">
    </script>
</head>
<body>
    <div id="demo_div">
        {include file="demo_form.tpl"}
    </div>
</body>
</html>

Создаем PHP-контроллер для Smarty и Ajax

Файл demo_html.php (показанный в листинге 16) выполняет связующую роль между Ajax и Smarty, обрабатывая Ajax-запросы и используя Smarty для генерации Ajax-ответов на основе шаблона demo_form.tpl, который вызывается только при наличии Ajax-заголовка. Как мы увидим далее, этот заголовок устанавливается в JavaScript-коде. Если Ajax-заголовок отсутствует, это означает, что либо это начальный запрос страницы, сделанный Web-браузером, либо в браузере выключен JavaScript; в таком случае ответ генерируется на основе шаблона demo_page.tpl. После каждого запроса значение флага edit_mode меняется на противоположное, что обеспечивает чередование доступной и недоступной для редактирования версий формы.

Листинг 16. Пример demo_html.php
<?php
header("Cache-Control: no-cache");
header("Pragma: no-cache");

require 'libs/Smarty.class.php';

$smarty = new Smarty;

$smarty->compile_check = true;
$smarty->debugging = false;
$smarty->force_compile = 1;

$edit_mode = @($_REQUEST['edit_mode'] == "true");
$smarty->assign("edit_mode", !$edit_mode);

$ajax_request = @($_SERVER["HTTP_AJAX_REQUEST"] == "true");
$smarty->display($ajax_request ? 'demo_form.tpl' : 'demo_page.tpl');

?>

Используем Ajax для вызова шаблонов Smarty

Функция submitDemoForm() (см. листинг 17) вызывается при нажатии кнопки отправки формы. Она посылает данные формы на сервер средствами jQuery, используя метод POST и URL-адрес, указанный в Web-форме. Данные формы кодируются в строку с помощью API-функции jQuery serialize(). Функция beforeSend(), вызываемая в jQuery перед отправкой Ajax-запроса, используется в этом примере для задания заголовка Ajax-Request, который необходим серверной стороне для идентификации Ajax-запросов. Функция success() вызывается при получении Ajax-ответа. Эта функция обратного вызова вставляет содержимое ответа в элемент <div> Web-страницы.

Листинг 17. Пример demo_js.js
function submitDemoForm() {
    var form = $("form[name=demo_form]");
    $.ajax({
        type: "POST",
        url: form.action ? form.action : document.URL,
        data: $(form).serialize(),
        dataType: "text",
        beforeSend: function(xhr) {
            xhr.setRequestHeader("Ajax-Request", "true");
        },
        success: function(response) {
            $("#demo_div").html(response);
        }
    });
    return false;
}

Настраиваем Smarty с включенным SELinux

Распаковав архив с кодом нашего приложения, вы увидите в нем директорию ajaxsmarty, содержащую PHP-файлы, JavaScript-файл и четыре поддиректории: cache, configs, templates и templates_c. В директории templates содержатся Smarty-шаблоны нашего приложения. Остальные три поддиректории пусты.

Загрузите последнюю стабильную версию Smarty (см. Ресурсы) и разархивируйте ее. (Данный пример приложения тестировался со Smarty версии 2.6.25.) Затем скопируйте поддиректорию libs пакета Smarty в директорию ajaxsmarty, являющуюся основной директорией нашего приложения.

Поместите директорию ajaxsmarty (содержащую как само приложение, так и директорию libs пакета Smarty) в html-директорию сервера Apache. Если вы пользуетесь услугами хостинга какой-либо компании, то, скорее всего, SELinux выключен, так как из-за него служба поддержки получала бы слишком много звонков. Если вы тестируете приложение на своей собственной Linux-системе, есть вероятность, что SELinux включен и, возможно, при запросе PHP-файла из браузера, вы увидите следующую ошибку: "SELinux is preventing the httpd from using potentially mislabeled files." Для ее устранения выполните с полномочиями пользователя root команду, показанную в листинге 18.

Листинг 18. Задаем контекст безопасности (метки) Web-файлов
restorecon -R -v /var/www/html/ajaxsmarty

Теперь вы можете открыть в своем браузере адрес http://localhost/ajaxsmarty/ и увидеть на Web-странице три ссылки. Если нажать на одну из них, вы увидите в Web-браузере следующую ошибку Smarty: "Fatal error: Smarty error: unable to write to $compile_dir '/var/www/html/ajaxsmarty/templates_c'. Be sure $compile_dir is writable by the Web server user. in /var/www/html/ajaxsmarty/libs/Smarty.class.php on line 1113".

Эта ошибка возникает из-за того, что Smarty еще не до конца настроен. Вы должны дать Web-серверу права на запись в директории templates_c и cache. Правильный способ сделать это – поменять владельца директорий, как показано в листинге 19. Обратите внимание, что имя пользователя apache и html-директория сервера на вашем компьютере могут быть другими.

Листинг 19. Изменяем владельца двух директорий, чтобы Smarty мог создавать в них свои файлы
chown apache:apache /var/www/html/ajaxsmarty/templates_c 
chown apache:apache /var/www/html/ajaxsmarty/cache

Если у вас нет доступа с полномочиями пользователя root, вместо использования chown можно изменить права доступа к директориям templates_c и cache. Это можно сделать с помощью FTP-клиента или с помощью chmod с параметром 777. Разрешать запись в эти директории всем пользователям – не лучшая идея, но это может быть единственным подходящим для вас вариантом, если вы не можете использовать chown. Если ваш Web-сервер находится в публичном доступе, вам следует связаться с администратором сервера.

Если на вашем компьютере включен SELinux, возможно, теперь при обращении к странице через браузер вы увидите одну из следующих ошибок: "SELinux prevented httpd reading and writing access to http files." или "SELinux is preventing httpd (httpd_t) write to ./templates_c (public_content_rw_t)". Чтобы их устранить, выполните с правами пользователя root команды, показанные в листинге 20.

Листинг 20. Позволяем Smarty создавать файлы в своих директориях при включенном SELinux
chcon -t public_content_rw_t /var/www/html/ajaxsmarty/templates_c 
chcon -t public_content_rw_t /var/www/html/ajaxsmarty/cache
setsebool -P allow_httpd_anon_write=1

Команду setsebool с параметром allow_httpd_anon_write нужно выполнить только один раз. Она позволяет демону httpd создавать файлы в директориях с меткой public_content_rw_t.

Заключение

В этой статье мы научились создавать шаблоны Smarty, генерирующие ответы в форматах JSON, XML и HTML на Ajax-запросы, узнали, как создать с помощью Smarty форму, работающую даже при выключенной в Web-браузере поддержке JavaScript, и как конфигурировать Smarty на Linux-машинах с включенным SELinux.


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Web-архитектура, Open source
ArticleID=556063
ArticleTitle=Ajax и Smarty: Часть 1. Разрабатываем Ajax-приложения с помощью Smarty
publish-date=10252010