« Поставить закладку » « Сделать стартовой »

« Форумы » « Блоги » « Статьи » « Новости » « Файлы » « Realcoding IRC » « Site map » « Поиск »


Главная Главная
Анонсы Анонсы
Форумы Форумы
Каталог Каталог
Поиск Поиск
Опросы Опросы
Книжный магазин Книжный магазин
Реклама на сайте
Публикации Публикации
Партнеры Партнеры
Карта Карта сайта
Рассылки Рассылки
RSS экспорт
Настройки Настройки
О нас пишут О нас пишут
Контакты Контакты
Гостевая книга Гостевая книга







    Популярное
Глава 16. Особенности сетевых приложений.

Часть II. ВСПЛЫВАЮЩИЕ ОКНА

Глава 13. Сборки .NET, установка приложений и COM Interop

Язык описания Web-служб WSDL

Описание функций C (Си) / C++ - ldexp

Схема XML (XML schema)

Как организовать двойную парольную защиту данных в Oracle

Функция AccessResource

Функция GetPrivateProfileInt

Функция GlobalPageUnlock


ПнВтСрЧтПтСбВс
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
    Архив файлов

    Сообщества

    Документация

    Кто на сайте
Вы не зарегистрированы.
Имя:

Пароль:

Запомнить

Регистрация позволит Вам пользоваться дополнительными сервисами.
Сейчас на сайте:
Гостей: 101
Пользователей: 0

Статьи:: Интернет технологии :: PHP :: Введение в Zend Framework. Часть 2


отправить ссылку другу версия для печати  Обсудить на форуме

Введение в Zend Framework. Часть 2



Продолжаем рассказ о Zend Framework. В первой части статьи была описана концепция программной архитектуры MVC, рассмотрена структура типового веб-приложения, базирующегося на Zend Framework и выполнена демонстрационная реализация контроллера и вида на его основе. Во второй части будет раскрыта тема модели и приведен пример взаимодействия приложения с базой данных.


Для печати рекомендуется использовать полную версию статьи в формате PDF: zend-fw-intro.pdf

Автор: Роб Ален, http://akrabat.com
Оригинал: http://akrabat.com/zend-framework-tutori…
Перевод: Александр Мусаев, http://paradigm.ru

* * *

База данных

Теперь, когда управляющая часть нашего приложения и код визуализации разделены, пришло время заняться моделью. Запомните, что модель — базовая часть приложения, которая реализует его основные функции. Следовательно, в нашем случае модель выполняет работу с базой данных. Мы используем класс Zend Framework Zend_Db_Table, предназначенный для поиска, вставки, обновления и удаления записей в таблице базы данных.

Настройка

Для использования Zend_Db_Table, понадобится сообщить ему, к какой базе данных, под каким именем пользователя и с каким паролем он будет обращаться. Принимая во внимание, что эту информацию предпочтительно не забивать в код, воспользуемся конфигурационным файлом для ее хранения.

Zend Framework предоставляет для этой цели класс Zend_Config, обеспечивающий гибкий объектно-ориентированный доступ к конфигурационным файлам в форматах INI и XML. Остановим свой выбор на INI-файле:

zf-tutorial/application/config.ini:
[general]
db.adapter = PDO_MYSQL
db.config.host = localhost
db.config.username = rob
db.config.password = 123456
db.config.dbname = zftest

(Безусловно, вам понадобится использовать свои собственные параметры доступа к БД, а не приведенные в примере.)

Использовать Zend_Config будет очень просто:
$config = new Zend_Config_Ini('config.ini', 'section');

В данном случае, Zend_Config_Ini загружает одну секцию из INI-файла (таким же образом при необходимости можно загрузить любую другую секцию). Возможность именования секций реализована для того, чтобы лишние данные без нужды не загружались. Zend_Config_Ini использует точку в именах параметров в качестве иерархического разделителя, благодаря чему можно группировать родственные параметры. В нашем файле config.ini, параметры host, username, password и dbname будут сгруппированы в объекте $config->db->config.

Мы будем загружать конфигурационный файл из файла начальной загрузки (index.php):

zf-tutorial/index.php:
...
Zend_Loader::loadClass('Zend_Controller_Front');
Zend_Loader::loadClass('Zend_Config_Ini');
Zend_Loader::loadClass('Zend_Registry');

// load configuration
$config = new Zend_Config_Ini('./application/config.ini', 'general');
$registry = Zend_Registry::getInstance();
$registry->set('config', $config);

// setup controller
...

После загрузки необходимых для работы классов (Zend_Config_Ini и Zend_Registry), в объект $config загружается секция конфигурационного файла application/config.ini под именем “general”. Далее объект $config включается в реестр, что обеспечивая доступ к нему из всего приложения.

Замечание: В данном примере нет реальной потребности хранить $config в реестре. Это сделано в качестве примера работы «настоящего» приложения, в конфигурационном файле которого вам, вероятно, придется хранить нечто большее, чем параметры доступа к БД. При использовании реестра, обратите так же внимание, что данные в нем доступны на глобальном уровне, и что при неосторожном использовании это может стать причиной потенциальных конфликтных ситуаций внутри приложения.

Использование Zend_Db_Table

Для того, чтобы использовать класс Zend_Db_Table, нам понадобится передать в него параметра доступа к базе данных, которые были только что загружены. Для этого необходимо создать объект Zend_Db и зарегистрировать его функцией Zend_Db_Table::setDefaultAdapter(). Ниже приведен соответствующий фрагмент кода в файле первичной загрузки:

zf-tutorial/index.php:
...
Zend_Loader::loadClass('Zend_Controller_Front');
Zend_Loader::loadClass('Zend_Config_Ini');
Zend_Loader::loadClass('Zend_Registry');
Zend_Loader::loadClass('Zend_Db');
Zend_Loader::loadClass('Zend_Db_Table');


// load configuration
$config = new Zend_Config_Ini('./application/config.ini', 'general');
$registry = Zend_Registry::getInstance();
$registry->set('config', $config);

// setup database
$db = Zend_Db::factory($config->db->adapter,
$config->db->config->toArray());
Zend_Db_Table::setDefaultAdapter($db);


// setup controller
...

Создание таблицы

Мы будем использовать базу данных MySQL, поэтому SQL запрос на создание таблицы будет выглядеть так:
CREATE TABLE album (
    id int(11) NOT NULL auto_increment,
    artist varchar(100) NOT NULL,
    title varchar(100) NOT NULL,
    PRIMARY KEY (id)
);

Этот запрос можно выполнить через любой MySQL-клиент, например, phpMyAdmin или стандартную консольную утилиту.

Добавление тестовой записи

Добавим несколько тестовых записей в таблицу для проверки функциональности главной страницы, на которой они в последствии должны отображаться.
INSERT INTO album (artist, title)
VALUES
    ('James Morrison', 'Undiscovered'),
    ('Snow Patrol', 'Eyes Open');

Модель

Zend_Db_Table — абстрактный класс, поэтому на его основе необходимо создать специализированный на нашей задаче класс-наследник. Имя нового класса не имеет принципиального значения, но такие классы стоит называть аналогично соответствующим им таблицам БД (это повысит уровень самодокументируемости кода). Таким образом, для нашей таблицы album имя класса будет Album.

Для того, чтобы передать в Zend_Db_Table имя таблицы, которой он будет управлять, необходимо присвоить это значение защищенному свойству класса $_name. Стоит отметить, что в классе Zend_Db_Table по-умолчанию всегда используется ключевое автоинкрементное поле таблицы с именем id. При необходимости значение имени этого поля так же можно менять.

Класс Album будет храниться в директории моделей.

zf-tutorial/application/models/Album.php:
<?php

class Album extends Zend_Db_Table
{
    protected $_name = 'album';
}

Ничего сложного, не так ли? Учитывая, что наши потребности в данном случае совсем невелики, базовой функциональности Zend_Db_Table хватит, чтобы полностью их удовлетворить. Если же в ваших задачах понадобится реализовать более сложные операции взаимодействия с базой данных, то класс модели — это именно то место, куда стоит поместить соответствующий код.

Список альбомов

Теперь, когда мы сконфигурировали базу данных, можно приступить к выполнению основной задачи приложения и отобразить несколько записей. Это должно быть реализовано в классе IndexController. Каждая функция-действие внутри IndexController взаимодействует с таблицей album через класс Album. Поэтому имеет смысл загрузить его при инициализации контролера внутри функции init().

zf-tutorial/application/controllers/IndexController.php:
...
function init()
{
    $this->view->baseUrl = $this->_request->getBaseUrl();
    Zend_Loader::loadClass('Album');
}
...

Замечание: В данном фрагменте кода приведен пример использования метода Zend_Loader::loadClass() для загрузки нестандартных классов. Это срабатывает, т. к. директория моделей (в которой хранится подгружаемый класс Albums) была добавлена нами в include_path.

Создадим список альбомов из базы с помощью indexAction():

zf-tutorial/application/controllers/IndexController.php:
...
function indexAction()
{
    $this->view->title = "My Albums";
    $album = new Album();
    $this->view->albums = $album->fetchAll();
}
...

Функция fetchAll() возвращает объект Zend_Db_Table_Rowset, который позволит нам сформировать список из всех записей в шаблонном файле вида:
zf-tutorial/application/views/scripts/index/index.phtml:
<?php echo $this->render('header.phtml'); ?>
<h1><?php echo $this->escape($this->title); ?></h1>
<p><a href="<?php echo $this->baseUrl; ?>/index/add">Add new album</a></p>
<table>
<tr>
<th>Title</th>
<th>Artist</th>
<th> </th>
</tr>
<?php foreach($this->albums as $album) : ?>
<tr>
<td><?php echo $this->escape($album->title);?></td>
<td><?php echo $this->escape($album->artist);?></td>
<td>
<a href="<?php echo $this->baseUrl; ?>/index/edit/id/<?php
echo $album->id;?>">Edit</a>
<a href="<?php echo $this->baseUrl; ?>/index/delete/id/<?php
echo $album->id;?>">Delete</a>
</td>
</tr>
<?php endforeach; ?>
</table>
<?php echo $this->render('footer.phtml'); ?>

По адресу http://localhost/zf-tutorial/ теперь должен отображаться список из двух наших альбомов.

Добавление нового альбома

Теперь перейдем к функции добавления нового диска в базу. Эта задача состоит из двух частей:
  • отображение формы, через которую пользователь будет вводить данные;
  • добавление принятых из формы данных в базу.
Реализуем перечисленные операции в функции-действии addAction():

zf-tutorial/application/controllers/IndexController.php:
...
function addAction()
{
    $this->view->title = "Add New Album";

    if ($this->_request->isPost()) {
        Zend_Loader::loadClass('Zend_Filter_StripTags');
        $filter = new Zend_Filter_StripTags();
        $artist = $filter->filter($this->_request->getPost('artist'));
        $artist = trim($artist);
        $title = trim($filter->filter($this->_request->getPost('title')));

        if ($artist != '' && $title != '') {
            $data = array(
                'artist' => $artist,
                'title' => $title,
            );
            $album = new Album();
            $album->insert($data);
            $this->_            redirect('/');
            return;
        }
    }

    // set up an "empty" album
    $this->view->album = new stdClass();
    $this->view->album->id = null;
    $this->view->album->artist = '';
    $this->view->album->title = '';

    // additional view fields required by form
    $this->view->action = 'add';
    $this->view->buttonText = 'Add';

}
...

Обратите внимание, как в начале функции было определено, имела ли место пересылка данных их формы. Если данные из формы были переданы, мы извлекаем значения artist и title, и обрабатываем их фильтром HTML тагов Zend_Filter_StripTags. Далее, если строки имеют непустое значение, мы используем класс Album() для добавления новой записи в таблицу БД. После добавления альбома, пользователь переадресуется обратно на главную страницу методом контроллера _redirect().

Последнее, что понадобится сделать, — подготовить HTML-форму для шаблона вида. Забегая вперед, можно отметить, что форма редактирования записей будет выглядеть идентично форме для их добавления. Поэтому мы используем общий шаблонный файл (_form.html), который будет использован в add.phtml и edit.phtml:

Шаблон страницы добавления нового альбома:

zf-tutorial/application/views/scripts/index/add.phtml:
<?php echo $this->render('header.phtml'); ?>
<h1><?php echo $this->escape($this->title); ?></h1>
<?php echo $this->render('index/_form.phtml'); ?>
<?php echo $this->render('footer.phtml'); ?>


zf-tutorial/application/views/scripts/index/_form.phtml:
<form action="<?php echo $this->baseUrl ?>/index/<?php
echo $this->action; ?>" method="post">
<div>
<label for="artist">Artist</label>
<input type="text" name="artist"
value="<?php echo $this->escape(trim($this->album->artist));?>"/>
</div>
<div>
<label for="title">Title</label>
<input type="text" name="title"
value="<?php echo $this->escape($this->album->title);?>"/>
</div>
<div id="formbutton">
<input type="hidden" name="id" value="<?php echo $this->album->id; ?>" />
<input type="submit" name="add"
value="<?php echo $this->escape($this->buttonText); ?>" />
</div>
</form>

Получился достаточно несложный код. Учитывая, что мы предполагаем применять _form.phtml еще и при редактировании записей, используем переменную $this->action, вместо того, чтобы жестко задавать имя необходимого действия. Таким же образом, через переменную задается надпись на кнопке передачи данных из формы.

Редактирование альбома

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

zf-tutorial/application/controllers/IndexController.php:
...
function editAction()
{
    $this->view->title = "Edit Album";
    $album = new Album();

    if ($this->_request->isPost()) {
        Zend_Loader::loadClass('Zend_Filter_StripTags');
        $filter = new Zend_Filter_StripTags();
        $id = (int)$this->_request->getPost('id');
        $artist = $filter->filter($this->_request->getPost('artist'));
        $artist = trim($artist);
        $title = trim($filter->filter($this->_request->getPost('title')));

        if ($id !== false) {
            if ($artist != '' && $title != '') {

                $data = array(
                    'artist' => $artist,
                    'title' => $title,
                );

                $where = 'id = ' . $id;
                $album->update($data, $where);
                $this->_redirect('/');
                return;
            } else {
                $this->view->album = $album->fetchRow('id='.$id);
            }
        }
    } else {
        // album id should be $params['id']
        $id = (int)$this->_request->getParam('id', 0);

        if ($id > 0) {
            $this->view->album = $album->fetchRow('id='.$id);
        }
    }

    // additional view fields required by form
    $this->view->action = 'edit';
    $this->view->buttonText = 'Update';
}
...

Стоит отметить, что в тех случаях, когда не была выполнена передача данных в скрипт из формы, мы можем получить параметр id из свойства params объекта Request, с помощью метода getParam().

Код шаблона приведен ниже:

zf-tutorial/application/views/scripts/index/edit.phtml:
<?php echo $this->render('header.phtml'); ?>
<h1><?php echo $this->escape($this->title); ?></h1>
<?php echo $this->render('index/_form.phtml'); ?>
<?php echo $this->render('footer.phtml'); ?>

Рефакторинг

Безусловно, от вашего внимания не могло ускользнуть, что addAction() и editAction() очень похожи, а шаблоны добавления и редактировании записей вообще идентичны. Логично предположить необходимость рефакторинга кода. Решите эту задачу самостоятельно, в качестве дополнительного практического упражнения.

Удаление альбома

Для того, чтобы завершить разработку приложения, понадобится добавить функцию удаления записей. У нас предусмотрены ссылки для удаления альбомов напротив каждого элемента в списке. Первое приходящее на ум решение — удалять записи сразу при клике по одной из этих ссылок. Но это неправильный подход. Вспомните, что согласно спецификации HTTP, не следует выполнять необратимых действий с помощью метода GET. В таких случаях рекомендуется применять POST. Примером того может быть работа Google Accelerator.

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

zf-tutorial/application/controllers/IndexController.php:
...
function deleteAction()
{
    $this->view->title = "Delete Album";
    $album = new Album();

    if ($this->_request->isPost()) {
        Zend_Loader::loadClass('Zend_Filter_Alpha');
        $filter = new Zend_Filter_Alpha();
        $id = (int)$this->_request->getPost('id');
        $del = $filter->filter($this->_request->getPost('del'));

        if ($del == 'Yes' && $id > 0) {
            $where = 'id = ' . $id;
            $rows_affected = $album->delete($where);
        }
    } else {
        $id = (int)$this->_request->getParam('id');

        if ($id > 0) {
            // only render if we have an id and can find the album.
            $this->view->album = $album->fetchRow('id='.$id);

            if ($this->view->album->id > 0) {
                // render template automatically
                return;
            }
        }
    }

    // redirect back to the album list unless we have rendered the view
    $this->_redirect('/');
}
...

Использован тот же способ определения метода обращения к скрипту и выбора требуемой операции (удаления записи или выдачи формы подтверждения). Точно так же, как в случае с добавлением и редактированием, удаление выполняется через Zend_Db_Table методом delete(). В конце функции выполняется перенаправление пользователя на страницу со списком альбомов. Таким образом, если одна из проверок корректности параметров не пройдена, происходит возврат на исходную страницу без помощи многократного обращения к _redirect() внутри функции.

Шаблон представляет собой простую форму:
zf-tutorial/application/views/scripts/index/delete.phtml:
<?php echo $this->render('header.phtml'); ?>
<h1><?php echo $this->escape($this->title); ?></h1>
<?php if ($this->album) :?>
<form action="<?php echo $this->baseUrl ?>/index/delete" method="post">
<p>Are you sure that you want to delete
    '<?php echo $this->escape($this->album->title); ?>' by
    '<?php echo $this->escape($this->album->artist); ?>'?
</p>
<div>
    <input type="hidden" name="id"
        value="<?php echo $this->album->id; ?>" />
    <input type="submit" name="del" value="Yes" />
    <input type="submit" name="del" value="No" />
</div>
</form>
<?php else: ?>
<p>Cannot find album.</p>
<?php endif;?>

<?php echo $this->render('footer.phtml'); ?>


Устранение неполадок

Если возникают сложности при обращении к любым действиям помимо index/index, скорее всего причина состоит в том, что класс-роутер не может корректно определить, в какой директории находится ваш веб-сайт. Такая ситуация может возникнуть, если URL сайта не соответствует пути к его директории относительно корневого каталога, открытого для доступа из сети.
Если приведенный выше код не соответствует вашему случаю, необходимо задать переменной $baseURL корректное для вашего сервера значение:

zf-tutorial/index.php:
...
// setup controller
$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setBaseUrl('/mysubdir/zf-tutorial');
$frontController->setControllerDirectory('./application/controllers');
...

Значение “/mysubdir/zf-tutorial/” понадобится заменить на действительный путь к файлу index.php. Например, если ваш URL к index.php выглядит как http://localhost/~ralle/zf-tutorial/index.php, корректным значением для переменной $baseUrl будет “/~ralle/zf-tutorial/”.

Заключение

На этом построение простого но полнофункционального MVC-приложения можно считать завершенным. Надеюсь, этот обзор был полезен и информативен для вас. Любые замечания к оригинальному тексту можно отправлять автору статьи по адресу rob@akrabat.com. Комментарии, связанные с русским переводом, отправляйте на musayev@yandex.ru.

В данной статье выполнен лишь поверхностный обзор Zend Framework, в котором существует великое множество других классов, помимо перечисленных здесь. Для подробного ознакомления с ними следует обратиться к соответствующим ресурсам:
При переводе использовались материалы свободной энциклопедии Wikipedia.org.

Данный перевод может быть найден по постоянному адресу:
http://archive.paradigm.ru/zend-fw-intro…

Оригинал статьи на английском доступен на сайте автора:
http://akrabat.com/zend-framework-tutori…

Update: Примеры из статьи на сайте автора: zf-tutorial_130.zip.



Рубрика: PHP



О том как разработчики пьют кофе.

Юмор

Сегодня ночью вернулся из Москвы, где я посетил сразу три конференции — SQA, PM Days и PHPconf. На конференции прозвучала масса интересных докладов, о которых наверняка еще не раз напишут, я же хочу поделиться некоторыми забавными наблюдениями. В перерывах между докладами все присутствующие могли выйти в холл, где их ожидали вкусные плюшки, молоко, чай и кофе. Для последнего на столах установили пять термосов. Разумеется для более чем двухсот участников конференции их было явно недостаточно и на каждой конференции эту проблему решали по своему, как умели.


Подробнее... | Рубрика: Юмор | Добавлено: 24.06.2008

Работаем с LINQ to XML.

LINQ

Что же, попробуем раскрыть принципы работы этой новой технологии от Microsoft.


Подробнее... | Рубрика: LINQ | Добавлено: 24.06.2008

XmlSerializer - Assembly Leak без спроса.

Сборки и развертывание

В некоторых частях .NET Framework, таких как XmlSerializer, используется внутреннее динамическое создание кода.XmlSerializer создает временные файлы C#, компилирует результирующие файлы во временную сборку и затем загружает эту сборку в процесс. Такое создание кода тоже стоит сравнительно дорого, поэтому XmlSerializer размещает временные сборки в кэш, по одной на каждый тип. Это значит, что в следующий раз при создании кода XmlSerializer для класса Х не будет создаваться новая сборка, а будет использована сборка из кэша. Однако, не все так просто.


Подробнее... | Рубрика: Сборки и развертывание | Добавлено: 24.06.2008

Остальные статьи:

Реализация MVC в WPF. Александр Шер
ADO.NET Sync Services. Марат Бакиров
Рефакторинг JavaScript с применением Microsoft ASP.NET AJAX. Александр Шер
Архитектура приложений: интерфейс пользователя. Дмитрий Мартынов
Введение в Windows Workflow Foundation. Марат Бакиров
Создание расширяемых и удобных библиотек на платформе .NET. Особенности реализац...
Почему Ruby и Python не могут занять место стареющей Java
Использование пространств имен для организации JavaScript-кода
Создание сложных приложений в ExtJS
Google добавил интерфейс для AJAX-библиотек
Стивен Синофски о Windows 7
Несколько вещей об Ajax, которые должен знать веб-мастер
Model-View-Controller для JavaScript
Remix 2008: интернет меняет Microsoft
Планировщик задач на JavaScript
Построение систем автоматического протоколирования Си/Си++ кода

Цитата дня (все,добавить):

    Рубрикатор

Программирование

C/С++
Обучение
Windows API
XAML
Моделирование
Паттерны
Visual Basic 7 .NET
WxWidgets
Функции WinApi
Функции С++
Разработка под Mac OS
Eiffel
Visual Studio 2008
UI дизайн
Алгоритмы
Конкурсные статьи
Turbo Pascal
Visual Studio
CASE-средства
Visual Studio 2005
Без VCL
Delphi
Тех. документация
Тестирование
Software Testing
ООП
TCP/IP
Google Android
Windows Installer
.NET Framework
Драйвера
C# C Sharp
Справка
Проектирование
Информ. системы
Visual Basic
Assembler
Оптимизация кода
Gtk+
Компоненты
Реинжиниринг
Управление проектами
Extreeme programming
Lotus Notes
Алгебраическое проектирование

Интернет технологии

PHP
Perl
ASP
WAP
Cookies
SSI
CGI
Web Servers
VB Script
DNS
CSS
XML
Html
Java Script
Java2ME
Firewall
Flash
.htaccess
Apache
VRML
Протоколы
Поисковые системы
Технология JAVA
Учебник по PHP
Учебник по JavaScript
Учебник по XML
Java Q&A
AJAX
DHTML
XHTML
Dreamweaver
Web 2.0
Python
Вебмастеру
Cisco
Ruby on Rails
Silverlight

Базы данных

Access
InterBase
MySQL
Oracle
ADO .NET
Основы SQL
Учебник по Access 2002
MS
Microsoft FoxPro
Доступ к данным
XML в MS SQL Server 2000
ODBC и MyODBC
Обучение
Caché
DB2
PostgresSQL
Sybase
Теория
Хранилища данных
Безопасность
Реляционные данные
MySQL и mSQL

Остальное:

Разное
Обзоры книг
Безопасность
Графика и дизайн
Юмор
Linux
Фракталы
Microsoft Axapta
Многоядерность
Сети
Microsoft Office
Работа
MS-DOS
Криптография
Графика и игроделание
Новости SDK
Системы защиты
Учебник по AutoCad
CVS
Windows XP
Windows Server 2003
Windows Vista
Windows 7
Мероприятия