В этой главе мы создадим страницу Add New Post, которая позволит добавлять новые посты в блог. Для этого нам понадобится следующее:
PostForm
, которая будет использоваться для задания и валидации заголовка поста, содержимого, статуса и тегов;PostManager
, которая будет содержать бизнес-логику для сохранения нового поста в БД;PostController
и его метод действия PostController::addAction()
, которые будут использоваться
для получения данных формы и вызова PostManager` для сохранения данных в БД;Сначала добавим форму PostForm
, которая позволит вводить данные одного поста:
его заголовок, содержимое, список связанных с ним тегов, разделенных запятыми, и
статус (опубликован или черновик). Чтобы это сделать, создайте файл PostForm.php
в каталоге Form под корневым каталогом модуля. Поместите в этот файл следующий код:
<?php
namespace Application\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilter;
use Application\Entity\Post;
/**
* Эта форма используется для сбора данных о посте.
*/
class PostForm extends Form
{
/**
* Конструктор.
*/
public function __construct()
{
// Определяем имя формы.
parent::__construct('post-form');
// Задает для этой формы метод POST.
$this->setAttribute('method', 'post');
$this->addElements();
$this->addInputFilter();
}
/**
* Этот метод добавляет элементы к форме (поля ввода и кнопку отправки формы).
*/
protected function addElements()
{
// Добавляем поле "title"
$this->add([
'type' => 'text',
'name' => 'title',
'attributes' => [
'id' => 'title'
],
'options' => [
'label' => 'Title',
],
]);
// Добавляем поле "content"
$this->add([
'type' => 'textarea',
'name' => 'content',
'attributes' => [
'id' => 'content'
],
'options' => [
'label' => 'Content',
],
]);
// Добавляем поле "tags"
$this->add([
'type' => 'text',
'name' => 'tags',
'attributes' => [
'id' => 'tags'
],
'options' => [
'label' => 'Tags',
],
]);
// Добавляем поле "status"
$this->add([
'type' => 'select',
'name' => 'status',
'attributes' => [
'id' => 'status'
],
'options' => [
'label' => 'Status',
'value_options' => [
Post::STATUS_PUBLISHED => 'Published',
Post::STATUS_DRAFT => 'Draft',
]
],
]);
// Добавляем кнопку отправки формы
$this->add([
'type' => 'submit',
'name' => 'submit',
'attributes' => [
'value' => 'Create',
'id' => 'submitbutton',
],
]);
}
/**
* Этот метод создает фильтр входных данных (используется для фильтрации/валидации).
*/
private function addInputFilter()
{
$inputFilter = new InputFilter();
$this->setInputFilter($inputFilter);
$inputFilter->add([
'name' => 'title',
'required' => true,
'filters' => [
['name' => 'StringTrim'],
['name' => 'StripTags'],
['name' => 'StripNewlines'],
],
'validators' => [
[
'name' => 'StringLength',
'options' => [
'min' => 1,
'max' => 1024
],
],
],
]);
$inputFilter->add([
'name' => 'content',
'required' => true,
'filters' => [
['name' => 'StripTags'],
],
'validators' => [
[
'name' => 'StringLength',
'options' => [
'min' => 1,
'max' => 4096
],
],
],
]);
$inputFilter->add([
'name' => 'tags',
'required' => true,
'filters' => [
['name' => 'StringTrim'],
['name' => 'StripTags'],
['name' => 'StripNewlines'],
],
'validators' => [
[
'name' => 'StringLength',
'options' => [
'min' => 1,
'max' => 1024
],
],
],
]);
$inputFilter->add([
'name' => 'status',
'required' => true,
'validators' => [
[
'name' => 'InArray',
'options'=> [
'haystack' => [Post::STATUS_PUBLISHED, Post::STATUS_DRAFT],
]
],
],
]);
}
}
Как видите из этого фрагмента, класс PostForm
определяет ZF3-форму с полями заголовка
содержимого, тегов и статуса. У него также есть кнопка Submit.
Так как мы уже детально рассмотрели формы в предыдущих главах, мы не будем разбирать представленный выше код.
Согласно паттерну DDD , мы размещаем бизнес-логику в моделях сервисов. В нашем примере Blog
мы создадим и зарегистрируем сервис PostManager. Этот сервис будет иметь public-метод
addNewPost()
, содержащий бизнес-логику добавления сущности Post
в БД и ее связи с одной
или несколькими сущностями Tag
.
Сервис
PostManager
будет содержать бизнес-логику примера Blog. Эта бизнес-логика включает в себя добавление нового поста в блог (но не ограничена этим).
Создайте файл PostManager.php в каталоге Service под корневым каталогом модуля. Поместите в этот файл следующий фрагмент кода:
<?php
namespace Application\Service;
use Application\Entity\Post;
use Application\Entity\Comment;
use Application\Entity\Tag;
use Zend\Filter\StaticFilter;
// Сервис The PostManager, отвечающий за дополнение новых постов.
class PostManager
{
/**
* Doctrine entity manager.
* @var Doctrine\ORM\EntityManager
*/
private $entityManager;
// Конструктор, используемый для внедрения зависимостей в сервис.
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
// Этот метод добавляет новый пост.
public function addNewPost($data)
{
// Создаем новую сущность Post.
$post = new Post();
$post->setTitle($data['title']);
$post->setContent($data['content']);
$post->setStatus($data['status']);
$currentDate = date('Y-m-d H:i:s');
$post->setDateCreated($currentDate);
// Добавляем сущность в менеджер сущностей.
$this->entityManager->persist($post);
// Добавляем теги к посту.
$this->addTagsToPost($data['tags'], $post);
// Применяем изменения к базе данных.
$this->entityManager->flush();
}
// Добавляет/обновляет теги в заданном посте.
private function addTagsToPost($tagsStr, $post)
{
// Удаляем связи тегов (если таковые есть)
$tags = $post->getTags();
foreach ($tags as $tag) {
$post->removeTagAssociation($tag);
}
// Добавляем теги к посту
$tags = explode(',', $tagsStr);
foreach ($tags as $tagName) {
$tagName = StaticFilter::execute($tagName, 'StringTrim');
if (empty($tagName)) {
continue;
}
$tag = $this->entityManager->getRepository(Tag::class)
->findOneByName($tagName);
if ($tag == null)
$tag = new Tag();
$tag->setName($tagName);
$tag->addPost($post);
$this->entityManager->persist($tag);
$post->addTag($tag);
}
}
}
В строках 25-43 находится public-метод addNewPost()
, принимающий в качестве аргумента переменную $data
(эта
переменная должна содержать данные, введенные пользователем сайта в форму). Мы создаем новый экземпляр сущности
Post
и заполняем его свойства введенными пользователем данными. Затем мы используем метод persist()
класса
EntityManager
(строка 36), чтобы добавить только что созданную сущность в менеджер сущностей. Private-метод
addTagsToPost()
вызывается (строка 39), чтобы добавить к посту один или несколько тегов, а метод flush()
-
чтобы применить изменения к базе данных в одно действие.
Private-метод addTagsToPost()
содержит алгоритмы для удаления старых связей между постом и тегами (строки 49-52),
разбора списка разделенных запятыми тегов (строка 55) и добавления новых тегов к посту (строки 56-73).
Далее, добавим фабрику для класса PostManager
. Для этого добавьте файл PostManagerFactory.php
под каталог Service/Factory под корневым каталогом модуля и заполните его следующим содержимым:
<?php
namespace Application\Service\Factory;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Application\Service\PostManager;
/**
* Это фабрика для PostManager. Ее целью является
* инстанцирование сервиса.
*/
class PostManagerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container,
$requestedName, array $options = null)
{
$entityManager = $container->get('doctrine.entitymanager.orm_default');
// Инстанцируем сервис и внедряем зависимости.
return new PostManager($entityManager);
}
}
И наконец, мы регистрируем сервис PostManager
изменив файл конфигурации module.config.php таким образом:
<?php
//...
return [
//...
'service_manager' => [
//...
'factories' => [
Service\PostManager::class => Service\Factory\PostManagerFactory::class,
],
],
//...
];
Для управления постами (добавления, изменения, просмотра и удаления) мы создадим класс
контроллера PostController
. Внутри этого класса мы создадим метод действия addAction()
,
который позволит добавлять новый пост в блог (см. код ниже):
class PostController extends AbstractActionController
{
/**
* Менеджер сущностей.
* @var Doctrine\ORM\EntityManager
*/
private $entityManager;
/**
* Менеджер постов.
* @var Application\Service\PostManager
*/
private $postManager;
/**
* Конструктор, используемый для внедрения зависимостей в контроллер.
*/
public function __construct($entityManager, $postManager)
{
$this->entityManager = $entityManager;
$this->postManager = $postManager;
}
/**
* Это действие отображает страницу "New Post". Она содержит
* форму, позволяющую ввести заголовок поста, содержимое и теги.
* Когда пользователь нажимает кнопку отправки формы, создается
* новая сущность Post.
*/
public function addAction()
{
// Создаем форму.
$form = new PostForm();
// Проверяем, является ли пост POST-запросом.
if ($this->getRequest()->isPost()) {
// Получаем POST-данные.
$data = $this->params()->fromPost();
// Заполняем форму данными.
$form->setData($data);
if ($form->isValid()) {
// Получаем валидированные данные формы.
$data = $form->getData();
// Используем менеджер постов для добавления нового поста в базу данных.
$this->postManager->addNewPost($data);
// Перенаправляем пользователя на страницу "index".
return $this->redirect()->toRoute('application');
}
}
// Визуализируем шаблон представления.
return new ViewModel([
'form' => $form
]);
}
}
В строке 33 фрагмента выше мы создаем экземпляр формы PostForm
.
В строке 36 мы проверяем, является ли запрос POST-запросом. Если это так, мы заполняем
форму входными данными и валидируем данные. Если данные действительны, мы вызываем метод
addNewPost()
для сервиса PostManager
(строка 49) и перенаправляем пользователя к
списку постов.
Чтобы инстанцировать PostController
, нам понадобится фабрика. Создайте фабрику контроллера, добавив
файл PostControllerFactory.php в каталог Controller/Factory под корневым каталогом модуля:
<?php
namespace Application\Controller\Factory;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Application\Service\PostManager;
use Application\Controller\PostController;
/**
* Это фабрика для PostController. Ее целью является инстанцирование
* контроллера.
*/
class PostControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container,
$requestedName, array $options = null)
{
$entityManager = $container->get('doctrine.entitymanager.orm_default');
$postManager = $container->get(PostManager::class);
// Инстанцируем контроллер и внедряем зависимости
return new PostController($entityManager, $postManager);
}
}
Затем зарегистрируйте контроллер PostController
в файле module.config.php:
<?php
//...
return [
//...
'controllers' => [
//...
'factories' => [
Controller\PostController::class =>
Controller\Factory\PostControllerFactory::class,
],
],
//...
];
Далее, добавьте маршрут posts для созданного контроллера (измените module.config.php, как показано ниже):
<?php
//...
return [
//...
'router' => [
'routes' => [
//...
'posts' => [
'type' => Segment::class,
'options' => [
'route' => '/posts[/:action[/:id]]',
'constraints' => [
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]*'
],
'defaults' => [
'controller' => Controller\PostController::class,
'action' => 'index',
],
],
],
],
],
//...
];
И наконец, добавим шаблон представления. Создайте файл add.phtml в каталоге application/post под каталогом модуля view и поместите в него следующий код:
<?php
$form = $this->form;
$form->get('title')->setAttributes([
'class'=>'form-control',
'placeholder'=>'Enter post title here'
]);
$form->get('content')->setAttributes([
'class'=>'form-control',
'placeholder'=>'Type content here',
'rows'=>6
]);
$form->get('tags')->setAttributes([
'class'=>'form-control',
'placeholder'=>'comma, separated, list, of, tags'
]);
$form->get('status')->setAttributes([
'class'=>'form-control'
]);
$form->get('submit')->setAttributes(['class'=>'btn btn-primary']);
$form->prepare();
?>
<h1>Add New Post</h1>
<p>
Please fill out the following form and click the <i>Create</i> button.
</p>
<div class="row">
<div class="col-md-6">
<?= $this->form()->openTag($form); ?>
<div class="form-group">
<?= $this->formLabel($form->get('title')); ?>
<?= $this->formElement($form->get('title')); ?>
<?= $this->formElementErrors($form->get('title')); ?>
</div>
<div class="form-group">
<?= $this->formLabel($form->get('content')); ?>
<?= $this->formElement($form->get('content')); ?>
<?= $this->formElementErrors($form->get('content')); ?>
</div>
<div class="form-group">
<?= $this->formLabel($form->get('tags')); ?>
<?= $this->formElement($form->get('tags')); ?>
<?= $this->formElementErrors($form->get('tags')); ?>
<p class="help-block">Separate tags with comma.</p>
</div>
<div class="form-group">
<?= $this->formLabel($form->get('status')); ?>
<?= $this->formElement($form->get('status')); ?>
<?= $this->formElementErrors($form->get('status')); ?>
</div>
<?= $this->formElement($form->get('submit')); ?>
<?= $this->form()->closeTag(); ?>
</div>
</div>
Теперь, если вы откроете в браузере URL http://localhost/posts/add, вы должны будете увидеть страницу добавления нового поста Add New Post, как показано ниже на рисунке 12.7:
Заполнив форму и нажав кнопку Create, вы сохраните новый пост в базе данных. Затем вы сможете посмотреть созданный вами пост в списке постов на странице Home.