место в рейтинге
  • 69645
  • 223
  • 36
Нравится блог?
Подписывайтесь!

Zend Framework: создаем блог. Шаг 1

Аллоха, ювижинцы! Ниже наглая копипаста с моего стандалона. Понеслась.

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

Я решил пропустить традиционное "Hello, World!", выбрав не менее традиционный блог :). На его примере, думаю можно хорошенько разобрать многие более-менее используемые компоненты фреймворка.

Итак, составим нечто вроде ТЗ, чтобы ясно представлять себе будущий функционал.

  • Список постов на главной с постраничной навигацией. При клике на заголовок, должна открываться полная версия. Каждый пост имеет свою категорию. Категории выводятся где-нибудь в сайдбаре и при клике должен выводится список постов этой категории, также с постаничной навигацией.
  • К каждому посту можно оставить комментарий.
  • Аутентификация администратора.
  • CRUD категорий, постов и комментов. Админки как таковой не будет, просто админу будут выводится ссылки на CRUD.

В принципе все. Эдакий simple blog. Если у вас имеются какие-нибудь предложения по дополнительной функциональности - велкам в комменты. Теперь приступим к самому вкусному - кодингу. Далее предпологается, что вы уже установили и создали свой проект при момощи Zend_Tool. Теперь взгянем на сгенерированную структуру проекта.

Структура проекта Zend Framework

С непривычки такое обилие папок может запутать и напугать. Но немного привыкнув, все станет понятно и логично. В configs хранится конфигурация вашего проекта, в controllers, models, views, как можно догадаться 3 составляющие части MVC - контроллеры, модели и вьюшки. В docs документация, в library дистрибутив ZF (если вы последовали совету из предыдущего поста, то эта папка у вас должна быть пуста, т.е. она и так будет пуста, пусть такой и остается), в public вся доступная извне часть вашего приложения (CSS-стили, JS-скрипты, картинки, etc), в tests всякие тесты. Скрытый файл .zfproject.xml в корне - описание структуры вашего приложения.

Ну-с, со структурой более-менее разобрались, двигаемся дальше. Попытаюсь на пальцах объяснить принцип работы ZF. В каталоге public лежат два файла - .htaccess и index.php. Первый перенаправляет все запросы на единственный входной файл index.php, который реализует паттерн Front Controller. В index.php обьявляются всякие нужные константы, типа корня приложения. Далее создается экземпляр класса Zend_Application, у которого вызываются поочередно методы bootstrap() и run(). Вдаваться, что делают эти методы мы пока не будем, потому что я и сам толком не знаю, но если коротко, то инициализируют начальные настройки и запускают роутер (маршрутизатор). В корне каталога приложения application лежит один файл Bootstrap.php. Это класс, наследующийся от Zend_Application_Bootstrap_Bootstrap. Он используется для действий при запуске приложения. Далее в configs лежит файл конфигурации application.ini. Данные в нем хранятся в формете .ini и используют наследование секций. В первой секции production хранятся настройки production-приложения, т.е. уже запущенного реального проекта. Остальные секции наследуются от production и служат для тестирования и разработки соответственно. Давайте занесем наши данные для подключения к БД. В конец секции production добавляем:

 

 

  1. ; DB config
  2. resources.db.adapter = PDO_MYSQL
  3. resources.db.params.host = localhost
  4. resources.db.params.username = root
  5. resources.db.params.password =
  6. resources.db.params.dbname = zftest
  7. resources.db.params.charset = utf8

 

Думаю здесь все понятно. Обычные данные для подключения к БД. Последней стоит кодировка. Мы будем юзать utf8. Также если у вас Денвер, поменяйте в httpd.cong windows-1251 на utf8. В редакторе кода, кстати тоже установите эту кодировку. Стоит отметить, что мы будем использовать MySQL через PDO, так что у вас должен быть установлен соотвествующий модуль. У меня по умолчанию установлено PDO_MySQL, на Денвере по моему PDO_SQLite. Так что посмотрите вывод фунции phpinfo() и доустановите отсутствующие модули. Да, и еще: комментарии в .ini файлах ставятся через точку с запятой (;), что удобно в отличие например от XML, где комментариев нет. Теперь давайте взглянем на структура БД нашего блога:

 

  1. CREATE TABLE IF NOT EXISTS `zf_posts` (
  2. `id` MEDIUMINT(5) UNSIGNED NOT NULL AUTO_INCREMENT,
  3. `category_id` TINYINT(2) UNSIGNED NOT NULL,
  4. `url` VARCHAR(100) NOT NULL,
  5. `title` VARCHAR(100) NOT NULL,
  6. `blog_post` MEDIUMTEXT NOT NULL,
  7. `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  8. PRIMARY KEY (`id`)
  9. ) ENGINE = MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
  10.  
  11. CREATE TABLE IF NOT EXISTS `zf_categories` (
  12. `id` TINYINT(2) UNSIGNED NOT NULL AUTO_INCREMENT,
  13. `url` VARCHAR(100) NOT NULL,
  14. `title` VARCHAR(100) NOT NULL,
  15. PRIMARY KEY (`id`)
  16. ) ENGINE = MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
  17.  
  18. CREATE TABLE IF NOT EXISTS `zf_comments` (
  19. `id` MEDIUMINT(7) UNSIGNED NOT NULL AUTO_INCREMENT,
  20. `post_id` MEDIUMINT(5) UNSIGNED NOT NULL,
  21. `name` VARCHAR(50) NOT NULL,
  22. `email` VARCHAR(50) NOT NULL,
  23. `url` VARCHAR(50),
  24. `blog_comment` TEXT NOT NULL,
  25. `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  26. PRIMARY KEY (`id`)
  27. ) ENGINE = MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;

 

Этот файл с полным дампом БД под названием schema.sql, вы найдете в прилагаемом архиве, в папке docs. Здесь у нас имеются 3 таблицы: посты, категории и комменты. На них останавливаться не будем.

Начнем с моделей. Здесь будут храниться наши CRUD-методы. Переходим в директорию models и создаем в ней новый каталог DbTable. В нем будут храниться наши модели. Стоит рассказать принцип именования моделей. Каждая модель соответствует одной таблице в БД. Называть класс нужно по назвнию таблицы с префиксом Model. Например наша модель постов будет называться Application_Model_DbTable_Posts. Здесь Application это каталог нашего приложения, Model папка с моделями, DbTable папка где храняться модели наших таблиц и наконец Posts - название таблицы. Довольно длинное название, но если вы ознакомились со стандартами кодинга Zend Framework, а я настоятельно рекомендую сделать это, то вы поймете, что к чему. Сделано это для имитации пространства имен. В PHP 5.3 наконец появилась нативная поддержка неймспейсов, думаю в будущих версиях ZF начнут их применять. Нам нужно получать пост по ID, для вывода отдельного поста. Вот листинг модели постов:

 

  1. <?php
  2.  
  3. /**
  4.  * Posts model
  5.  *
  6.  * @author Kanat Gailimov, http://gailimov.info
  7.  * @copyright Copyright (c) Kanat Gailimov (http://gailimov.info) 2011
  8.  */
  9.  
  10. class Application_Model_DbTable_Posts extends Zend_Db_Table_Abstract
  11. {
  12. /**
  13.   * Db table name
  14.   *
  15.   * @var string
  16.   */
  17. protected $_name = 'zf_posts';
  18.  
  19. /**
  20.   * Get post by ID
  21.   *
  22.   * @param int $id ID of post
  23.   * @return array
  24.   */
  25. public function getById($id)
  26. {
  27. $id = intval($id);
  28. $row = $this->fetchRow('id = ' . $id);
  29. if (!$row) {
  30. throw new Exception('Ахтунг! Выборка поста не удалась :(');
  31. }
  32. return $row;
  33. }
  34. }

 

Код подробно прокомментирован, но думаю пояснить все же стоит. Первым делом создаем класс, наследующийся от Zend_Db_Table_Abstract. Делее создаем protected свойство, в котором будет храниться название нашей таблицы. Затем создаем метод для выборки постов по ID'шнику, в который передаем этот самый ID'шник. Для этого используе метод fetchRow() класса Zend_Db_Table_Abstract. Этот метод возращает одну строку в таблице.

Займемся контроллерами. По умолчанию ZF генерирует для нас два файла с контроллерами: ErrorController.php и IndexController.php. Первый для обработки ошибок, второй собственно для отображения страниц. URL'ы в ZF строятся таким образом: http://sitename.loc/controller/action/params, где controller - контроллер, action - метод или как его еще называют экшен (действие), params - параметры. Контроллеры именуются по схеме - ControllerNameController, т.е. в camel case (при том первая буква заглавная) и плюс в конце обязательно добавляется Controller. Сам класс называется точно так же как файл, т.е. здесь не действуют стандартные имитаторы пространств имен ZF, и наследуется от Zend_Controller_Action. Методы именуются по схеме methodAction, т.е. название метода в нижнем регистре плюс слово Action. Мы будем использовать сгенерированный indexController. В нем уже содержатся два метода init() и indexAction(). Нам также нужен еще один метод для просмотра отдельного поста. Можно объявить его в коде ручками, но мы воспользуемся Zend_Tool. Вы спросите почему? Ну чтобы показать как можно генерировать методы, и еще Zend_Tool помимо самих экшенов создаст нам соответствующие вьюшки. Итак, открываем консоль, переходим в директорию с проектом и вводим команду:

 

  1. zf create action post index

 

Здесь post - название экшена (будет postAction()), index - контоллер. В каталоге application/views/scrips/index/ появится файл post.phtml. Это вид для нашего экшена. В ZF вьюшки это php-файл, с окончанием .phtml. Но о видах потом. Сейчас давайте посмотрим на код нашего контроллера:

 

  1. <?php
  2.  
  3. class IndexController extends Zend_Controller_Action
  4. {
  5.  
  6. public function init()
  7. {
  8. // Уставливаем название блога
  9. $this->view->title = 'Тестовый блог на Zend Framework';
  10. // Устанавливаем разделитель для тега title с помощью хелперов
  11. // headTitle() и setSeparator()
  12. $this->view->headTitle()->setSeparator(' | ');
  13. // Передаем заголовок в тег title, с помошью хелпера headTitle()
  14. $this->view->headTitle($this->view->title);
  15. }
  16.  
  17. public function indexAction()
  18. {
  19. // Создаем экземпляр модели постов
  20. $posts = new Application_Model_DbTable_Posts();
  21. // Выбираем все посты
  22. // Формируем условие
  23. $select = $posts->select()->order('created_at DESC')
  24. ->order('id DESC');
  25. // Выполняем запрос
  26. $this->view->posts = $posts->fetchAll($select);
  27. }
  28.  
  29. /**
  30.   * View post
  31.   *
  32.   * @return void
  33.   */
  34. public function postAction()
  35. {
  36. // Берем ID'шник из параметра
  37. $id = intval($this->_getParam('id', 0));
  38. if ($id > 0) {
  39. // Создаем экземпляр модели постов и выбираем посты по ID
  40. $post = new Application_Model_DbTable_Posts();
  41. $this->view->post = $post->getById($id);
  42. // Устанавливаем заголовок для поста в тег title
  43. $this->view->postTitle = $this->view->post['title'];
  44. $this->view->headTitle($this->view->postTitle);
  45. }
  46. }
  47.  
  48. }

 

Поясню код. Сначала обьявляем наш класс IndexController, наследующийся от Zend_Controller_Action. Далее объявляем открытые методы. init() - это действия, которые будут выполняться всегда. Типа констуктора. Сюда мы устанавливаем заголовок блога и разделитель. indexAction() - действие для главной страницы. Здесь мы выбираем посты с помощью метода fetchAll() класса Zend_Db_Table_Abstract. postAction() - показ отдельного поста. Здесь мы получаем параметр с помощью метода _getParam() и, если ID больше нуля выбираем пост, с помощью нашего ранее созданного метода getPostById($id).

Настало время третьей составляющей паттерна MVC, а именно view. За вид в Zend Framework отвечает компонент Zend_View. Мы для удобства будем использовать layout'ы, т.е. макеты в нашем виде. Фишка layout'ов, в том, что не нужно в каждом файле представления писать что-то типа include 'inc/header.php'. Вместо этого создается отдельный файл в котором описывается главный шаблон. В нужных местах прописываются переменные, в которые будут помещаться динамические части. Чтобы создать лайаут воспользуемся Zend_Tool. Прописываем в консоли команду:

 

  1. zf enable layout

 

Эта команда создаст каталог application/layout/scripts и поместит в него файл layout.phtml. Также в application.ini будет добавлена строчка:

 

  1. resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts/"

 

Приведу код нашего макета:

 

  1. <!DOCTYPE html>
  2. <meta charset=utf-8>
  3. <!--[if IE]>
  4. <script>
  5. document.createElement('header');
  6. document.createElement('nav');
  7. document.createElement('section');
  8. document.createElement('article');
  9. document.createElement('aside');
  10. document.createElement('footer');
  11. </script>
  12. <![endif]-->
  13. <link rel="stylesheet" href="<?php echo $this->baseUrl() ?>/css/style.css" media="screen" />
  14. <?php echo $this->headTitle() ?>
  15. </head>
  16. <div id="wrapper">
  17. <header id="header">
  18. <h1><a href="<?php echo $this->baseUrl() ?>"><?php echo $this->title ?></a></h1>
  19. </header>
  20. <?php echo $this->layout()->content ?>
  21. <aside>
  22. <section>
  23. <header>
  24. <h4>Категории</h4>
  25. </header>
  26. <ul>
  27. <li><a href="#">Первая категория</a></li>
  28. <li><a href="#">Вторая категория</a></li>
  29. </ul>
  30. </section>
  31. </<footer>
  32. <p>© <a href="http://gailimov.info">Канат Гайлимов</a> <?php echo date('Y') ?></p>
  33. </footer>
  34. </div> <!-- wrapper -->
  35. </body>
  36. </html>

 

Здесь объявляется стандартный доктайп HTML 5. Конечно, можно воспользоваться хелпером ZF, но я предпочел написать ручками. Далее подключаются css-файл, код которого я приводить не буду, но он будет включен в архив. Заметьте, здесь используется хелпер baseUrl(), который "знает" путь к каталогу нашего приложения. Так что, где бы ни был расположен наш блог, все пути будут правильными. headTitle() - возравщает заголовок нашего блога в теге title. Помните, в контроллере мы устанавливали значение для него? Самое интересное происходит в строке:

 

  1. <?php echo $this->layout()->content ?>

 

Сюда будет подставляться наш основной контент. Давайте теперь взглянем на файл со списком постов для главной (views/scripts/index/index.phtml):

 

  1. <section id="content">
  2. <section id="posts">
  3. <?php if (count($this->posts) < 1) : ?>
  4. <article>
  5. <p>Посты закончились</p>
  6. </article>
  7. <?php else : foreach ($this->posts as $post) : ?>
  8. <article>
  9. <header>
  10. <h2><a href="<?php echo $this->url(array('controller' => 'index', 'action' => 'post', 'id' => $post->id)) ?>"><?php echo $this->escape($post->title) ?></a></h2>
  11. </header>
  12. <?php echo $post->blog_post ?>
  13. <p class="date"><?php echo $this->escape($post->created_at) ?></p>
  14. </article>
  15. <?php endforeach; endif ?>
  16. </section> <!-- posts -->
  17. </section> <!-- content -->

 

Здесь ничего сложного, обычная проверка на массива с постами и вывод в цикле foreach. Новымы можгут быть лишь хелперы url() и escape(), служащие для построения ссылок и escape() для экранирования HTML-тегов. В параметры первому передается ассоциативный массив со значениями контроллера, экшена и параметры экшена.

А вот вьюха для отдельного поста (views/scripts/index/post.phtml):

 

  1. <section id="content">
  2. <section id="posts">
  3. <article>
  4. <header>
  5. <h2><?php echo $this->post['title'] ?></h2>
  6. </header>
  7. <?php echo $this->post['blog_post'] ?>
  8. <p class="date"><?php echo $this->post['created_at'] ?></p>
  9. </article>
  10. </section> <!-- posts -->
  11. </section> <!-- content -->

 

Теперь наконец можете набрать в браузере адрес своего проекта и посмотреть результат. В следующем посте мы сделаем вывод категорий и постов в них. Может и комментарии захватим. Так что подписывайтесь на RSS. Архив с исходниками можно забрать отсюда.

P.S. Критика, разрыв поста в клочья приветствуется :).

Канат Гайлимов KanatGailimov
Мальчик-красавчик
12 февраля 2011, 15:29
2285

Загрузка...
Loading...

Комментарии

Нафига эти большие оступы между текстом и кодом?
xapon
0
0
Нормально, но как-то слишком стандартно и обычно, такие вещи в каждом первом туториале есть же :)
HTML5 — ок :3
ну я типа учусь еще пока :)
ошибочку нашел
В каталоге application/views/scrips/error/index/ появится файл post.phtml.
правильный путь не должен содержать пункт "error".

а вообще очень благодарен:)
спасибо, поправил :)

Оставьте свой комментарий

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

Также можно нажать Ctrl+Enter

Популярные посты

Инструкция для аллергиков. Как бороться с аллергией в период обострения

Инструкция для аллергиков. Как бороться с аллергией в период обострения

Я аллергик с детства. Имею аллергию на пыльцу березы, липы, полыни (выяснил это благодаря кожным пробам), а также пищевую аллергию на горчицу. Свои проблемы знаю, однако это меня не спасло.
Romeo_17
вчера / 17:21
  • 38014
  • 62
Алматы предложили сделать центром секс-туризма

Алматы предложили сделать центром секс-туризма

Известный политолог России Андрей Карпов предложил сделать Алматы центром секс-туризма. Но для этого сперва нужно легализовать проституцию в стране.
tala03
13 авг. 2017 / 14:48
Балконы Алматы. Интересные экземпляры встречаются только на старых домах

Балконы Алматы. Интересные экземпляры встречаются только на старых домах

Ещё один отчёт, который очень хотел сделать. Посмотрим на старые алматинские балконы. Хоть у нас и нет шедевров, но кое-что можно снять и выставить.
Ispanec
10 авг. 2017 / 0:04
  • 2847
  • 9
Американцы хотят напасть на КНДР, но не знают, где находится эта страна

Американцы хотят напасть на КНДР, но не знают, где находится эта страна

В США 75% жителей считают, что КНДР является "критической угрозой" для их страны. Авторы популярного американского шоу Джимми Кимела решили проверить жителей США, знают ли те, где находится КНДР.
tala03
10 авг. 2017 / 16:48
  • 2642
  • 62
Ограбление года: 3 млн долларов и 50 тысяч евро украли у казахстанского экс-министра

Ограбление года: 3 млн долларов и 50 тысяч евро украли у казахстанского экс-министра

КТК: "У жителя Алматы украли… 3 миллиона долларов! В деле, которое расследует сейчас местная полиция, удивляет всё – и внушительная сумма, и фамилия пострадавшего..."
SamJamKZ
10 авг. 2017 / 11:43
  • 2387
  • 42
Учителям, владеющим английским языком, повысят зарплату на 200%

Учителям, владеющим английским языком, повысят зарплату на 200%

В Казахстане началось поэтапное внедрение трехъязычного образования. По данным МОН РК, с 2017 учебного года начнется внедрение трехъязычия в 5-х классах.
tala03
10 авг. 2017 / 15:20
  • 1963
  • 31
Приложение казахстанских школьниц признали лучшим на конкурсе в Сан-Франциско

Приложение казахстанских школьниц признали лучшим на конкурсе в Сан-Франциско

Команда алматинских школьниц выиграла главный приз международного конкурса Technovation Challenge 2017 по разработке мобильных приложений.
yviNews
11 авг. 2017 / 16:38
  • 2238
  • 5
Недоразумение с грантами в ВУЗы: «медалисты» до сих пор имеют преимущество

Недоразумение с грантами в ВУЗы: «медалисты» до сих пор имеют преимущество

Многие способные выпускники без Алтын Белги готовились к тестированию, чтобы в честной борьбе попытать счастья на гранты без ущемления со стороны якобы "золотых" выпускников.
DanaJarlygapova
14 авг. 2017 / 14:35
Кандидат в президенты Польши просит место жительства в Казахстане

Кандидат в президенты Польши просит место жительства в Казахстане

Балли Мажец, ранее Балжан Наурызбаева, уроженка Южно-Казахстанской области уже 30 лет проживает в Польше. Является гражданской активисткой и председателем ассоциации Wspolnota Kazachska.
tala03
10 авг. 2017 / 0:25
  • 1412
  • 11