Содержание


Формы


Forms and actions




Добавление нового альбома

Теперь приступим к написанию кода для добавления нового альбома. Необходимо выполнить два пункта:

1)      Вывести форму для заполнения

2)      Отправить форму, проверить данные и сохранить их в БД.

Для этого будем использовать Zebd\Form. Этот компонент ядра фреймворка используется для создания и проверки(валидации, validation) форм. Добавим Zend\InputFilter к нашей сущности Album.  Начнем с создания нового класса Album\Form\AlbumForm который будет наследовать класс Zend\Form\Form для определения нашей формы. Класс будет находиться в файле AlbumForm.php, находящийся в папке module/Album/srs/Album/Form:

// module/Album/src/Album/Form/AlbumForm.php:
namespace AlbumForm;
 
use Zend\Form\Form;
 
class AlbumForm extends Form
{
    public function __construct($name = null)
    {
        // we want to ignore the name passed
        parent::__construct('album');
        $this->setAttribute('method', 'post');
        $this->add(array(
            'name' => 'id',
            'attributes' => array(
                'type'  => 'hidden',
            ),
        ));
        $this->add(array(
            'name' => 'artist',
            'attributes' => array(
                'type'  => 'text',
            ),
            'options' => array(
                'label' => 'Artist',
            ),
        ));
        $this->add(array(
            'name' => 'title',
            'attributes' => array(
                'type'  => 'text',
            ),
            'options' => array(
                'label' => 'Title',
            ),
        ));
        $this->add(array(
            'name' => 'submit',
            'attributes' => array(
                'type'  => 'submit',
                'value' => 'Go',
                'id' => 'submitbutton',
            ),
        ));
    }
}


В конструкторе(__construct()) класса AlbumForm мы устанавливаем имя для вызова конструктора родителя, устанавливаем метод передачи данных с формы(‘method’, ‘post’), создаем четыре элемента формы: id(скрытое поле), artist(текстовое поле), title(текстовое поле), submitbutton(кнопка отправки данных). Для каждого элемента  устанавливаем различные атрибуты и опции(‘label’ – то, что будет показываться в браузере, название поля).

Так же необходимо создать проверку введенных данных в форму, или если просто – валидацию (validation).В Zend Framework 2 это делается созданием входных фильтров(inputfilters). Они могут быть как стандартные, так и индивидуальные (написанные Вами), главное что б они расширяли InputFilterAwareInterface. Добавим фильтр к нашей сущности Album:

// module/Album/src/Album/Model/Album.php:
namespace AlbumModel;
 
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
 
class Album implements InputFilterAwareInterface
{
    public $id;
    public $artist;
    public $title;
    protected $inputFilter;
 
    public function exchangeArray($data)
    {
        $this->id     = (isset($data['id']))     ? $data['id']     : null;
        $this->artist = (isset($data['artist'])) ? $data['artist'] : null;
        $this->title  = (isset($data['title']))  ? $data['title']  : null;
    }
 
    public function setInputFilter(InputFilterInterface $inputFilter)
    {
        throw new Exception("Not used");
    }
 
    public function getInputFilter()
    {
        if (!$this->inputFilter) {
            $inputFilter = new InputFilter();
            $factory     = new InputFactory();
 
            $inputFilter->add($factory->createInput(array(
                'name'     => 'id',
                'required' => true,
                'filters'  => array(
                    array('name' => 'Int'),
                ),
            )));
 
            $inputFilter->add($factory->createInput(array(
                'name'     => 'artist',
                'required' => true,
                'filters'  => array(
                    array('name' => 'StripTags'),
                    array('name' => 'StringTrim'),
                ),
                'validators' => array(
                    array(
                        'name'    => 'StringLength',
                        'options' => array(
                            'encoding' => 'UTF-8',
                            'min'      => 1,
                            'max'      => 100,
                        ),
                    ),
                ),
            )));
 
            $inputFilter->add($factory->createInput(array(
                'name'     => 'title',
                'required' => true,
                'filters'  => array(
                    array('name' => 'StripTags'),
                    array('name' => 'StringTrim'),
                ),
                'validators' => array(
                    array(
                        'name'    => 'StringLength',
                        'options' => array(
                            'encoding' => 'UTF-8',
                            'min'      => 1,
                            'max'      => 100,
                        ),
                    ),
                ),
            )));
 
            $this->inputFilter = $inputFilter;
        }
 
        return $this->inputFilter;
    }
}


InputFilterAwareInterface определяет два метода: setInputFilter () и getInputFilter (). Нам необходимо реализовать только getInputFilter (), поэтому для setInputFilter () вызовем исключение.

В методе getInputFilter () создаем InputFilter и добавляем в него нужные фильтры. Создадим по одному Input для каждого свойства(элемента формы), который необходимо отфильтровать (filter) или проверить (validate). Для элемента «id» добавим только фильтр «Int» (целые числа). Для элементов «текстовое поле»  добавим по два фильтра: StripTags и StrimTrim для удаления нежелательных HTML тегов и пробелов в начале и конце строки соответственно.  Так же сделали их required(обязательными для заполнения) и добавили валидатор (validatot) StringLength для указания минимального(min) и максимального(max) количества введенных символов.

Теперь реализуем отображение формы и обработку пришедших данных. Жто делается уже в нужном действии нужного контроллера. У нас это: addAction() и AlbumController:

// module/Album/src/Album/Controller/AlbumController.php:
 
//...
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Album\Model\Album;          // <-- Add this import
use Album\Form\AlbumForm;       // <-- Add this import
//...
 
    // Add content to this method:
    public function addAction()
    {
        $form = new AlbumForm();
        $form->get('submit')->setValue('Add');
 
        $request = $this->getRequest();
        if ($request->isPost()) {
            $album = new Album();
            $form->setInputFilter($album->getInputFilter());
            $form->setData($request->getPost());
 
            if ($form->isValid()) {
                $album->exchangeArray($form->getData());
                $this->getAlbumTable()->saveAlbum($album);
 
                // Redirect to list of albums
                return $this->redirect()->toRoute('album');
            }
        }
        return array('form' => $form);
    }
//...


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

$form = new AlbumForm();
$form->submit->setValue('Add');
Мы создаем новый экземпляр нашей формы и добавляем нужное название «Add» для кнопки submit. Мы сделали так, что б можно было легко использовать заготовку формы для разных целей.
$request = $this->getRequest();
if ($request->isPost()) {
    $album = new Album();
    $form->setInputFilter($album->getInputFilter());
    $form->setData($request->getPost());
    if ($form->isValid()) {


Сначала получаем объект ответа. Если просто – сборщик всего, что пришло. Потом проверяем ( isPost() ), есть ли данные в массиве POST, так как ранее мы установили, что данные из формы передаются через POST. Далее создаем входной фильтр(InputFilter) через экземпляр объекта Album. И наконец-то отправляем пришедшие данные через POST в форму, где и идет проверка: фильтры и валидность ( isValid() ).

$album->exchangeArray($form->getData());
$this->getAlbumTable()->saveAlbum($album);


Если все проверки прошли успешно, тогда берем полученные данные и отправляем их в модель для сохранения в БД используя saveAlbum().

// Redirect to list of albums
return $this->redirect()->toRoute('album');


После сохранения данных перенаправляем на список альбомов.

return array('form' => $form);


А тут мы передаем все необходимое в шаблон вида, для отрисовки формы. Также, если какие то проверки не прошли, то высветится форма с уже заполненными ранее данными и указанием ошибок (почему данные не прошли валидацию).

Теперь пришла очередь отрисовки (render) формы в шаблоне вида add.phtml:

<?php
// module/Album/view/album/album/add.phtml:
 
$title = 'Add new album';
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>
<?php
$form = $this->form;
$form->setAttribute('action', $this->url('album', array('action' => 'add')));
$form->prepare();
 
echo $this->form()->openTag($form);
echo $this->formHidden($form->get('id'));
echo $this->formRow($form->get('title'));
echo $this->formRow($form->get('artist'));
echo $this->formSubmit($form->get('submit'));
echo $this->form()->closeTag();

И так. Задаем заголовок страницы, который будет показываться в браузере. Рендерим(отрисовываем, render) форму. Для упрощения  этой задачи в Zend Framework 2 есть несколько помощников вида. Помощник вида form() имеет в своем распоряжении методы для открытия и закрытия формы. Это: openTag() и  closeTag(). Далее, для каждого элемента с заранее заданой опцией «label» используем formRow(), а для автономных элементов (standalone) используем formHidden() и formSubmit().

Всё! Теперь можете перейти по ссылке «Добавить альбом(Addnewalbum)» и все заработает. Вы введете данные и они сохранятся, после чего перекинет на главную. И увидите уже обновленный список.

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

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

// module/Album/src/Album/AlbumController.php:
//...
 
    // Add content to this method:
    public function editAction()
    {
        $id = (int) $this->params()->fromRoute('id', 0);
        if (!$id) {
            return $this->redirect()->toRoute('album', array(
                'action' => 'add'
            ));
        }
        $album = $this->getAlbumTable()->getAlbum($id);
 
        $form  = new AlbumForm();
        $form->bind($album);
        $form->get('submit')->setAttribute('value', 'Edit');
 
        $request = $this->getRequest();
        if ($request->isPost()) {
            $form->setInputFilter($album->getInputFilter());
            $form->setData($request->getPost());
 
            if ($form->isValid()) {
                $this->getAlbumTable()->saveAlbum($form->getData());
 
                // Redirect to list of albums
                return $this->redirect()->toRoute('album');
            }
        }
 
        return array(
            'id' => $id,
            'form' => $form,
        );
    }
//...


В общем все знакомо. Рассмотрим только отличия. Сразу бросается в глаза id, который используется для загрузки конкретного альбома на редактирование.

$id = (int) $this->params()->fromRoute('id', 0);
if (!$id) {
    return $this->redirect()->toRoute('album', array(
        'action' => 'add'
    ));
}
$album = $this->getAlbumTable()->getAlbum($id);


params() – это плагин контроллера(можно понимать как вспомогательная функция ),дающий возможность достать параметры из URL. Если в URL был указан id и он не равен нулю( 0 ) то увидим заполненную форму с данными о соответствующем альбоме, которые были взяты из БД .

$form = new AlbumForm();
$form->bind($album);
$form->get('submit')->setAttribute('value', 'Edit');


Метод bind() присоединяет модель к форме. Может использоваться двумя способами:

1)      При рендеринге (отрисовке) формы. Будут заполнены соответствующие поля.

2)      После прохождения проверки isValid() данные пришелшие из ормы будут занесены в БД.

Эти действия выполняются с помощью объекта гидратора(hydrator object). Вобщем есть много гидраторов, но по-умолчанию используется только один Zend\Stdlib\Hydrator\ArraySerializable, который сработает, если найдет два таких метода в модели: getArrayCopy() и  exchangeArray().exchangeArray() у нас уже есть. Допишем недостоющий:

// module/Album/src/Album/Model/Album.php:
// ...
    public function exchangeArray($data)
    {
        $this->id     = (isset($data['id']))     ? $data['id']     : null;
        $this->artist = (isset($data['artist'])) ? $data['artist'] : null;
        $this->title  = (isset($data['title']))  ? $data['title']  : null;
    }
 
    // Add the following method:
    public function getArrayCopy()
    {
        return get_object_vars($this);
    }
// ...



Преимущества такого подхода в том, что нам не нужно привязывать данные к $album, как мы это делали раньше, а сразу воспользуемся методом saveAlbum() для сохранения данных в БД.

Шаблон вида для редактирования альбома edit.phtml выглядит так:

<?php
// module/Album/view/album/album/edit.phtml:
 
$title = 'Edit album';
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>
 
<?php
$form = $this->form;
$form->setAttribute('action', $this->url(
    'album',
    array(
        'action' => 'edit',
        'id'     => $this->id,
    )
));
$form->prepare();
 
echo $this->form()->openTag($form);
echo $this->formHidden($form->get('id'));
echo $this->formRow($form->get('title'));
echo $this->formRow($form->get('artist'));
echo $this->formSubmit($form->get('submit'));
echo $this->form()->closeTag();


Различия заключаются в заголовке страницы и названии кнопки.

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

Для завершения нашего примера осталось написать действие удаления альбома. Ссылка на удаление у нас уже имеется -  напротив названия каждого альбома. Но помните, что нельзя делать необратимые действия через GET запрос. Хороший тон -  использовать для этого POST.

При нажатии на эту ссылку мы высвети форму подтверждений и если пользователь нажмет «да» -  удалим. Так как форма будет очень простая, её код напишем сразу в шаблоне вида.

Начнем:

// module/Album/src/Album/AlbumController.php:
//...
    // Add content to the following method:
    public function deleteAction()
    {
        $id = (int) $this->params()->fromRoute('id', 0);
        if (!$id) {
            return $this->redirect()->toRoute('album');
        }
 
        $request = $this->getRequest();
        if ($request->isPost()) {
            $del = $request->getPost('del', 'No');
 
            if ($del == 'Yes') {
                $id = (int) $request->getPost('id');
                $this->getAlbumTable()->deleteAlbum($id);
            }
 
            // Redirect to list of albums
            return $this->redirect()->toRoute('album');
        }
 
        return array(
            'id'    => $id,
            'album' => $this->getAlbumTable()->getAlbum($id)
        );
    }
//...


Тут все так же, как мы уже делали. Получаем id. Проверяем, что данные пришли через POST. Для удаление используем метод deleteAlbum() нашей сущности для работы с БД. Если же данные пришли не из POST, то снова высвечиваем данные об альбоме, и спрашиваем подтверждения на удаление.

Шаблон вида delete.phtml:

<?php
// module/Album/view/album/album/delete.phtml:
 
$title = 'Delete album';
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>
 
<p>Are you sure that you want to delete
    '<?php echo $this->escapeHtml($album->title); ?>' by
    '<?php echo $this->escapeHtml($album->artist); ?>'?
</p>
<?php
$url = $this->url('album', array(
    'action' => 'delete',
    'id'     => $this->id,
));
?>
<form action="<?php echo $url; ?>" method="post">
<div>
    <input type="hidden" name="id" value="<?php echo (int) $album->id; ?>" />
    <input type="submit" name="del" value="Yes" />
    <input type="submit" name="del" value="No" />
</div>
</form>


Высвечиваем информацию об альбоме и две кнопки «Да» и «Нет». Если да -  удаляем.

Домашняя страница

Осталась последний штрих, и наш пример заработает так, как мы того хотим. Сейчас домашняя страница не показывает список альбомов.

Это связано с настройкой роутинга в module.config.php. Найдите это:

'home' => array(
    'type' => 'Zend\Mvc\Router\Http\Literal',
    'options' => array(
        'route'    => '/',
        'defaults' => array(
            'controller' => 'Application\Controller\Index',
            'action'     => 'index',
        ),
    ),
),


И измените на это:

'home' => array(
    'type' => 'Zend\Mvc\Router\Http\Literal',
    'options' => array(
        'route'    => '/',
        'defaults' => array(
            'controller' => 'Album\Controller\Album', // <-- change here
            'action'     => 'index',
        ),
    ),
),


Все. Наслаждайтесь. Разбирайтесь. Удачного изучения.

Больше полезной информации сможете найти в других разделах нашего сайта. 


Автор статьи: DuB