Телефонная книга
Ещё один пример (уже довольно функциональный) – расширение ContactBook
На этот раз мы решили показать, как можно значительную часть работы переложить на плечи Smarty и как нужно выстраивать архитектуру расширения.
1. Smarty-шаблон
Для того, чтобы работать с AJAX в Smarty нужно заключить весь шаблон в такие теги
<div id="divcontactbook"> {startvirtual} {* здесь написаны все остальные фрагменты *} {finishvirtual block="divcontactbook"} </div>
Простейшая форма, отправляемая по AJAX – поиск по телефонной книге
{* форма поиска *} <b>Поиск</b> {ajxformopen id="s" actref="divcontactbook"} <div style="width:300px;"> {* обращаем внимание на то, как заэкранирвоана переменная *} {virtualform id="search" name="text" default="`$search`"} <input type="submit" value="Искать!"> </div> {wackoformclose}
Здесь выводим все контакты. Обратите внимание на то, как сделана проверка на первую и последнюю итерацию цикла, на пустой массив и на то, как сделаны ссылки, переходы по которым будут проходить через AJAX
{foreach from=$contacts item=contact name=contactbook} {* в первой итерации цикла выводим шапку таблицу *} {if $smarty.foreach.contactbook.first} <table width="100%"> <tr> <td width="20%"><b>Имя</b></td> <td width="20%"><b>Фамилия</b></td> <td width="20%"><b>Телефон</b></td> <td width="20%"><b>E-mail</b></td> {if $canedit == "1"} <td></td> <td></td> {/if} </tr> {/if} <tr> <td>{$contact.name}</td> <td>{$contact.sname}</td> <td>{$contact.phone}</td> <td>{mailto address="`$contact.email`"}</td> {if $canedit == "1"} <td>{ajxhref text="<img src='`$rooturl`images/additional/edit.png' />" elements="divcontactbook" params="editid=`$contact.id`"}</td> <td>{ajxhref text="<img src='`$rooturl`images/additional/drop.png' />" elements="divcontactbook" params="delid=`$contact.id`" }</td> {/if} </tr> {* в последней - закрываем таблицу *} {if $smarty.foreach.contactbook.last} </table> {/if} {foreachelse} {* если массив оказался пуст - выводим сообщение *} Ничего не найдено {/foreach}
Ещё одна форма – в ней производится и редактирование, и добавление контактов
{* если имеем право редактировать *} {if $canedit == "1"} <br /><br /> {ajxformopen id="e" actref="divcontactbook"} {* с помощью этого поля определяем, новая это запись или редактирование старой *} {virtualform id="nid" name="hidden" default="`$editcontact.id`" } <table> <tr> <td>Имя </td><td>{virtualform id="editname" name="text" default="`$editcontact.name`" }</td> </tr> <tr> <td>Фамилия</td><td>{virtualform id="editsname" name="text" default="`$editcontact.sname`"}</td> </tr> <tr> <td>Телефон</td><td>{virtualform id="editphone" name="text" default="`$editcontact.phone`"}</td> </tr> <tr> <td>E-mail </td><td>{virtualform id="editemail" name="text" default="`$editcontact.email`"}</td> </tr> </table> <input type="submit" value="Отправить!"> {wackoformclose} {/if}
2. Экшн для вывода Smarty
<?
// для листалки нужно определить к-во контактов на страницу и точка начала
if($max==0) $max=10;
$start = (int)$_REQUEST['start'];
// подключаем собственный класс
$CBServ = $this->srvFactory("ContactBook");
// работа со смарти. сначала создадим смарти-объект
$smarty = $CBServ->newSmarty();
// можем ли мы редактировать и добавлять?
if ($this->HasAccess("editCB")){
$smarty->assign('canedit', "1");
}
// удаляем контакт
if ($id = (int)$_REQUEST['delid']){
$CBServ->DelContactById($id);
}
// редактирование - пока только вернём значения для вставки в поля ввода
if (($id = (int)$_REQUEST['editid'])&&(!$_REQUEST['editname'])){
$smarty->assign('editcontact', $CBServ->GetContactById($id));
}else{
$smarty->assign('editcontact', array(
"id" => "",
"name" => "",
"sname" => "",
"phone" => "",
"email" => "",
)
);
}
// а вот теперь нам прислали контакт. хм... а он новый или старый?
if ($_REQUEST['editname']){
$info['id'] = (int)$_REQUEST['nid'];
$info['name'] = $_REQUEST['editname'];
$info['sname'] = $_REQUEST['editsname'];
$info['phone'] = (int)$_REQUEST['editphone'];
$info['email'] = $_REQUEST['editemail'];
// тут и определим
if ($info['id']){
// раз есть id, значит старый
$CBServ->EditContact($info);
}else{
// а если нет, то новый
$CBServ->AddContact($info);
}
}
// формируем список контактов для отображения.
// тут есть небольшая оптимизация. можно убрать условие и оставить только первую часть,
// то есть всегда искать по пустому запросу
if ($text = $_REQUEST['search']){
$contacts = $CBServ->SearchContacts($text, $max, $start);
$crows = $CBServ->GetContactsCount($text); // для листалки нужно знать, сколько всего результатов
}else{
$contacts = $CBServ->GetAllContacts($max, $start);
$crows = $CBServ->GetContactsCount(); // аналогично
}
// выстроим ссылки для листания
for($i=0;$i*$max<$crows;$i++) {
$link[$i]['start'] = $i*$max; // это будут точки старта
$link[$i]['text'] = (($i+1)*$max>$crows?$crows:($i+1)*$max); // а это - текст ссылки
}
// передадим шаблону значения переменных
$smarty->assign('contacts', $contacts);
$smarty->assign('links', $link);
$smarty->assign('search', $text);
// и выведем результат выполнения шаблона
echo $smarty->fetch('ContactBook.txt');
?>
3. Класс расширения
Пример правильной архитектуры – вся обработка данных и работа с БД происходит в классе расширения. Таким образом, при изменении структуры таблицы нам придётся внести минимум правок. Кроме того, код становится более логичным и понятным за счёт небольших размеров каждого файла – не больше 100 строк каждый.
<?php
class Service_ContactBook extends XcBase {
var $_kernel;
function Service_ContactBook($kernelLink) {
$this->_localname = 'ContactBook';
$this->XcBase($kernelLink);
$this->_kernel = $this->srvMainKernel();
}
function onInstall($params) {
$query = "CREATE TABLE ".$this->_db->config["table_prefix"]."contactbook (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(25),
sname VARCHAR(25),
phone VARCHAR(13),
email VARCHAR(50)
)";
$this->_kernel->Query($query);
return true;
}
function onUninstall($params) {
$this->_kernel->Query("DROP TABLE ".$this->_db->config["table_prefix"]."contactbook");
return true;
}
// возвращает $max контактов по алфавиту, начиная со $start
function GetAllContacts($max, $start="0"){
if (!$start) $start="0";
return $this->_kernel->LoadAll("SELECT * FROM ".$this->_db->config["table_prefix"]."contactbook
ORDER BY `sname` ASC LIMIT ".$start." , ".$max);
}
// запрашиваем контакт по id
function GetContactById($id){
return $this->_kernel->dbGetById("contactbook",$id);
}
// удаляем контакт по id
function DelContactById($id){
$this->_kernel->Query("delete from ".$this->_db->config["table_prefix"]."contactbook
where id = '".$id."'");
}
// добавляем контакт
function AddContact($info){
/*
эта функция - хороший пример использования специальных функций ядра,
занчительно укорачивающих код.
*/
$this->_kernel->insertInto("contactbook",$info);
}
// редактируем контакт. id возьмём из $info
function EditContact($info){
$this->_kernel->Query('UPDATE '.$this->_db->config["table_prefix"].'contactbook SET
'.$this->_kernel->dbConstructSetArray($info).' WHERE `id` = '.$info['id']);
}
// ищем по всем полям и возврщаем ровно одну страницу результатов
function SearchContacts($text, $max, $start=0){
if (!$start) $start="0";
$query = "SELECT * FROM ".$this->_db->config["table_prefix"]."contactbook WHERE
name like '%".quote($text)."%' OR
sname like '%".quote($text)."%' OR
phone like '%".quote($text)."%' OR
email like '%".quote($text)."%' ORDER BY `sname` ASC LIMIT $start , $max";
return $this->_kernel->LoadAll($query);
}
// получим количество результатов выполнения поискового запроса. нужно для листалки
function GetContactsCount($text=""){
$resc = $this->_kernel->LoadSingle('SELECT count(*) FROM '.$this->_db->config["table_prefix"]."contactbook
WHERE
name like '%".quote($text)."%' OR
sname like '%".quote($text)."%' OR
phone like '%".quote($text)."%' OR
email like '%".quote($text)."%'");
return $resc["count(*)"];
}
// обычная функция создания смарти
function NewSmarty(){
$mythis = $this->_kernel;
$sm = $mythis->NewSmarty();
$def = $mythis->getSiteDataPath().'data/'.$this->_localname.'/';
$sm->template_dir = $def.'templates/';
$sm->compile_dir = $def.'templates_c/';
$sm->config_dir = $def.'config/';
$sm->cache_dir = $def.'cache/';
$sm->plugins_dir[] = $def.'plugins/';
return $sm;
}
}
?>
Живой пример
Скачать