Автор: Rob Allen,
Это устаревшая версия руководства. Последняя версия доступна по ссылке.

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

Архитектура Model-View-Controller

Обычно PHP-приложение выглядит следующим образом:

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
<?php
include "common-libs.php";
include "config.php";
($hostname, $username, $password);
($database);
?>
<?php include "header.php"; ?>
<h1>Home Page</h1>
<?php
$sql = "SELECT * FROM news";
$result = ($sql);
?>
<table>
<?php
while ($row = ($result)) {
?>
<tr>
<td><?php echo $row['date_created']; ?></td>
<td><?php echo $row['title']; ?></td>
</tr>
<?php
}
?>
</table>
<?php include "footer.php"; ?>

В течение всего жизненного цикла приложения такого типа оно становится немасштабируемым и содержит множество уязвимостей во множестве мест.
Одним из способов повышения масштабируемости приложения является разделение кода на три части (обычно в отдельных файлах):

Модель Модель – часть приложения, которая работает с данными, предназначенными для вывода. В приведённом выше примере это получение новостей. Таким образом, модель реализует бизнес-логику приложения и берёт на себя работу с БД.
Вид Скрипты вида состоят из частей приложения, которые должны быть выведены на экран. Обычно, это HTML-код.
Контроллер Контроллер объединяет вместе модель и вид, определяя, какие именно скрипты модели и вида должны быть задействованы.

Zend Framework использует архитектуру Model-View-Controller. Это подразумевает разделение раных частей приложения, облегчая разработку и масштабирование приложения.

Системные требования

Для корректной работы Zend Framework понадобится:

  • PHP 5.1.4 (или выше)
  • Поддержка веб-сервером функционала mod_rewrite

Условности

В этом руководстве я предполагаю, что вы используете PHP 5.1.4 (или более позднюю версию) и веб-сервер Apache. Ваш Apache должен быть сконфигурирован с поддержкой mod_rewrite.
Также Apache должен поддерживать использование файлов .htaccess. Для этого обычно достаточно установить параметр “AllowOverride” в значение All в файле httpd.conf. Для получения более подробной информации воспользуйтесь документацией.

Получение Zend Framework

Загрузить Zend Framework вы можете .

Структура каталогов

Zend Framework не содержит жёстких требований к структуре каталогов, но существуют рекомендации, которых мы будем придерживаться. Эта структура предполагает, что вы имеете полный контроль над конфигурацией Apache, таким образом имея возможность держать файлы вне корневого каталога.
Структура каталогов указана на изображении:
Структура каталогов Zend Framework
Как вы можете видеть, мы имеем отдельные каталоги для скриптов модели, вида и контроллера. Каталог “public” является корневым для нашего сайта, который будет открыт при помощи URL http://localhost/zf-tutorial/public/. Благодаря такой структуре каталогов большинство файлов приложения защищено при помощи Apache и недоступно извне.

Замечание.
Для настройки публично доступного веб-сайта вам понадобится создать виртуальный хост и указать в DocumentRoot ваш публичный каталог. Например, создание виртуального хоста zf-tutorial.localhost будет приблизительно таким:

1
2
3
4
5
6
7
<VirtualHost *:80>
    ServerName zf-tutorial.localhost
    DocumentRoot /var/www/html/zf-tutorial/public
    <Directory "/www/cs">
        AllowOverride All
    </Directory>
</VirtualHost>

В таком случае сайт будет доступен по адресу zf-tutorial.localhost (возможно понадобится отредактировать файл c:\windows\system32\drivers\etc\hosts и указать, что запросы zf-tutorial.localhost должны идти на адрес 127.0.0.1).

Изображения, JavaScript и CSS файлы хранятся в специальных папках в публичном каталоге. Файлы Zend Framework’а будут располагаться в папке library. Если вам понадобится использовать другие библиотеки, вы можете поместить их туда же.
Распакуйте архив с Zend Framework’ом во временный каталог. Скопируйте подкаталог library/Zend в zf-tutorial/library/. Каталог zf-tutorial/library/ должен содержать подкаталог Zend.

Bootstrapping (начальная загрузка)

Модуль контроллера Zend Framework, Zend_Controller, спроектирован с поддержкой ЧПУ. Для достижения этого все запросы должны перенаправляться на файл index.php. Такая схема также известна как паттерн проектирования Front Controller. Он проводит нас в центральную точку всех страниц приложения и гарантирует, что окружения корректно настроено для запуска приложения. Мы добиваемся этого при помощи файла .htaccess в каталоге zf-tutorial/public.
Файл zf-tutorial/public/.htaccess:

1
2
3
4
5
6
7
8
9
10
11
12
# Rewrite rules for Zend Framework
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* index.php

# Security: Don't allow browsing of directories
Options -Indexes

# PHP settings
php_flag magic_quotes_gpc off
php_flag register_globals off
php_flag short_open_tag on

Правило RewriteRule очень простое и может быть прочитано как “для всех файлов, не существующих на диске, использовать файл index.php”.
Также мы устанавливаем несколько параметров PHP ini по соображениям безопасности и здравого смысла, также включаем параметр short_open_tag для использования в скриптах вида. Они уже настроены по умолчанию, но мы хотим быть уверенными. Заметьте, что установка параметров при помощи php_frag в .htaccess будет работать только в том случае, если вы используете mod_php. Если вы используете CGI/FastCGI, тогда вы должны быть уверены в том, что ваш php.ini настроен корректно. Заметьте также, что для того, чтобы файлы .htaccess использовались вашим Apache, параметр AllowOverride должен иметь значение All в файле httpd.conf.

Bootstrap-файл index.php

zf-tutorial/public/index.php – наш bootstrap-файл и мы начинаем со следующего кода:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
(E_ALL|E_STRICT);
('display_errors', 1);
('Europe/Moscow');

// указываем каталоги и включаем автозагрузку классов
('.' . PATH_SEPARATOR . '../library/'
    . PATH_SEPARATOR . '../application/models'
    . PATH_SEPARATOR . ());
include "Zend/Loader.php";
Zend_Loader::registerAutoload();

// настраиваем контроллер
$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setControllerDirectory('../application/controllers');

// запускаем
$frontController->dispatch();

Заметьте, что мы не пишем тег ?> в конце файла. Он не является обязательным и мы опускаем его для того, чтобы избежать труднообнаруживаемых ошибок при редиректе с использованием header() при лишних пробелах после тега ?>.
Разберём содержимое файла.

1
2
3
(E_ALL|E_STRICT);
('display_errors', 1);
('Europe/Moscow');

Эти строки позволят нам увидеть все ошибки, которые мы допустим. Также мы установим наш часовой пояс, как этого требует PHP5.1+.

1
2
3
4
5
6
// указываем каталоги и включаем автозагрузку классов
('.' . PATH_SEPARATOR . '../library/'
    . PATH_SEPARATOR . '../application/models'
    . PATH_SEPARATOR . ());
include "Zend/Loader.php";
Zend_Loader::registerAutoload();

Zend Framework спроектирован таким образом, что его файлы должны находиться в “include path”. Также в include path мы укажем каталог с нашими классами моделей, что позволит нам проще подключать их. Мы подключаем файл Zend/Loader.php, получаем доступ к классу Zend_Loader и вызываем метод registerAutoload() для автоматического подключения классов при их инстанцировании.

1
2
3
4
// настраиваем контроллер
$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setControllerDirectory('../application/controllers');

Нам необходимо сконфигурировать фронт-контроллер таким образом, чтобы дать ему знать, в каких каталогах следует искать наши контроллеры.

1
2
3
    $frontController = Zend_Controller_Front::getInstance();
    $frontController->setControllerDirectory('./application/controllers');
    $frontController->throwExceptions(true);

Так как мы запускаем тестовую систему, мы сообщаем фронт-контроллеру выбрасывать все исключения. По умолчанию, он ловит их для нас и перенаправляет в ErrorController. Это может несколько запутать новичков в Zend Framework, легче просто выбрасывать все исключения, чтобы мы могли их легко увидеть. Конечно, в конечном продукте не следует выводить ошибки на экран пользователя.
Фронт-контроллер использует класс роутера для парсинга URL и определения функции, используемой для отображения страницы. Для корректной работы роутера следует определить часть URL – путь до нашего public-каталога, чтобы роутер мог увидеть элементы URI после этой точки. Этим занимается объект окружения (Request). Он очень хорошо справляется с задачей определения базового URL, но если о по каким-либо причинам не сможет этого сделать, вы всегда можете указать этот параметр вручную при помощи метода $frontController->setBaseUrl().
Мы запускаем наше приложение:

1
2
// запускаем
$frontController->dispatch();

Если вы перейдёте по URL http://localhost/zf-tutorial/public/ вы увидите fatal error:

Fatal error: Uncaught exception ‘Zend_Controller_Dispatcher_Exception’ with
message ‘Invalid controller specified (index)’ in…

Это говорит о том, что нам нужно продолжать настраивать приложение.

Вебсайт

Мы будем заниматься созданием простой системы для отображения нашей коллекции audioCD. Главная страница будет отображать нашу коллекцию. Также мы сможем удалять, редактировать и добавлять CD. Перейдём к хранению нашего списка в базе данных со следующей схемой:

Поле Тип Null Прочее
id Integer No Autoincrement, Primary Key
artist Varchar(100) No
title Varchar(100) No

Необходимые страницы

Нам потребуются следующие страницы:

Главная страница Отображает список альбомов со ссылками для редактирования и удаления их. Также выводится ссылка для добавления нового альбома.
Добавление альбома Отображается форма для добавления альбома
Редактирование альбома Страница отображает форму для редактирования альбома
Удаление альбома Страница запрашивает подтверждение на удаление альбома, после чего удаляет его

Организация страниц

Перед настройкой наших страниц важно понять, как ZF работает с ними. Каждая страница приложения – это “действие” (action), действия группируются в контроллеры. Например, для URL формата http://localhost/public/zf-tutorial/news/view, действие – view, контроллер – news. Это позволяет группировать родственные действия. Например, контроллер news может иметь действия list, archived и view. Реализация MVC в ZF также поддерживает модули для группировки контроллеров вместе, но наше приложение не нуждается в этом.
По умолчанию ZF использует действие index. Таким образом URL http://localhost/zf-tutorial/public/news/ вызовет действие index контроллера news. Аналогичным образом контроллер по умолчанию – index, URL http://localhost/zf-tutorial/public/ выполнит действие Index контроллера index.
Итак, мы имеем четыре страницы для действий с альбомами. Мы сгруппируем их в один контроллер как четыре действия:

Page Controller Action
Home page Index index
Add New Album Index add
Edit Album Index edit
Delete Album Index delete

Мило и просто!

Настройка контроллера

Теперь мы займёмся настройкой контроллера. В Zend Framework контроллер – это класс, который может быть вызван как {Controller name}Controller. Заметьте, что {Controller name} должен начинаться с буквы в верхнем регистре. Этот класс должен располагаться в файле {Controller name}Controller.php, располагающемся в директории application/controllers. Ещё раз – {Controller name} должен начинаться с заглавной буквы, все остальные символы должны быть в нижнем регистре. Каждое действие – это pubilc-метод в классе контроллера с именем {action name}Action. {action name} должен состоять только из букв в нижнем регистре. Разнообразные вариации именования контроллеров и действий допустимы, для этого существуют специальные правила, которые вы должны изучить перед использованием. Всё это описано в документации.
Таким образом, наш класс контроллера называется IndexController и определён в файле zf-tutorial/application/controllers/IndexController.php. Создайте этот файл используя следующий скелет.
zf-tutorial/application/controllers/IndexController.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class IndexController extends Zend_Controller_Action
{
    function indexAction()
    {
    }

    function addAction()
    {
    }

    function editAction()
    {
    }

    function deleteAction()
    {
    }
}

Теперь мы имеем четыре действия, как мы и хотели. Но они не готовы к использованию пока мы не настроим скрипты вида.

URL Action
http://localhost/zf-tutorial/public/ IndexController::indexAction()
http://localhost/zf-tutorial/public/index/add IndexController::addAction()
http://localhost/zf-tutorial/public/index/edit IndexController::editAction()
http://localhost/zf-tutorial/pubilc/index/delete IndexController::deleteAction()

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

Настройка отображения

View-компонент ZF называется Zend_View. View-компонент позволяет отделять код отображения страницы от кода действий.
Базовое использование Zend_View выглядит так:

1
2
3
$view = new Zend_View();
$view->setScriptPath('/path/to/view_files');
echo $view->render('viewScipt.php');

Как вы можете заметить, писать этот код в каждом нашем действии будет не лучшим решением. Лучше мы инициализируем view в другом месте и сделаем доступным экземпляр view в каждом действии. Проектировщики Zend Framework предвидели этот тип проблем и решили их добавив функционал помощников действий (action helpers). Zend_Controller_Action_Helper_ViewRenderer берёт на себя заботы об инициализации свойства view ($this->view) для нашего использования и рендеринга скрипта. Для рендеринга он указывает объекту Zend_View искать скрипт вида в каталоге views/scripts/{controller name} и по умолчанию ищет скрипт с именем действия и расширением phtml. В данном случае будет запрошен view-скрипт views/scripts/{controller name}/{action_name}.phtml, отрендеренное содержимое которого будет присоединено к телу объекта ответа (response). Объект ответа используется для объединения HTTP-заголовков, тела содержимого страницы и сгенерированных исключений как результат использования системы MVC. Фронт-контроллер автоматически отправляет заголовки и тело содержимого в конце диспетчеризации.
Для интеграции вида в наше приложение всё, что нам необходимо – создать файлы вида. Чтобы удостовериться, что всё работает как надо, вы добавляем некоторое специфичное содержимое (title страницы) в действие контроллера.
zf-tutorial/application/controllers/IndexController.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

class IndexController extends Zend_Controller_Action
{
    function indexAction()
    {
        $this->view->title = "My Albums";
    }

    function addAction()
    {
        $this->view->title = "Add New Album";
    }

    function editAction()
    {
        $this->view->title = "Edit Album";
    }

    function deleteAction()
    {
        $this->view->title = "Delete Album";
    }
}

В каждом методе мы передаём заголовок страницы в скрипт вида как свойство “title”. Имейте в виду, что вывод на экран не происходит в этой точке – он производится фронт-контроллером после окончания процесса диспетчеризации.

Теперь нам надо добавить четыре файла вида в наше приложение. Эти файлы также известны как скрипты вида, или шаблоны. Каждый файл шаблона носит имя своего действия с расширением .phtml, которое показывает, что это файл шаблона. Файл должен быть в подкаталоге с именем контроллера, как эти четыре файла:
zf-tutorial/application/views/scripts/index/index.phtml

1
2
3
4
5
6
7
8
<>
    <>
        <><?php echo $this->escape($this->title); ?></>
    </>
    <>
        <><?php echo $this->escape($this->title); ?></>
    </>
</>

zf-tutorial/application/views/scripts/index/add.phtml

1
2
3
4
5
6
7
8
<>
    <>
        <><?php echo $this->escape($this->title); ?></>
    </>
    <>
        <><?php echo $this->escape($this->title); ?></>
    </>
</>

zf-tutorial/application/views/scripts/index/edit.phtml

1
2
3
4
5
6
7
8
<html>
    <head>
        <title><?php echo $this->escape($this->title); ?></title>
    </head>
    <body>
        <h1><?php echo $this->escape($this->title); ?></h1>
    </body>
</html>

zf-tutorial/application/views/scripts/index/delete.phtml

1
2
3
4
5
6
7
8
<html>
    <head>
        <title><?php echo $this->escape($this->title); ?></title>
    </head>
    <body>
        <h1><?php echo $this->escape($this->title); ?></h1>
    </body>
</html>

Попробуйте набрать в вашем браузере URL’ы наших контроллеров/действий и увидете заголовки страниц (title), указанные нами в действиях.

Общий HTML-код

Очень быстро становится очевидным, что в наших шаблонах есть дублирующийся HTML-код. Это очень распространённая проблема и для её решения был спроектирован компонент Zend_Layout. Zend_Layout позволяет нам переместить код блоков header и footer в макет (layout), подключающий скрипты вида, специфичные для действия, которое будет выполнено.
Следующие изменения необходимы. Во-первых, нам надо решить, где хранить наш макет скриптов вида. Рекомендуемым местом является директория application. Создайте каталог layouts в директории zf-tutorial/application.
Мы должны запустить Zend_Layout в bootstrap-файле, добавив в public/index.php следующий код:

1
2
3
4
5
6
...
$frontController->throwExceptions(true);
$frontController->setControllerDirectory('../application/controllers');
Zend_Layout::startMvc(('layoutPath'=>'../application/layouts'));
// запускаем
$frontController->dispatch();

Метод startMvc() выполняет некоторую работу за кулисами, настраивая плагин фронт-контроллера, который гарантирует, что компонент Zend_Layout обработает скрипт макета со скриптами вида действия по окончании процесса диспетчеризации.
Теперь нам понадобится скрипт вида макета. По умолчанию, он носит имя layout.phtml и расположен в каталоге макетов (layoutPath, в нашем случае – zf-tutorial/application/layouts). Он должен выглядеть примерно так:
zf-tutorial/application/layouts/layout.phtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
        <title><?php echo $this->escape($this->title); ?></title>
    </head>
    <body>
        <div id="content">
            <h1><?php echo $this->escape($this->title); ?></h1>
            <?php echo $this->layout()->content; ?>
        </div>
    </body>
</html>

Заметьте, что мы сделали наш код XHTML-совместимым. Так как заголовок в теге <h1> отображается на всех страницах, мы вынесли его в файл макета и используем помощник вида escape() для уверенности, что он будет закодирован.
Для получения скрипта вида текущего действия мы используем помощник вида layout(): echo $this->layout()->content, который сделает это для нас. Это возможность выполнить скрипт вида действия перед обработкой макета.
Теперь мы можем очистить четыре скрипта действий, т.к. мы у нас нет ничего специфического, что следовало бы там выводить. Очистим содержимое скриптов вида index.phtml, add.phtml, edit.phtml и delete.phtml.
Мы можете попробовать набрать в браузере URL’ы снова и не увидите никаких изменений с прошлого раза! Ключевое отличие в том, что вся работа происходит в макете.

Стили

Для того, чтобы наше приложение хорошо выглядело, нам понадобится CSS-файл. Проблемой является то, что мы не можем подключить CSS файл, не зная полного URL до нашего приложения. Для этого мы создадим помощник вида (view helper) под названием baseUrl(), который будет получать требуемую нам информацию от объекта окружения. Это позволит нам использовать неизвестную часть URL.
Помощники вида расположены в директории application/views/helpers и называются {Helper name}.php (первая буква должна быть в верхнем регистре), их содержимое – это класс с названием Zend_View_Helper_{Helper name} (опять же, первая буква – большая). В классе должен быть метод с названием {helper name}() (первая буква – в нижнем регистре). В нашем случае файл называется BaseUrl.php и выглядит так:
zf-tutorial/application/views/helpers/BaseUrl.php

1
2
3
4
5
6
7
8
class Zend_View_Helper_BaseUrl
{
    function baseUrl()
    {
        $fc = Zend_Controller_Front::getInstance();
        return $fc->getBaseUrl();
    }
}

Не усложняйте метод. Мы просто инстанцируем фронт контроллер и возвращаем результат метода getBaseUrl().
Теперь нам надо добавить CSS файл в секцию файла application/layouts/layout.phtml:
zf-tutorial/application/layouts/layout.phtml

1
2
3
4
5
6
7
...
<>
    < http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <><?php echo $this->escape($this->title); ?></>
    < rel="stylesheet" type="text/css" media="screen" href="<?php echo $this->baseUrl();?>/css/site.css" />
</>
...

И, наконец, мы должны создать файл стилей:
zf-tutorial/public/css/site.css

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
32
33
34
body,html {
    margin: 0 5px;
    font-family: Verdana,sans-serif;
}
h1 {
    font-size:1.4em;
    color: #008000;
}
a {
    color: #008000;
}

/* Table */
th {
    text-align: left;
}
td, th {
    padding-right: 5px;
}

/* style form */
form dt {
    width: 100px;
    display: block;
    float: left;
    clear: left;
}
form dd {
    margin-left: 0;
    float: left;
}
form #submitbutton {
    margin-left: 100px;
}

Эти стили сделают наше приложение несколько приятней внешне, но, как вы можете заметить, я не дизайнер.

База данных

Мы уже отделили управление приложением от вида, самое время ознакомиться с понятием модели. Помните, что модель это часть приложения, называемая ядром (также известная как “бизнес-логика”) и в нашем случае берёт на себя работу с БД. Мы будем использовать класс Zend_Db_Table, который будет использоваться для извлечения, вставки, обновления и удаления записей из таблиц БД.

Настройка

Для использования Zend_Db_Table нам необходимо указать, какую базу данных использовать вместе с логином и паролем для доступа. Так как мы предпочитаем не хард-кодить, вынося эту информацию в приложение, мы используем конфигурационный файл для хранения этой информации.
Zend Framework содержит компонент Zend_Config для настраиваемого объектно-ориентированного доступа к конфигурационным файлам. Файл конфигурации может быть в INI или XML формате. Мы будем использовать INI-файл с именем config.ini и хранить его в директории application:
zf-tutorial/application/config.ini

1
2
3
4
5
6
[general]
db.adapter = PDO_MYSQL
db.params.host = localhost
db.params.username = rob
db.params.password = 123456
db.params.dbname = zftest

Вы должны использовать ваши логин, пароль и имя базы данных, не мои! Для больших приложений с несколькими конфигурационными файлами вы можете создать отдельную директорию, такую как application/config, и хранить ваши конфиги вместе.
Использовать Zend_Config очень легко:

1
$config = new Zend_Config_Ini('config.ini', 'section');

Заметьте, что в этом случае Zend_Config_Ini загружает только одну секцию INI-файла, не все (все секции вы тоже можете загрузить, если вам это понадобится). Zend_Config_Ini также воспринимает точку в имени параметра как иерархический разделитель для группировки схожих параметров конфигурации. В нашем config.ini параметры host, username, password и dbname сгруппированы в $config->params->config.
Мы будем загружать файл конфигурации в наш bootstrap-файл (public/index.php):
Часть zf-tutorial/public/index.php

1
2
3
4
5
6
7
8
9
10
11
12
...
include "Zend/Loader.php";
Zend_Loader::registerAutoload();

// загружаем конфигурацию
$config = new Zend_Config_Ini('../application/config.ini', 'general');
$registry = Zend_Registry::getInstance();
$registry->set('config', $config);

// настраиваем контроллер
$frontController = Zend_Controller_Front::getInstance();
...

При помощи класса Zend_Config_Ini мы загружаем секцию ‘general’ конфига application/config.ini в наш объект $config. После этого мы записываем этот объект в реестр чтобы он мог быть извлечён в любой другой части приложения.
Замечание: В этом руководстве мы не нуждаемся в хранении $config в реестре, но это хорошая идея для больших приложений, так как вы можете иметь больше информации в INI-файле, чем конфигурация БД.

Настройка Zend_Db_Table

Для использования Zend_Db_Table нам необходимо сообщить ему конфигурацию БД, которую мы только что загрузили. Для того, чтобы сделать это, мы должны инстанцировать Zend_Db и зарегистрировать его при помощи статичного метода Zend_Db_Table::setDefaultAdapter(). Опять же, мы сделаем это в нашем bottstrapp-файле.
Часть файла zf-tutorial/public/index.php

1
2
3
4
5
6
7
8
9
10
11
...
$registry = Zend_Registry::getInstance();
$registry->set('config', $config);

// настраиваем БД
$db = Zend_Db::factory($config->db);
Zend_Db_Table::setDefaultAdapter($db);

// настраиваем контроллер
$frontController = Zend_Controller_Front::getInstance();
...

Как вы можете увидеть, Zend_Db имеет статичный метод factory(), который извлекает данные из объекта $config->db и инстанцирует корректный адаптер БД.

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

Мы переходим к использованию MySQL и создаём таблицу следующим SQL-выражением:

1
2
3
4
5
6
albums (
    id (11) ,
    artist (100) ,
    title (100) ,
    (id)
);

Запустим это выражение в MySQL-клиенте, например в phpMyAdmin или в стандартном клиенте MySQL в командной строке.

Вставка тестовых альбомов

Мы вставляем несколько строк в таблицу, чтобы мы могли тестировать функциональность главной страницы. Я взял первые два альбома из раздела “Top Sellers” на Amazon.co.uk:

1
2
3
4
albums (artist, title)

    ('Duffy', 'Rockferry'),
    ('Van Morrison', 'Keep It Simple');

Модель

Zend_Db_Table – это абстрактный класс, его будет наследовать наш класс, который будет управлять альбомами. Нет требований к именовании нашего класса, но есть смысл назвать его именем таблицы БД. Таким образом, наш класс будет называться Albums, как и наша таблица albums. Для того, чтобы дать знать Zend_Db_Table, как называется наша таблица, у нас установлено защищённое (protected) свойство $_name с именем таблицы. Также Zend_Db_Table предполагает, что ваша таблица имеет auto-incement primary key с названием id. Имя этого поля при необходимости может быть изменено.
Мы будем хранить наш класс Album в файле с названием Album.php в директории applications/models:
zf-tutorial/application/models/Albums.php

1
2
3
4
class Albums extends Zend_Db_Table
{
    protected $_name = 'albums';
}

Не слишком сложно? К счастью для нас, Zend_Db_Table предоставляет весь необходимый нам функционал. Тем не менее, если вам необходим специфичный функционал модели, вы всегда можете расширить свой класс. Чаще всего дополнительные методы, которые вам понадобятся – методы извлечения из БД с приведением данных к нужному виду. Вы можете также использовать Zend_Db_Table для связных таблиц, это позволит извлекать релевантные данные сразу из нескольких таблиц.

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

Теперь мы имеем настроенную конфигурацию, мы можем извлечь из БД информацию и вывести некоторые альбомы. Это делать мы будем в классе IndexController и методе indexAction():
zf-tutorial/application/controllers/IndexController.php

1
2
3
4
5
6
7
8
    ...
    function indexAction()
    {
        $this->view->title = "My Albums";
        $albums = new Albums();
        $this->view->albums = $albums->fetchAll();
    }
    ...

Метод fetchAll() возвращает экземпляр Zend_Db_Table_Rowset, который позволяет нам итерации по возвращённым строкам в скрипте вида. Теперь мы можем заполнить файл index.phtml:
zf-tutorial/application/views/scripts/index/index.phtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<>< href="<?php echo $this->url(array('controller'=>'index', 'action'=>'add'));?>">Add new album</></>
<>
    <>
        <>Title</>
        <>Artist</>
        <>&nbsp;</>
    </>
    <?php foreach($this->albums as $album) : ?>
        <>
            <><?php echo $this->escape($album->title);?></>
            <><?php echo $this->escape($album->artist);?></>
            <>
                < href="<?php echo $this->url(array('controller'=>'index', 'action'=>'edit', 'id'=>$album->id));?>">Edit</>
                < href="<?php echo $this->url(array('controller'=>'index', 'action'=>'delete', 'id'=>$album->id));?>">Delete</>
            </>
        </>
    <?php endforeach; ?>
</>

Прежде всего мы создаём ссылку на добавление нового альбома. Помощник вида url() помогает нам создать ссылку. Мы просто передаём ему массив параметров, которые нам необходимы, и он возвращает нам URL нужного действия.
Мы создаём html-таблицу для вывода каждого заголовка альбома, исполнителя и выдаёт ссылки для редактирования и удаления записей. Стандартный цикл foreach используется для итерации по списку альбомов. Мы используем альтернативную форму записи цикла с endforeach для закрытия блока. Опять же, помощник вида url() используется для создания ссылок на редактирование и удаление альбомов.
По ссылке http://localhost/zf-tutorial/ теперь вы увидите список из двух альбомов, подобно этому:
Список альбомов

Добавление новых альбомов

Теперь мы можем реализовать добавление новых альбомов. Добавление альбомов будет состоять из двух частей:

  • Отображение формы пользователю
  • Обработка данных формы и запись в БД

Для создания формы мы используем Zend_Form. Этот компонент позволяет нам создавать форму и проводить валидацию данных. Мы создаём новый класс модели AlbumForm, который расширяет Zend_Form, определяя нашу форму:
zf-tutorial/application/models/AlbumForm.php

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
<?php

class AlbumForm extends Zend_Form
{
    public function __construct($options = null)
    {
        parent::__construct($options);
        $this->setName('album');

        $id = new Zend_Form_Element_Hidden('id');

        $artist = new Zend_Form_Element_Text('artist');
        $artist->setLabel('Artist')
            ->setRequired(true)
            ->addFilter('StripTags')
            ->addFilter('StringTrim')
            ->addValidator('NotEmpty');

        $title = new Zend_Form_Element_Text('title');
        $title->setLabel('Title')
            ->setRequired(true)
            ->addFilter('StripTags')
            ->addFilter('StringTrim')
            ->addValidator('NotEmpty');

        $submit = new Zend_Form_Element_Submit('submit');
        $submit->setAttrib('id', 'submitbutton');

        $this->addElements(($id, $artist, $title, $submit));
    }
}

В конструкторе AlbumForm мы создаём четыре элемента для id, артиста, заголовка альбома и кнопки отправки данных. Для каждого элемента мы устанавливаем различные атрибуты, включая label для отображения. Для текстовых элементов мы подключаем два фильта, StripTags и StringTrim для удаления ненужного HTML и избыточных пробелов. Также мы устанавливаем обязательные элементы при помощи setRequired() и добавляем валидатор NotEmpty для уверенности в том, что пользователь заполнит необходимые поля.
Теперь нам надо получить форму для вывода, для этого мы заполним метод addActino():
zf-tutorial/application/controllers/IndexController.php

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
...
    function addAction()
    {
        $this->view->title = "Add New Album";

        $form = new AlbumForm();
        $form->submit->setLabel('Add');
        $this->view->form = $form;

        if ($this->_request->isPost()) {
            $formData = $this->_request->getPost();
            if ($form->isValid($formData)) {
                $albums = new Albums();
                $row = $albums->createRow();
                $row->artist = $form->getValue('artist');
                $row->title = $form->getValue('title');
                $row->save();

                $this->_redirect('/');
            } else {
                $form->populate($formData);
            }
        }
    }
...

Теперь разберём этот код подробнее:

1
2
3
$form = new AlbumForm();
$form->submit->setLabel('Add');
$this->view->form = $form;

Мы инстанцируем класс AlbumForm, устанавливаем метку для кнопки отправки в “Add” и передаём её в скрипт вида для рендеринга.

1
2
3
    if ($this->_request->isPost()) {
        $formData = $this->_request->getPost();
        if ($form->isValid($formData)) {

Если метод isPost() объекта окружения возвращает true, значит форма была отправлена и мы получаем данные формы с использованием метода getPost(), после чего проверяем валидность данных методом isValid() нашей формы.

1
2
3
4
5
6
$albums = new Albums();
$row = $albums->createRow();
$row->artist = $form->getValue('artist');
$row->title = $form->getValue('title');
$row->save();
$this->_redirect('/');

Если форма валидна, тогда мы инстанцируем класс модели Albums и используем createRow для создания пустой записи, заполняя её данными перед сохранением. После этого мы сохраняем новую запись альбома и делаем редирект на главную страницу при помощи метода _redirect().

1
2
3
} else {
$form->populate($formData);
}

Если данные формы не валидны, мы наполняем форму введёнными данными и отображаем её снова.
Теперь нам необходимо вывести форму в скрипте вида:
zf-tutorial/applicaiton/views/scripts/index/add.phtml

1
<?php echo $this->form ;?>

Как вы могли заметить, вывод формы очень простой.

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

Редактирование альбома почти идентично добавлению, код очень похож:
zf-tutorial/application/controllers/IndexController.php

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
32
33
34
35
...
function editAction()
{
    $this->view->title = "Edit Album";

    $form = new AlbumForm();
    $form->submit->setLabel('Save');
    $this->view->form = $form;

    if ($this->_request->isPost()) {
        $formData = $this->_request->getPost();
        if ($form->isValid($formData)) {
            $albums = new Albums();
            $id = (int)$form->getValue('id');
            $row = $albums->fetchRow('id='.$id);
            $row->artist = $form->getValue('artist');
            $row->title = $form->getValue('title');
            $row->save();

            $this->_redirect('/');
        } else {
            $form->populate($formData);
        }
    } else {
        // album id is expected in $params['id']

        $id = (int)$this->_request->getParam('id', 0);
        if ($id > 0) {
            $albums = new Albums();
            $album = $albums->fetchRow('id='.$id);
            $form->populate($album->toArray());
        }
    }
}
...

Рассмотрим отличия от добавления альбома. Во-первых, когда форма впервые показывается пользователю, мы должны извлечь имя исполнителя и заголовок альбома из БД и установить их как значения соответствующих элементов формы:

1
2
3
4
5
6
7
// album id is expected in $params['id']
$id = (int)$this->_request->getParam('id', 0);
if ($id > 0) {
    $albums = new Albums();
    $album = $albums->fetchRow('id='.$id);
    $form->populate($album->toArray());
}

Если не были посланы POST-данные мы используем значения из базы данных. Класс Zend_Db_Table_Row имеет метод toArray(), который мы можем использовать для заполнения формы.
Ещё одно отличие – мы не записываем новые данные в БД, а сохраняем изменения. Эту задачу выполняет следующий код:

1
2
3
$albums = new Albums();
$id = (int)$form->getValue('id');
$row = $albums->fetchRow('id='.$id);

Шаблон вида аналогичен add.phtml:
zf-tutorial/application/views/scripts/index/edit.phtml

1
<?php echo $this->form; ?>

Теперь вы можете добавлять и редактировать альбомы.

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

Для завершения нашего приложения мы должны добавить возможность удаления альбомов. Мы уже имеем ссылку “Удалить” для каждого альбома на странице вывода и просто переходим к удалению при клике по ней. Это неверно. Мы не должны делать это действие необратимым и используем метод POST вместо GET.
Мы должны показать подтверждение удаления, когда пользователь переходит по ссылке “Удалить”. Если он нажмёт “yes” – мы удаляем альбом. Так как эта форма простая, мы создадим HTML-код в скрипте вида.
Начнём с кода действия:
zf-tutorial/application/controllers/IndexController.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
function deleteAction()
{
    $this->view->title = "Delete Album";

    if ($this->_request->isPost()) {
        $id = (int)$this->_request->getPost('id');
        $del = $this->_request->getPost('del');
        if ($del == 'Yes' && $id > 0) {
            $albums = new Albums();
            $where = 'id = ' . $id;
            $albums->delete($where);
        }
        $this->_redirect('/');
    } else {
        $id = (int)$this->_request->getParam('id');
        if ($id > 0) {
            $albums = new Albums();
            $this->view->album = $albums->fetchRow('id='.$id);
        }
    }
}
...

Мы используем метод объекта окружения isPost() для определения, стоит ли нам отобразить форму подтверждения или удалить альбом, используя класс Album(). Удаление производится вызовом метода delete() класса Zend_Db_Table. Если POST-данные не переданы, тогда мы проверяем наличие параметра id и, если он соответствует записи в БД, передаём эту запись в скрипты вида.
Скрипт вида будет содержать простую форму:
zf-tutorial/application/views/scripts/index/delete.phtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php if ($this->album) :?>
    <>Are you sure that you want to delete
        '<?php echo $this->escape($this->album->title); ?>' by
        '<?php echo $this->escape($this->album->artist); ?>'?
    </>
    < action="<?php echo $this->url(array('action'=>'delete')); ?>" method="post">
        <>
            < type="hidden" name="id" value="<?php echo $this->album->id; ?>" />
            < type="submit" name="del" value="Yes" />
            < type="submit" name="del" value="No" />
        </>
    </>
<?php else: ?>
    <>Cannot find album.</>
<?php endif;?>

В этом скрипте мы выводим сообщение для подтверждения удаления, которое содержит кнопки “Yes” и “No”. В нашем действии мы удаляем запись, если была нажата кнопка “Yes”.
Теперь вы имеете полностью работоспособное приложение.

Решение проблем

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

1
2
3
4
5
6
7
8
...
// setup controller
$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setControllerDirectory('../application/controllers');
$frontController->setBaseUrl('/mysubdir/zf-tutorial/public');
Zend_Layout::startMvc(('layoutPath'=>'../application/layouts'));
...

Вы должны заменить ‘/mysubdir/zf-tutorial/public’ на корректный путь до index.php. Например, если ваш URL до index.php – http://localhost/~ralle/zf-tutorial/public/index.php, тогда корректное значение $baseUrl будет ‘/~ralle/zf-tutorial/public’.

Итоги

В результате мы создали простое, но полнофункциональное MVC-приложение с использованием Zend Framework. Я надеюсь, что вы нашли в этом руководстве много интересного. Если вы найдёте какие-либо ошибки, пожалуйста, сообщите мне на email (rob [dog] akrabat.com)!
Это руководство позволит вам лишь взглянуть на основы использования фреймворка, классов гораздо больше, чем мы рассмотрели. Вы можете почитать и взглянуть на для более подробного изучения. Если вам интересна разработка фреймворка, можете взглянуть на .
И последнее. Если вы предпочитаете печатные страницы, я в настоящее время пишу книгу под названием Zend Framework in Action, которая уже доступна для предзаказа. Подробности на сайте .

77 Comments

  1. dd says:

    В заголовке красуется “начало работы”, долго пытался понять смысл использования фреймворка. Может у Вас пример неудачный? На чистом пхп всё это приложение – 1 файл на 80 строк максимум. Ну и в процессе чтения куда-то теряется смысл :(

  2. Сергей says:

    Посмотрите пример и решите для себя, как вам удобнее. Если смысла вы не видите – значит вам или действительно удобнее без него, или же недостаточно опыта, чтобы со всех сторон оценить фреймворк.
    Приложение из этого руководства – оно только для примера. Фреймворки предназначены для более масштабных проектов.

  3. NEKT says:

    Не уверен что смогу оценить всю прелесть фреймворка с высоты своего опыта.

    Хотя я только начинаю разбираться с Zend Framework, но что-то мне подсказывает, что в нем те фишки, которые я давно полюбил и использую навроде сквозной нумерации записей во всех таблицах базы (а лучше сквозная нумерация объектов в базе) осуществляется через пятое отверстие восьмого интерфейса…

  4. Сергей Митрошин says:

    NEKT, расскажите, пожалуйста, что есть сквозная нумерация записей во всех таблицах.
    А по поводу сабжа – не обязательно использовать все компоненты ZF. Не нравится Zend_Db – не проблема, работаем с Doctrine/Propel, или вообще используем родные PHP-расширения.

  5. FeniX says:

    Спасибо за статью. Одна из самых вменяемых из всех, которые я нашел для начала работы с zend freamework. =)

  6. SpyLive says:

    Давно искал чтото подобное, уже больше 3 лет пишу на php и настало время когда захотелось перейти на ZF, и это одна из лучших статей для начала понимая процесса. Спасибо.

  7. Longmayar says:

    Вот блин, целый час проблему искал… :) ))

    После создания BaseUrl.php страничка стала выдавать вот такую вот ошибку:

    Fatal error: Uncaught exception ‘Zend_Loader_PluginLoader_Exception’ with message ‘Plugin by name ‘BaseUrl’ was not found in the registry; used paths: Zend_View_Helper_: Zend/View/Helper/;../application/views\helpers/’

    Оказывается в BaseUrl.php я пропустил открытие <?php

    Жесть :)

    А туториал очень хороший. Спасибо.

  8. Maxim says:

    Longmayar, спасибо за подсказку )

  9. dreamer says:

    Для начинающих очень хороший туториал. Только чего-то пару блоков кода не показал (файл со стилями и с удалением). С исходного кода страницы можно восстановить. Большое спасибо.

  10. Сергей Митрошин says:

    dreamer, спасибо за багрепорт. Исправил.

  11. saintist says:

    Статья то что нужно
    Спасибо, очень просто и понятно все разложено

  12. Chips says:

    Первая вменяемая статья по ZF. Спасибо, хотя бы стало понятно как работает это всё

  13. Bob says:

    Помогите, плиз.. никак не могу понять в чем дело. После создания в Models файла Albums.php выдает постоянно Fatal error: Class ‘Albums’ not found in C:\Program Files\Zend\Apache2\htdocs\zftest\application\controllers\IndexController.php on line 19
    Заранее спасибо.

  14. Сергей Митрошин says:

    Возможно, в include_path вы неверно указали путь к каталогу моделей.

  15. Bob says:

    хм…нет, вроде все правильно подключаю…
    set_include_path(‘.’ . PATH_SEPARATOR . ‘../library/’
    . PATH_SEPARATOR . ‘../application/models’
    . PATH_SEPARATOR . get_include_path());
    require_once ‘Zend/Controller/Front.php’;
    require_once ‘Zend/Layout.php’;
    может можно как-нибудь скриншот сбрость??

  16. Сергей Митрошин says:

    Да, точно. Он нашёл файл, но не нашёл класс. Если, конечно, у вас error_reporting = E_ALL
    Вы класс назвали неверно, видимо.

  17. Bob says:

    хм…все равно осталась…
    index.php

    error_reporting(E_ALL );

    set_include_path(‘.’ . PATH_SEPARATOR . ‘../library/’
    . PATH_SEPARATOR . ‘../application/models/’
    . PATH_SEPARATOR . get_include_path());
    require_once ‘Zend/Controller/Front.php’;
    require_once ‘Zend/Layout.php’;

    require ‘Zend/Loader/Autoloader.php’;
    Zend_Loader_Autoloader::getInstance();

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

    $db = Zend_Db::factory($config->db);
    Zend_Db_Table::setDefaultAdapter($db);

    // Setup controller
    $controller = Zend_Controller_Front::getInstance();
    $controller->setControllerDirectory(‘../application/controllers’);
    $controller->setBaseUrl(‘/zftest/public’);
    $controller->throwExceptions(true); // should be turned on in development time

    //bootstrap layouts
    Zend_Layout::startMvc(array(
    ‘layoutPath’ => ‘../application/layouts’,
    ‘layout’ => ‘main’
    ));

    // run!
    $controller->dispatch();

    Albums.php
    class Albums extends Zend_Db_Table
    {
    protected $_name = ‘albums’;
    }

  18. Baron says:

    Блин все супер. Но у меняпочему-то данные при добавлении новых данных сохраняет а при редактировании(обновлении) не хочет обновлять((

  19. Сергей Митрошин says:

    Baron, с такой постановкой вопроса можно только гадать, что у вас не работает.

  20. Sanpedro says:

    Спасибо, за добротный тоториал…

  21. xen says:

    Спасибо)) Очень нужна была именно такая инфа)))

  22. zoro says:

    BOB и Сергей Митрошин у меня такая же проблема … выходит ошибка что не найден класс Albums… вы смогли понять эту проблему

    Fatal error: Class ‘Albums’ not found in /home/www/ilovezf/application/controllers/IndexController.php on line 7

    делаю все как в статье

  23. STan says:

    Аналогичная ситуация:
    Fatal error: Class ‘Albums’ not found in C:\Program Files\Apache Group\Apache2\htdocs\zend\application\controllers\IndexController.php on line 7

    Все уже несколько раз перепроверил, все как в туториале…

  24. Csbwalker says:

    По поводу
    “Fatal error: Class ‘Albums’ not found”
    Возникла проблема при изменении результата QuickStart с официального сайта.
    У меня (возможно у Вас по другому) в application/Bootstrap.php было прописано
    protected function _initAutoload()
    {
    $autoloader = new Zend_Application_Module_Autoloader(array(
    ‘namespace’ => ‘Default_’,
    ‘basePath’ => dirname(__FILE__),
    ));
    return $autoloader;
    }
    И названия моделей должны были быть Default_Model_ИМЯМОДЕЛИ, соответственно после изменения:
    (Albums.php)
    class Albums extends Zend_Db_Table
    на
    class Default_Model_Albums extends Zend_Db_Table
    и в (IndexController.php)
    public function indexAction()
    {
    $this->view->title = “My Albums”;
    $albums = new Albums();
    $this->view->albums = $albums->fetchAll();
    }
    на
    public function indexAction()
    {
    $this->view->title = “My Albums”;
    $albums = new Default_Model_Albums();
    $this->view->albums = $albums->fetchAll();
    }

    Попробуйте, возможно, Вы были невнимательны в том же месте, что и я.

  25. jdex says:

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

  26. jdex says:

    Фильтр убрал названия полей. Речь идет о полях “name” и “artist” соответственно.

  27. Dima says:

    Только начал осваивать Zend – дали админить сайт и первая проблема с ним, что сайт не индексируется поисковиками, не работает на нем функция GetContent “failed to open stream: HTTP request failed! HTTP/1.1 500 Internal Server Error in”? сам сайт открывается корректно. Подскажите в каком направлении думать?

  28. rust says:

    А у меня вот какая ошибка

    Fatal error: Uncaught exception ‘Zend_Db_Table_Exception’ with message ‘No adapter found for Albums’ in Y:\home\test1.ru\www\ZendFramework\library\Zend\Db\Table\Abstract.php:667 Stack trace:

    Тестируется на denver 3

    может config.ini както подругому нужно написать

  29. Антон says:

    Warning: require_once(Zend/Loader.php) [function.require-once]: failed to open stream: No such file or directory in Z:\home\zf-tutorial\www\public\index.php on line 8

    Fatal error: require_once() [function.require]: Failed opening required ‘Zend/Loader.php’ (include_path=’.;../library/;../application/models;.;/usr/local/php5/PEAR’) in Z:\home\zf-tutorial\www\public\index.php on line 8

    Скажите на что он жалуется ??

  30. Антон says:

    Хе все уже понял … тм нужно было прописать вместо
    include “Zend/Loader.php”;
    Zend_Loader::registerAutoload();

    следующее:
    require_once ‘Zend/Loader/Autoloader.php’;
    $autoloader=Zend_Loader_Autoloader::getInstance();
    $autoloader->setFallbackAutoloader(true);

  31. Антон says:

    да теперь у меня та же проблема что и у rust

  32. Сергей Митрошин says:

    rust, Антон: видимо надо прописать следующее: Zend_Db_Table::setDefaultAdapter($dbAdapter);

  33. Антон says:

    Сергей не поверите! … просто нужно знать какой пароль на Danwere 3 :) ) …
    там тупо его нет а я зачем то admin ввожу !

    Но спасибо за уделённое внимание! …

  34. Сергей Митрошин says:

    :)
    Вообще да, defaultAdapter сам определяется иногда. В закономерности ещё не разобрался (думаю, что в определённой версии сделали), предпочитаю указывать вручную.

  35. Антон says:

    И еще один вопросик :( … фот как сделать фильтр, что бы он не экранировал кавычки, а переводил в бинарный код?
    типо stripcslashes

  36. Антон says:

    мдя я чукча можно перед выводом просто вставить
    escape($album->title));?>

  37. миша says:

    выдается ошибка
    Fatal error: Uncaught exception ‘Zend_Controller_Dispatcher_Exception’ with message ‘”erotics” controller does not exist’ in /home….(путь)
    в файле ry/Zend.php on line 229

    вэтой линии код
    static public function exception($class, $message = ”, $code = 0)
    {
    $class = (string) $class;

    self::loadClass($class);

    $exception = new $class($message, $code);

    if (!$exception instanceof Exception) {
    throw new Zend_Exception(‘Invalid exception class used in Zend::exception()’);
    }

    return $exception;
    }

    скрипт создает страницу и она из -за исключений не работает:(

  38. Сергей Митрошин says:

    Михаил, исключение выбрасывается из-за того, что скрипт не может найти ваш контроллер. Приведённый вами код здесь совершенно не причём.

  39. Антон says:

    а не поскажете, как проверять на валидность и русский шрифт без формы а при помощи Zend_Filter?
    а то чет у меня с Zend_Filter_Input не выходит как – то …:(
    Спасибо!

  40. Сергей Митрошин says:

    Для обработки входных данных без формы предназначен Zend_Filter_Input, иначе – Zend_Form.
    Остальные классы компонента Zend_Filter предназначены исключительно для фильтрации, к валидации они отношения не имеют.

  41. Антон says:

    Ясно спасибо :) …а то я чую просто вырезает и все :)

  42. Filipok says:

    Здравствуйте. Не плохой туториал. Но у меня закралась по какой то причине не работает удаление, изменение и добавление новых записей.
    Исправление ошибок читал, всё сделал как было написано, указал явно путь до index.php но это не помогает.


    выдаёт
    Not Found

    The requested URL /zf-tutorial/public/index/add was not found on this server.

    а если сделать вот так

    то всё работает но при этом не работают стили, да и не правильно так как мне кажеться.

  43. Filipok says:

    разобрался сам, только есть одно но, у меня дирректорию с CSS воспринимает как контроллер и как следствие ошибки и не пашут стили

  44. Filipok says:

    всем спасибо, со всем разобрался 8)

  45. Strategist says:

    Здравствуйте, подскажите кто-нибудь пожалуйста в чем еще может быть решение указанной выше проблеммы При обращении на выводит: class Albums extends Zend_Db_Table { protected $_name = ‘albums’; }
    Fatal error: Class ‘Albums’ not found in Z:\home\localhost\www\zf-tutorial\application\controllers\IndexController.php on line 7
    lib скачал именно Zend Framework 1.7 Preview Release, все вышеуказанные советы не помогли

  46. Влад says:

    У меня та же проблема, что и у Strategist:
    Fatal error: Class ‘Albums’ not found in Z:\home\route\www\application\controllers\IndexController.php on line 7

    Не могу понять, что не так. Класс лежит в application/models/в файле Albums.php и называется соответственно – Albums.
    echo get_include_path(); выводит .;../library/;../application/models/;.;/usr/local/php5/PEAR
    Не могу найти причину ошибки. Что делать? Помогите советом.

  47. Cергей says:

    “Попробуйте набрать в вашем браузере URL’ы наших контроллеров/действий и увидете заголовки страниц (title), указанные нами в действиях.” – юрл по кофейной гуще угадать? Все перепробовал. Результат – нот фаунд.

  48. Сергей Митрошин says:

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

  49. Cергей says:

    Да, это не агрессия, а больше раздражёность от невозможности найти верный ответ. Да мод_реврайт стоит. пхпинфо проверил.

  50. Сергей Митрошин says:

    Покажите, пожалуйста, ваши правила mod_rewrite из .htaccess

  51. Cергей says:

    можете написать как должен выглядит юрл(полный адресс) на основании примера. Ибо я даже сайт переименовал под название в примере (с перенастройкой эрор логов и прочего).

  52. Cергей says:

    # Rewrite rules for Zend Framework
    RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule .* index.php

    # Security: Don’t allow browsing of directories
    Options -Indexes

    # PHP settings
    php_flag magic_quotes_gpc off
    php_flag register_globals off
    php_flag short_open_tag on

  53. Сергей Митрошин says:

    URL должен выглядеть так: для IndexController editAction

  54. Cергей says:

    – объект не найден;

    “Notice: Zend_Loader::Zend_Loader::registerAutoload is deprecated as of 1.8.0 and will be removed with 2.0.0; use Zend_Loader_Autoloader instead in C:\xampp\htdocs\zf-tutorial\www\library\Zend\Loader.php on line 206
    My Albums” Так хоть что-то выдаёт. указывая путь полный. Но с нотисом. В чём может быть дело?

  55. Сергей Митрошин says:

    В вашем случае надо использовать
    рекомендую изучить основы создания VirtualHost в apache
    По поводу Notice – всё так и есть, Zend_Loader::registerAutoload() deprecated, т.е. не рекомендуется к использованию. Статья старая, поэтому есть некоторые отличия от последних версий ZF.

  56. Cергей says:

    да, совершенно верно! Спасибо за оперативные ответы, на данном этапе разобрался!

  57. Сергей Митрошин says:

    Пожалуйста. Будут вопросы – обращайтесь.

  58. Cергей says:

    Глава “Список альбомов”
    не удалось отобразить. По ссылке – “http://localhost/zf-tutorial/” выдаёт древо каталогов, а по ссылке “http://localhost/zf-tutorial/public” –
    Fatal error: Class ‘Albums’ not found in C:\xampp\htdocs\zf-tutorial\application\controllers\IndexController.php on line 8. Т.е. класс не видит.

  59. Cергей says:

    всё, разобрался!

  60. Сергей Митрошин says:

    В чём была проблема?

  61. Strategist says:

    Да уж на само деле все было просто, я все это проделывал на denwer и чтоб все нормально заработало надо было public переименовать в www

  62. rad says:

    Notice: Zend_Loader::Zend_Loader::registerAutoload is deprecated as of 1.8.0 and will be removed with 2.0.0; use Zend_Loader_Autoloader instead in /srv/www/library/Zend/Loader.php on line 223
    Если Вы видите эту надпись на странице, советую заглянуть сюда

  63. Сергей Митрошин says:

    Скоро будет готов перевод новой версии этого руководства, для текущих версий ZF.

  64. Юрий says:

    О, жду перевода для свежего ZF. Скорей бы и спасибо за статью.

  65. Nataly says:

    И все же, когда будет готова новая версия руководств, например, для ZF 1.9.6&

  66. Сергей Митрошин says:

    Будет готова на этой неделе.

  67. Nataly says:

    Славно, спасибо.

  68. Anton says:

    По-моему в последней версии ZF класс Zend_Application порядком изменился так, что статья уже устарела. Хотелось бы увидеть этот же туториал для новой философии. Кстати в комплекте ZF идет утилита ZendTools, которая позволяет генерировать каркас приложения.

  69. Сергей Митрошин says:

    Несколько часов назад был опубликован перевод новой версии руководства: http://zend-framework.ru/getting-started/

  70. Anton says:

    Ой, слона не заметил…

  71. Andy says:

    очень доступно написано, автор красавчегг)))

  72. ананас says:

    Я скачал и установил Zend Server, выбрал при установке “установить apache” , но не знаю, где установщик создал корневой каталог для сервера, вследствие чего даже не знаю, где искать то зендовское приложение, которое у меня сейчас запускается на локалхосте. Но папки zf-tutorial у меня на компе нету. Помогите, плиз!

  73. skillu says:

    У меня такая же проблема как и у Filipok. Т.е. при пути (например) выдет ошибку:
    —————————————————————————-
    Fatal error: Uncaught exception ‘Zend_Controller_Dispatcher_Exception’ with message ‘Invalid controller specified (zf_prac)’ in Z:\home\localhost\www\ZF_prac\library\Zend\Controller\Dispatcher\Standard.php:248 Stack trace: #0 Z:\home\localhost\www\ZF_prac\library\Zend\Controller\Front.php(954): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http)) #1 Z:\home\localhost\www\ZF_prac\public\index.php(20): Zend_Controller_Front->dispatch() #2 {main} thrown in Z:\home\localhost\www\ZF_prac\library\Zend\Controller\Dispatcher\Standard.php on line 248
    —————————————————————————–

    Но если прописать путь

    то все работает !

    Хотелось бы узнать в чем проблема ?

  74. luda says:

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

  75. Amaton says:

    Хотел бы освежить вопрос Люды… посоветуйте более широкие примеры, желательно с разбором.
    П.С. Совет тем кто “у меня и денвер и что куда писать и как настраивать”, ИМХО рановато вам копать и зенд и подобные архитектуры.

  76. HGepe says:

    Отличная статья! Дает хорошее понимание концепции MVC применимо к ZendFramework.
    Буду читат дальше.
    Автору РЕСПЕКТ!!!

  77. Max says:

    Действительно хорошая статья,для новичка самое оно,но все же можно было бы чуть больше комментариев к коду добавить,а так супер.

Leave a Reply

info@zend-framework.ru