13.3 Создание блога
- 13.3.1 Введение
- 13.3.2 Установка
- 13.3.3 Создание Блог приложения
- 13.3.4 Создание моделей
- 13.3.5 Создание записи в блоге
- 13.3.6 Routers
- 13.3.7 Просмотр записи в блоге
13.3.1 Введение
В этом разделе мы опишем процесс создания блог приложения на MZZ: от установки MZZ до состояния готового к использованию приложения. Мы заранее подготовили прототип дизайна блога.

В процессе создания приложения будут созданы две сущности - post, postComment. Из стандартных модулей мы будем использовать только модуль user. Во фреймворке уже имеется готовый модуль комментариев, но, так, как цель данного руководства показать весь процесс написания приложения, мы реализуем комментарии еще раз, с более простой функциональностью.
Наш блог будет обладать следующими действиями: список записей (list), создание записи (create), редактирование записи (edit), удаление записи (delete), создание комментария (comment), список комментариев (comments), удаление комментария (commentDelete).
13.3.2 Установка
Процесс установки самого фреймворка очень прост. Для это необходимость скачать MZZ с Download раздела на официальном сайте и распаковать его в любую директорию.
Код является кроссплатформеным и работает под всеми операционными системами и веб серверами.
13.3.3 Создание Блог приложения
Скачайте dummy (пустой) проект с Download и распакайте его в любую директорию, которая доступна веб-серверу.
Структура типичного веб приложения на MZZ выглядит следующим образом:
| Имя файла | Описание |
|---|---|
| db/ | дампы базы данных приложения |
| files/ | файлы, хранимые модулем fileManager |
| modules/ | модули приложения |
| routes/ | роуты приложения |
| templates/ | шаблоны |
| tmp/ | временные файлы |
| www/ | корневая директория приложения |
| application.php | класс приложения |
| config.php | основная конфигурация приложения |
Корневая директория приложения (www/) - это директория доступная для чтения веб-браузером. В ней хранятся все js/css-файлы, картинки и прочий общедоступный контент. Структура этой директории выглядит так:
| Имя файла | Описание |
|---|---|
| css/ | файлы стилей |
| images/ | файлы изображений |
| js/ | файлы javascript |
| index.php | точка входа в приложение, именно этому файлу передаётся управление |
Мы установили сам фреймворк в директорию /home/MZZ, приложение будет установлено в /home/MZZ/app, веб-сервер настроен на директорию - /home/MZZ/app/www. Адрес нашего проекта будет http://MZZ.blog/.
При необходимости, если фреймворк установлен в отличной от нашего примера директории, в файле config.php можно изменить путь к фреймворку.
define('SYSTEM_PATH', realpath(dirname(__FILE__) . '/../system/'));
Также включим DEBUG-режим (т.к. мы будем писать новый модуль, нам необходимо видеть все ошибки) в том же файле.
define('DEBUG_MODE', true);
tmp директорию.
chown <user>:<group> -R /home/MZZ/app/tmp
chmod 755 -R /home/MZZ/app/tmp
3.1 Конфигурация базы данных
Откроем db/mzz_dummy.sql и изменим имя базы данных.
/*!40000 DROP DATABASE IF EXISTS `MZZ_blog`*/; CREATE DATABASE /*!32312 IF NOT EXISTS*/ `MZZ_blog` /*!40100 DEFAULT CHARACTER SET utf8 */; USE `MZZ_blog`;
Импортируем измененный дамп dummy-базы в MySQL:
mysql -u user -ppassword < /home/MZZ/app/db/mzz_dummy.sql
И изменим настройки базы данных все в том же config.php.
systemConfig::$db['default']['dsn'] = 'mysql:host=localhost;dbname=MZZ_blog'; systemConfig::$db['default']['user'] = 'root'; systemConfig::$db['default']['password'] = '';
Если все было сделано правильно и dummy приложение установлено, по адресу http://MZZ.blog/ мы увидим некоторый hello текст.
Hello, world! Привет, мир! Модуль: dummy Экшн: hello
13.3.4 Создание моделей
Теперь нам необходимо создать модуль блог, структуру его директорий и, собственно, сами модели. Для этих целей в MZZ реализован web-интерфейс для управления модулями. Зайти в него можно через административный интерфейс по адресу http://MZZ.blog/admin/ используя стандартый логин и пароль (admin / test). Далее необходимо выбрать пункт меню Администрирование -> Утилиты разработчика. В дальнейшем под словом devToolbar мы будем подразумевать именно этот интерфейс.
Для создания модуля надо нажать на иконку рядом с "Модули и классы". Имя для нового модуля: blog. Каталог генерации: каталог проекта (по умолчанию). Сохраним изменения и, если все успешно, мы увидим текст "Модуль blog успешно создан". Перейдем к моделям.
Модель Post будет содержать 5 свойств: идентификатор (name), заголовок (title), содержимое (content) и дату создания (created_at). Выполним запрос в mysql для создания таблицы:
CREATE TABLE `MZZ_blog`.`blog_post` ( `name` VARCHAR(255) NOT NULL, `title` VARCHAR(255) NOT NULL, `content` TEXT NOT NULL, `created_at` DATETIME NOT NULL, PRIMARY KEY (`name`) ) ENGINE = MyISAM;
В devToolbar, в секции модуля blog, создадим класс post. Имя таблицы blog_post.
В MZZ, действие (action) должно принадлежать какому-либо определенному объекту. Но что делать с действиями list и create? Для этого мы создаем фейковый объект и называем его, как правило, именем модуля или <имяМодуля>Folder. Создавать таблицу в базе данных необходимости нет, поэтому просто создадим класс postFolder для blog и укажем несуществующую таблицу blog_postFolder.
Для комментариев создадим только один объект postComment. Они тоже имеют действия, не связанные с конкретным комментарием, это comments и comment, но в нашем случае для этих целей вполне подойдет объект post, тем более, что комментирование относится к постам в блоге. Начем с создания таблицы в базе данных:
CREATE TABLE `MZZ_blog`.`postComment` ( `id` INT NOT NULL , `post_id` INT NOT NULL , `author_name` VARCHAR( 255 ) NOT NULL , `content` TEXT NOT NULL , `created_at` INT NOT NULL , PRIMARY KEY ( `id` ) , INDEX ( `post_id` ) ) ENGINE = MYISAM ;
Создаем класс postComment для модуля blog в devToolbar указав имя таблицы blog_postComment.
13.3.5 Создание записи в блоге
В MZZ встроен механизм скаффолдинга, который позволяет генерировать готовый к использованию код для стандартных действией над объектом (CRUD - Create, Retrive, Update, Delete). Действия Create и Update чаще всего очень похожи, поэтому мы их объединили в одно Save действие. Здесь надо заметить, что использование CRUD подходит только в довольно общих и простых случаях, и получившийся код в любом случае необходимо дорабатывать под себя.
Первое действие, которое мы создадим, будет создание записи в блоге (create). Как было написано ранее, действие должно принадлежать существующему объекту, поэтому оно будет принадлежать классу postFolder. Создаем это действие в devToolbar указав CRUD: save и CRUD class: post (т.к. на самом деле это действие для создания объекта типа post)
По адресу http://MZZ.blog/blog/create находится автоматически сгенерированная форма для добавления записи. Отредактируем ее чуть-чуть.
Откроем файл шаблона modules/blog/templates/create.tpl и приведем нашу формочку в более приятный вид. В MZZ есть генератор форм, который генерирует HTML-код для различных элементов форм, выставляет дефолтные значения и восстанавливает введеные пользоваталем данные в форме в случае ошибки заполнения формы и отображает ее:
<h3>{if $isEdit}Edit{else}Create{/if} a post</h3> {form action=$form_action method="post"} <table width="100%" border="0" cellpadding="5" cellspacing="0" align="center"> <tr> <td>{form->caption name="post[name]" value="Name"}</td> <td> {form->text name="post[name]" size="30" value=$post->getName()} {if $validator->isFieldError('post[name]')}<div class="error">{$validator->getFieldError('post[name]')}</div>{/if} </td> </tr> <tr> <td>{form->caption name="post[title]" value="Title"}</td> <td> {form->text name="post[title]" size="30" value=$post->getTitle()} {if $validator->isFieldError('post[title]')}<div class="error">{$validator->getFieldError('post[title]')}</div>{/if} </td> </tr> {if !$isEdit} <tr> <td>{form->caption name="post[created_at]" value="Created at"}</td> <td> {form->text name="post[created_at]" size="30" value=$smarty.now|date_format:"%H:%M:%S %d/%m/%Y"} {if $validator->isFieldError('post[created_at]')}<div class="error">{$validator->getFieldError('post[created_at]')}</div>{/if} </td> </tr> {/if} <tr> <td valign="top">{form->caption name="post[content]" value="Content"}</td> <td> {form->textarea name="post[content]" rows="10" cols="45" value=$post->getContent()} {if $validator->isFieldError('post[content]')}<div class="error">{$validator->getFieldError('post[content]')}</div>{/if} </td> </tr> <tr> <td> </td> <td>{form->submit name="submit" value="_ simple/save"}</td> </tr> </table> </form>
Сделаем пару изменений в контроллере modules/blog/controllers/blogCreateController.php. Здесь мы можем изменить валидаторы для нашей формы. Сейчас они выглядит так:
$validator->rule('required', 'post[title]', 'Field title is required'); $validator->rule('length', 'post[title]', 'Field title is out of length', array(0, 255)); $validator->rule('required', 'post[content]', 'Field content is required'); $validator->rule('required', 'post[created_at]', 'Field created_at is required');
Нам они не очень походят, поэтому заменим их на:
$validator->rule('required', 'post[title]', 'Field title is required'); $validator->rule('required', 'post[name]', 'Field name is required'); $validator->rule('required', 'post[content]', 'Field content is required'); $validator->rule('required', 'post[created_at]', 'Field created_at is required'); $validator->rule('regex', 'post[name]', 'Field name has illegal characters', '#^[a-z\d-_]+$#i'); $validator->rule('length', 'post[name]', 'Field name is out of length', array(0, 255)); $validator->rule('length', 'post[title]', 'Field title is out of length', array(0, 255)); $validator->rule('date', 'post[created_at]', 'Invalid creation date', array('regex' => 'time_date')); $validator->rule('callback', 'post[name]', 'Post name is not unique', array(array($this, 'checkUniquePostName'), $post));
Перечисляя по порядку, валидаторы определяют следующее: поля title, name, content и created_at обязательны для заполнения, name может содержать только символы a-z, A-Z, 0-9, - и _ (так как это идентификатор), name и title имеют длину не более чем 255 символов, created_at имеет формат вида 14:43:32 20/12/2009 (вместо значения time_date может быть любое указано регулярное выражение для валидации формата даты, мы будем использовать стандарный вид даты для русской локали). Последний валидатор является callback функцией, для валидации значения будет вызван метод checkUniquePostName у текущего контроллера. Используется он для проверки того, что запись с таким же именем уже не существует. Добавим этот метод в blogCreateController.
public function checkUniquePostName($name, $post) { if ($name == $post->getName()) { return true; } $postMapper = $this->toolkit->getMapper('blog', 'post'); return is_null($postMapper->searchByKey($name)); }
Добавим установку имени для записи:
$post->setName($data['name']); $post->setTitle($data['title']); $post->setContent($data['content']); $post->setCreatedAt($data['created_at']);
Также изменим способ редиректа после сохранения записи.
return jipTools::redirect();
Наше действие не будет выполняться как JIP-действие (об этом мы расскажем позже) поэтому заменим на:
$url = new url('withAnyParam'); $url->add('name', $post->getName()); $url->add('action', 'view'); return $this->redirect($url->get());
Класс url генерирует готовые ссылки используя роутер withAnyParam и переданные в него значения. Метод $this->redirect() выполняет HTTP-редирект пользователя на указанную страницу (просмотр созданной записи).
Чтобы сохранить дату создания в правильном формате, добавим метод в modules/blog/models/post.php
public function setCreatedAt($value) { $datetime = explode(' ', $value); $date = explode('/', $datetime[1]); $datetime = implode('-', array_reverse($date)) . ' ' . $datetime[0]; parent::__call('setCreatedAt', array($datetime)); }

13.3.6 Routers
Маршрутизация (Routing) - это процесс поиска подходящего роута и превращения с его помощью запрошенного пути в обычный массив. Правила маршрутизации хранятся в файле <project_folder>/routers/default.php и могут быть переопределены. Так же у модулей могут быть указаны свои правила маршрутизации, которые могут быть описаны в классе модуля в файле modules/<module>/<module>Module.php.
В modules/blog/blogModule.php мы добавим простой роут обработки путей для конкретной записи в блоге:
public function getRoutes() { return array( array(), array( 'blogPost' => new requestRoute('blog/:name/:action', array( 'module' => 'blog', 'action' => 'view'), array( 'name' => '.*?', 'action' => '(?:create)')))); }
Этот метод возвращает массив из двух элементов: первый содержит роуты, которые будут вызываны после других и второй - перед остальными. Этот простой роут определяет правила для разбора пути вида "blog/first_blog_entry". В MZZ по умолчанию определены некоторые стандартные роуты, в том числе и для обработки путей вида module/name/action (withAnyParam). Смысл создания нашего в том, чтобы роутер знал что "blog/create" это создание новой записи, а не просмотр записи с именем "create".
13.3.7 Просмотр записи в блоге
Если создание записи прошло успешно (для примера, мы создали запись с именем "welcome"), то после добавления мы увидим исключение Unknown action view in module blog. Создадим в devToolbar действие view для класса post выбрав CRUD: view.
По адресу http://MZZ.blog/ru/blog/welcome мы увидим 404 ошибку. Произошло это из-за того, что по умолчанию контроллер blogViewController сгенерировался для работы с числовым идентификатором (id):
$id = $this->request->getInteger('id'); $post = $postMapper->searchByKey($id);
В нашем же случае используется строковое имя для идентификации объекта (name). Исправим это:
$name = $this->request->getString('name'); $post = $postMapper->searchByKey($name);