Пример разработки блога на Zend Framework 2. Часть 2. Модуль MyBlog

Submitted by Ромка on Втр, 03/09/2013 - 16:33

Ромка аватар

Это вторая из трех частей статьи, посвященной разработке простого приложения при помощи Zend Framework 2. В первой статье я рассмотрел структуру ZendSkeletonApplication, а в этой части приведу пример разработки простого модуля. Третья часть будет посвящена работе с пользователями.

Установка и настройка дополнительных модулей

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

  1. добавляем соответствующую строчку в composer.json, чтобы сообщить Композеру о новом модуле,
  2. выполняем команду php composer.phar update, чтобы Композер загрузил новый модуль и при необходимости перегенерировал автолоад файлы,
  3. добавляем новый модуль в список modules в файле config/application.config.php,
  4. при необходимости, размещаем конфигурационный файл модуля (обычно пример такого файла находится в папке config модуля) в config/autoload и делаем в нем необходимые правки.

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

Давайте начнем с установки простого, но полезного модуля Zend Developer Tools.

Zend Developer Tools

Zend Developer Tools — это удобный тулбар, содержащий полезную для разработчика информацию о созданной странице: число и список запросов к БД, список ролей текущего пользователя, использованные Entity, загруженная конфигурация сайта и т.д. Разумеется, тулбар может быть расширен любой другой вспомогательной информацией. Найти его можно тут: https://github.com/zendframework/ZendDeveloperTools.

Чтобы установить тулбар сначала добавим строчку:

  1. "zendframework/zend-developer-tools": "dev-master",

в файл composer.json в корне проекта и затем выполним команду php composer.phar update в корне проекта.

Затем в файл config/application.config.php в массив modules нужно добавить элемент ZendDeveloperTools:

  1. 'modules' => array(
  2. 'Application',
  3. 'ZendDeveloperTools',
  4. ),

Теперь осталось скопировать файл vendor/zendframework/zend-developer-tools/config/zenddevelopertools.local.php.dist в папку config/autoload нашего проекта и переименовать его, например, в zenddevelopertools.local.php (часть имени до local.php по большому счету значения не имеет).

Всё, теперь, по умолчанию, внизу всех страниц выводится информация о затраченных на генерацию страницы ресурсах, конфигурация проекта и т.п.

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

Текущая версия приложения доступна на Гитхабе в репозитории проекта с тэгом zenddevelopertools: https://github.com/romka/zend-blog-example/tree/zenddevelopertools

Doctrine ORM

Для интеграции с Доктриной понадобятся модули DoctrineModule и DoctrineORMModule (https://github.com/doctrine/DoctrineModule и https://github.com/doctrine/DoctrineORMModule).

Добавим в секцию require файла composer.json строчки:

  1. "doctrine/common": ">=2.1",
  2. "doctrine/doctrine-orm-module": "0.7.*"

и выполним в консоли команду php composer.phar update.

Модуль DoctrineModule можно не указывать явно в нашем composer.json, так как эта зависимость прописана на уровне модуля DoctrineORMModule.

Теперь нужно в директории config/autoload разместить файл doctrine.local.php с параметрами доступа к БД, который будет использоваться Доктриной, его содержимое должно быть примерно таким:

  1. <?php
  2. return array(
  3. 'doctrine' => array(
  4. 'connection' => array(
  5. 'orm_default' => array(
  6. 'driverClass' =>'Doctrine\DBAL\Driver\PDOMySql\Driver',
  7. 'params' => array(
  8. 'host' => 'localhost',
  9. 'port' => '3306',
  10. 'user' => 'username',
  11. 'password' => 'pass',
  12. 'dbname' => 'dbname',
  13. )
  14. )
  15. ),
  16. ),
  17. );

Теперь если мы перезагрузим страницу нашего сайта, то внизу страницы в Zend девлопер тулбаре увидим два новых блока, показывающих число выполненных запросов и список маппингов к БД. Оба значения равны нулю, так как маппинги мы пока не сделали и, как следствие, запросов к базе нет.

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

Модуль MyBlog

В каталоге modules создадим следующие директории и файлы:

MyBlog/
    config/
        module.config.php
    src/
        MyBlog/
            Entity/
                BlogPost.php
    Module.php

Содержимое файла Module.php должно быть таким:

  1. <?php
  2. namespace MyBlog;
  3.  
  4. class Module
  5. {
  6. public function getAutoloaderConfig()
  7. {
  8. return array(
  9. 'Zend\Loader\StandardAutoloader' => array(
  10. 'namespaces' => array(
  11. __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
  12. ),
  13. ),
  14. );
  15. }
  16.  
  17. public function getConfig()
  18. {
  19. return include __DIR__ . '/config/module.config.php';
  20. }
  21. }

Файл аналогичен тому, что используется в модуле Application, мы говорим ядру фреймворка где искать конфигурационный файл модуля и файлы исходников.

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

Файл src/MyBlog/Entity/BlogPost.php является связью (маппингом) между Доктриной и базой данных и о нем нужно поговорить подробнее.

BlogPost.php

Каждый блогпост в моем примере будет содержать следующие поля:

  • заголовок,
  • тело блогпоста,
  • id автора (0 для анонимов),
  • статус (опубликовано/не опубликовано)
  • дата публикации.

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

Этот файл объявляет класс BlogPost, который содержит описания полей блогпоста и методов доступа к ним. Полную версию файла вы можете посмотреть на Гитхабе (https://github.com/romka/zend-blog-example/blob/master/module/MyBlog/src...), вот так выглядит его часть:

  1. <?php
  2. namespace MyBlog\Entity;
  3.  
  4. use Doctrine\Common\Collections\ArrayCollection;
  5. use Doctrine\ORM\Mapping as ORM;
  6.  
  7. class BlogPost
  8. {
  9. /**
  10.   * @var int
  11.   * @ORM\Id
  12.   * @ORM\Column(type="integer")
  13.   * @ORM\GeneratedValue(strategy="AUTO")
  14.   */
  15. protected $id;
  16.  
  17. /**
  18.   * @var string
  19.   * @ORM\Column(type="string", length=255, nullable=false)
  20.   */
  21. protected $title;
  22.  
  23. /**
  24.   * Get id.
  25.   *
  26.   * @return int
  27.   */
  28. public function getId()
  29. {
  30. return $this->id;
  31. }
  32.  
  33. /**
  34.   * Set id.
  35.   *
  36.   * @param int $id
  37.   *
  38.   * @return void
  39.   */
  40. public function setId($id)
  41. {
  42. $this->id = (int) $id;
  43. }
  44.  
  45. /**
  46.   * Get title.
  47.   *
  48.   * @return string
  49.   */
  50. public function getTitle()
  51. {
  52. return $this->title;
  53. }
  54.  
  55. /**
  56.   * Set title.
  57.   *
  58.   * @param string $title
  59.   *
  60.   * @return void
  61.   */
  62. public function setTitle($title)
  63. {
  64. $this->title = $title;
  65. }
  66.  
  67. }

Каждая переменная в этом классе станет полем в БД, параметры полей задаются в аннотациях, которые будут прочитаны Доктриной (примерно вот так: http://php.net/manual/en/reflectionclass.getdoccomment.php, класс Doctrine\Common\Annotations\AnnotationReader метод getClassAnnotations()).

Теперь в конфигурационный файл модуля config/module.config.php можно добавить информацию о нашей новой Entity, которая будет использована Доктриной:

  1. return array(
  2. 'doctrine' => array(
  3. 'driver' => array(
  4. 'myblog_entity' => array(
  5. 'class' =>'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
  6. 'paths' => array(__DIR__ . '/../src/MyBlog/Entity')
  7. ),
  8. 'orm_default' => array(
  9. 'drivers' => array(
  10. 'MyBlog\Entity' => 'myblog_entity',
  11. )
  12. )
  13. )
  14. ),
  15. );

И осталось добавить модуль MyBlog в список активных модулей в application.config.php.

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

./vendor/bin/doctrine-module orm:info

И результатом должно быть сообщение вида:

Found 1 mapped entities:
[OK]   MyBlog\Entity\BlogPost

После того как мы убедились в том, что Доктрина видит наш объект BlogPost выполним команду:

./vendor/bin/doctrine-module orm:validate-schema

В результате должна вернуться ошибка вида:

[Mapping]  OK - The mapping files are correct.
[Database] FAIL - The database schema is not in sync with the current mapping file.

Это и логично, так как наша база данных все еще пуста и сейчас мы создадим нужную таблицу командой:

./vendor/bin/doctrine-module orm:schema-tool:update --force

Её результатом будет следующий вывод:

Updating database schema...
Database schema updated successfully! "1" queries were executed

И теперь вызов команды:

./vendor/bin/doctrine-module orm:validate-schema

вернет результат:

[Mapping]  OK - The mapping files are correct.
[Database] OK - The database schema is in sync with the mapping files.

Если сейчас обновить страницу нашего сайта, то в тулбаре внизу страницы мы увидим, что Доктрина видит один маппинг Myblog\Entity\BlogPost.

Исходные коды текущей версии проекта можно найти в репозитории проекта на Гитхабе с тэгом blogpost_entity: https://github.com/romka/zend-blog-example/tree/blogpost_entity.

Теперь, когда у нас есть сущность для работы с блогпостами можно перейти к написанию своего первого контроллера, реализующего форму добавления блогпоста.

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

В директории src/MyBlog модуля создадим два новых каталога с файлами:

Controller/
    BlogController.php
Form/
    BlogPostForm.php
    BlogPostInputFilter.php

Далее в конфигурационный файл модуля нужно добавить элементы, объявляющие список контроллеров модуля, маршруты и путь к директории с шаблонами:

  1. 'controllers' => array(
  2. 'invokables' => array(
  3. 'MyBlog\Controller\BlogPost' => 'MyBlog\Controller\BlogController',
  4. ),
  5. ),
  6.  
  7. 'router' => array(
  8. 'routes' => array(
  9. ‘blog' => array(
  10. 'type' => 'segment',
  11. 'options' => array(
  12. 'route' => '/blog[/][:action][/:id]',
  13. 'constraints' => array(
  14. 'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
  15. 'id' => '[0-9]+',
  16. ),
  17. 'defaults' => array(
  18. 'controller' => 'MyBlog\Controller\BlogPost',
  19. 'action' => 'index',
  20. ),
  21. ),
  22. ),
  23. ),
  24. ),
  25.  
  26. 'view_manager' => array(
  27. 'template_path_stack' => array(
  28. __DIR__ . '/../view',
  29. ),
  30. ),

Исходя из заданных выше настроек, все страницы нашего блога будут иметь урлы вида blog/[action]/[id] (элементы пути в квадратных скобках не обязательны).

Файл BlogPostForm.php будет содержать форму, которая будет использоваться для добавления/редактирования блогпоста, давайте создадим эту форму.

BlogPostForm.php

В самом простом случае код формы будет выглядеть вот так (это не полный исходный код формы, целиком его можно увидеть тут: https://github.com/romka/zend-blog-example/blob/master/module/MyBlog/src...):

  1. class BlogPostForm extends Form
  2. {
  3. public function __construct($name = null)
  4. {
  5. parent::__construct('blogpost');
  6. $this->setAttribute('method', 'post');
  7. $this->add(array(
  8. 'name' => 'id',
  9. 'type' => 'Hidden',
  10. ));
  11. $this->add(array(
  12. 'name' => 'title',
  13. 'type' => 'Text',
  14. 'options' => array(
  15. 'label' => 'Title',
  16. ),
  17. 'options' => array(
  18. 'min' => 3,
  19. 'max' => 25
  20. ),
  21. ));
  22. $this->add(array(
  23. 'name' => 'text',
  24. 'type' => 'Textarea',
  25. 'options' => array(
  26. 'label' => 'Text',
  27. ),
  28. ));
  29. $this->add(array(
  30. 'name' => 'state',
  31. 'type' => 'Checkbox',
  32. ));
  33. $this->add(array(
  34. 'name' => 'submit',
  35. 'type' => 'Submit',
  36. 'attributes' => array(
  37. 'value' => 'Save',
  38. 'id' => 'submitbutton',
  39. ),
  40. ));
  41. }
  42. }

Очевидно, что этот код объявляет необходимые нам поля формы, но пока для них не заданы ни фильтры (позволяющие преобразовать входящие данные), ни валидаторы (которые не позволят ввести в форму данные в неправильном формате). Их мы зададим позже, а сейчас давайте напишем код контроллера, который будет выводить форму добавления блогпоста и сохранять введенные данные.

BlogController.php

Полный код контроллера вы можете посмотреть в репозитории (https://github.com/romka/zend-blog-example/blob/master/module/MyBlog/src...), ниже его ключевая часть:

  1. class BlogController extends AbstractActionController
  2. {
  3.  
  4. public function indexAction()
  5. {
  6. return new ViewModel();
  7. }
  8.  
  9. public function addAction()
  10. {
  11. $form = new \MyBlog\Form\BlogPostForm();
  12. $form->get('submit')->setValue('Add');
  13.  
  14. $request = $this->getRequest();
  15. if ($request->isPost()) {
  16. $form->setData($request->getPost());
  17.  
  18. if ($form->isValid()) {
  19. $objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
  20.  
  21. $blogpost = new \MyBlog\Entity\BlogPost();
  22.  
  23. $blogpost->exchangeArray($form->getData());
  24.  
  25. $blogpost->setCreated(time());
  26. $blogpost->setUserId(0);
  27.  
  28. $objectManager->persist($blogpost);
  29. $objectManager->flush();
  30.  
  31. // Redirect to list of blogposts
  32. return $this->redirect()->toRoute('blog');
  33. }
  34. }
  35. return array('form' => $form);
  36. }
  37. }

Значимым для нас является код экшена addAction (имена всех экшенов должны создаваться по маске nameAction()). В нем мы сначала создаем объект формы и заменяем текст кнопки submit на ней (у нас одна и та же форма будет использоваться и для создания, и для редактирования блогпостов, по этому тексты на этой кнопке удобно иметь разные):

  1. $form = new \MyBlog\Form\BlogPostForm();
  2. $form->get('submit')->setValue('Add');

Затем, в случае если форма прошла валидацию (а валидацию сейчас она пройдет в любом случае, так как валидаторов у нас пока нет) мы создаем экземпляр класса \MyBlog\Entity\BlogPost(), который является связью между нашим приложением и БД, наполняем созданный объект данными и сохраняем их в БД:

  1. $blogpost->exchangeArray($form->getData());
  2.  
  3. $blogpost->setCreated(time());
  4. $blogpost->setUserId(0);
  5.  
  6. $objectManager->persist($blogpost);
  7. $objectManager->flush();

Текущую версию шаблона, отвечающего за отображение формы, можно увидеть по ссылке https://github.com/romka/zend-blog-example/blob/blogpost_form_1/module/M....

Если cейчас попробовать сохранить пустую форму, то Доктрина вернет сообщение об ошибке вида:

An exception occurred while executing 'INSERT INTO blogposts (title, text, userId, created, state) VALUES (?, ?, ?, ?, ?)' with params [null, null, 0, 1377086855, null]:
 
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'title' cannot be null

Это правильно, ведь у нас есть только одно поле, помеченное как nullable="true" — это поле state, а все остальные должны быть заполнены. Давайте добавим к форме инпут фильтры и валидаторы, чтобы перехватывать подобные ошибки еще до попытки сохранить данные (на уровне нашего приложения, а не БД), чтобы у пользователя была возможность исправить ошибку.

Валидация форм

В ранее созданном файле BlogPostInputFilter.php разместим такой код (полная версия на Гитхабе: https://github.com/romka/zend-blog-example/blob/master/module/MyBlog/src...):

  1. class BlogPostInputFilter extends InputFilter
  2. {
  3. public function __construct()
  4. {
  5. $this->add(array(
  6. 'name' => 'title',
  7. 'required' => true,
  8. 'validators' => array(
  9. array(
  10. 'name' => 'StringLength',
  11. 'options' => array(
  12. 'min' => 3,
  13. 'max' => 100,
  14. ),
  15. ),
  16. ),
  17. 'filters' => array(
  18. array('name' => 'StripTags'),
  19. array('name' => 'StringTrim'),
  20. ),
  21.  
  22. ));
  23.  
  24. $this->add(array(
  25. 'name' => 'text',
  26. 'required' => true,
  27. 'validators' => array(
  28. array(
  29. 'name' => 'StringLength',
  30. 'options' => array(
  31. 'min' => 50,
  32. ),
  33. ),
  34. ),
  35. 'filters' => array(
  36. array('name' => 'StripTags'),
  37. array('name' => 'StringTrim'),
  38. ),
  39. ));
  40.  
  41. $this->add(array(
  42. 'name' => 'state',
  43. 'required' => false,
  44. ));
  45. }
  46. }

Предполагаю, что смысл этих строк должен быть интуитивно понятным: для полей title и text мы добавляем инпут фильтры, которые удалят из текстов все html тэги (фльтр StripTags) и обрежут пробелы по краям строк (StrinTrim), а также добавляем валидаторы, определяющие значения минимальной и максимальной длины полей (StringLength).

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

  1. $this->setInputFilter(new \MyBlog\Form\BlogPostInputFilter());

Теперь форма не пройдет валидацию, если в нее введены некорректные данные.

View плагины

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

Добавить такие сообщения можно с помощью методов:

  1. $this->flashMessenger()->addMessage($message);
  2. $this->flashMessenger()->addErrorMessage($message);

Извлечь сообщения, добавленные таким способом можно в контроллере или в phtml-шаблонах таким образом:

  1. $object->flashMessenger()->getMessages();
  2. $object->flashMessenger()->getErrorMessages();

Проблема в том, что неудобно (а в Twig-шаблонах, которые мы будем использовать позже, и вовсе невозможно) вызывать PHP-код для вывода сообщений. По этому мы напишем небольшой View-плагин, который сможет одной строчкой выводить на экран все сообщения.

Для этого в директории src\MyBlog модуля создадим такие директории и файлы:

View\
    Helper\
        ShowMessages.php

Содержимое ShowMessages.php можно посмотреть тут: https://github.com/romka/zend-blog-example/blob/master/module/MyBlog/src..., оно не очень интересно, я здесь просто получаю список сообщений, форматирую и возвращаю готовый html-код для их отображения.

Осталось сделать три действия:

  1. зарегистрировать View-плагин,
  2. добавить его использование в шаблон,
  3. и вывести сообщения о успешном/неудачном сохранении формы.

Чтобы зарегистрировать плагин добавим в настройки модуля в сецкию view_helper => invokables строчку:

  1. 'view_helpers' => array(
  2. 'invokables' => array(
  3. 'showMessages' => 'MyBlog\View\Helper\ShowMessages',
  4. ),
  5. ),

В шаблоны добавим вывод сообщений:

  1. print $this->showMessages();

Для вывода сообщений на экран добавим в контроллер такие строчки:

  1. $message = 'Blogpost succesfully saved!';
  2. $this->flashMessenger()->addMessage($message);

Теперь у нас есть возможность выводить пользователю системные сообщения.

Эту версию приложения вы можете найти в гит-репозитории с тэгом blogpost_form_1: https://github.com/romka/zend-blog-example/tree/blogpost_form_1.

На текущем этапе у нас есть:

  1. сущность для связи приложения и БД, созданная при помощи Доктрины,
  2. контроллер обслуживающий страницу добавления блогпоста,
  3. форма добавления блогпоста с инпут фильтрами и валидаций,
  4. свой кастомный View-плагин для вывода сообщений на экран.

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

Страница блогпоста

Добавим в контроллер BlogpostController новое действие view:

  1. public function viewAction()
  2. {
  3. $id = (int) $this->params()->fromRoute('id', 0);
  4. if (!$id) {
  5. $this->flashMessenger()->addErrorMessage('Blogpost id doesn\'t set');
  6. return $this->redirect()->toRoute('blog');
  7. }
  8.  
  9. $objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
  10.  
  11. $post = $objectManager
  12. ->getRepository('\MyBlog\Entity\BlogPost')
  13. ->findOneBy(array('id' => $id));
  14.  
  15. if (!$post) {
  16. $this->flashMessenger()->addErrorMessage(sprintf('Blogpost with id %s doesn\'t exists', $id));
  17. return $this->redirect()->toRoute('blog');
  18. }
  19.  
  20. $view = new ViewModel(array(
  21. 'post' => $post->getArrayCopy(),
  22. ));
  23.  
  24. return $view;
  25. }

Этот экшен доступен по адресу blog/view/ID. В нем мы сначала мы проверяем, что в URL’е задан id блогпоста, если это не так, то возвращаем ошибку и редиректим пользователя на страницу со списком блогпостов. Затем извлекаем пост из базы и передаем его в шаблон.

В качестве имени шаблона по умолчанию используется имя контроллера, по этому теперь в директории модуля view/my-blog/blog нужно создать файл view.phtml с примерно вот таким содержимым:

  1. <?php
  2. print $this->showMessages();
  3. print '<h1>' . $post['title'] . '</h1>';
  4. print '<div>' . $post['text'] . '</div>';
  5. <code>
  6. <h3>Список блогпостов</h3>
  7. Обновим код нашего indexAction до такого вида:
  8. <code>
  9. public function indexAction()
  10. {
  11. $objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
  12.  
  13. $posts = $objectManager
  14. ->getRepository('\MyBlog\Entity\BlogPost')
  15. ->findBy(array('state' => 1), array('created' => 'DESC'));
  16.  
  17. $view = new ViewModel(array(
  18. 'posts' => $posts,
  19. ));
  20.  
  21. return $view;
  22. }

Здесь мы выбираем все опубликованные блогпосты (state == 1), сортируем их по дате публикации и передаем в шаблон index.phtml https://github.com/romka/zend-blog-example/blob/blogpost_form_2/module/M.... В шаблоне выводятся заголовки блогпостов и ссылки на их редактирование и удаление.

Небольшое отступление

Выше, при создании формы я забыл добавить поле userId, в котором хранится айдишник автора блогпоста. Так как сейчас регистрации/авторизации в нашем блоге нет, это поле по умолчанию заполняется нулем, но в будущем оно пригодится, по этому сейчас я добавил в форму hidden поле userId.

Кроме того, я добавил к форме Csrf-токен (поле security), который должен защитить форму от подделки. По умолчанию этот токен формируется на основании пользовательской сессии и соли и живет 300 секунд (Zend\Form\Element\Csrf.php), но может быть (и по хорошему должен быть) переопределен и к нему как минимум должна быть добавлена зависимость от ip посетителя.

Редактирование блогпоста

Для редактирования поста мы будем использовать уже существующую форму. В контроллере необходимо создать действие editAction(), которое будет создавать форму, наполнять её существующими данными и отдавать пользователю. Этот экшен является смесью addAction(), в части работы с формой, и viewAction(), в части выборки данных https://github.com/romka/zend-blog-example/blob/blogpost_form_2/module/M....

Вот самая интересная часть этого контроллера:

  1. if ($form->isValid()) {
  2. $objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
  3.  
  4. $data = $form->getData();
  5. $id = $data['id'];
  6. try {
  7. $blogpost = $objectManager->find('\MyBlog\Entity\BlogPost', $id);
  8. }
  9. catch (\Exception $ex) {
  10. return $this->redirect()->toRoute('blog', array(
  11. 'action' => 'index'
  12. ));
  13. }
  14.  
  15. $blogpost->exchangeArray($form->getData());
  16.  
  17. $objectManager->persist($blogpost);
  18. $objectManager->flush();
  19.  
  20. $message = 'Blogpost succesfully saved!';
  21. $this->flashMessenger()->addMessage($message);
  22.  
  23. // Redirect to list of blogposts
  24. return $this->redirect()->toRoute('blog');
  25. }

Здесь мы загружаем из БД блогпост, основываясь на id, который пришел в форме, обновляем данные:

  1. $blogpost->exchangeArray($form->getData());

и кладем обновленный блогпост в базу:

  1. $objectManager->persist($blogpost);
  2. $objectManager->flush();

Удаление блогпостов

Удаление блогпоста задача тривиальная, достаточно вывести пользователю форму с вопросом вида ”Действительно ли вы хотите удалить пост?” и в случае если пользователь нажмет кнопку “Да”, выполнить соответствующие действия.

Код соответствующего контроллера и шаблона можно посмотреть на Гитхабе: https://github.com/romka/zend-blog-example/blob/blogpost_form_2/module/M....

Исходники с тэгом blogpost_form_2 (https://github.com/romka/zend-blog-example/tree/blogpost_form_2) содержат формы редактирования и удаления блогпоста, список постов и соответствующие шаблоны.

На этом я бы хотел завершить вторую часть статьи. В третьей части мы займёмся работой с пользователями.