Статьи

Использование Select In Zend Framework 2

Я использовал Zend Framework 2 в недавнем проекте, чтобы оценить его возможности, и один из первых блокировщиков, которые у меня были, пытался понять, как, черт возьми, использовать элемент формы Select. На момент написания этой статьи нет хорошей документации о том, как ее использовать, поэтому вместо этого я расскажу, как ее использовать в этом посте.

Менеджер альбомов, Пересмотрено

Для этой демонстрации я буду использовать приложение Zend-Skeleton, над которым я первоначально работал, следуя учебному пособию по Zend Framework 2. Я остановился на своем предыдущем посте, где мы сохранили наш код с помощью аннотаций форм. Вы можете следить за мной в этом посте, проверив мой проект zend-skeleton на github и отметив тег «select-begin».

Начнем с того, что у нас есть аннотированный объект модели Album с заголовком и исполнителем:

<?php
// module/Album/src/Album/Model/Album.php:
namespace Album\Model;

use Zend\Form\Annotation as Form;

class Album{
    /**
* @Form\Required(false)
* @Form\Attributes({"type":"hidden"})
*/
    public $id;

    /**
* @Form\Required(true)
* @Form\Attributes({"type":"text"})
* @Form\Options({"label":"Artist"})
* @Form\Filter({"name":"StringTrim"})
* @Form\Validator({"name":"StringLength", "options":{"min":1, "max":100}})
*/
    public $artist;

    /**
* @Form\Required(true)
* @Form\Attributes({"type":"text"})
* @Form\Options({"label":"Title"})
* @Form\Filter({"name":"StringTrim"})
* @Form\Filter({"name":"StripTags"})
*/
    public $title;

Итак, мы написали наше приложение, и наш клиент доволен, однако он хотел бы иметь возможность пометить альбомы жанром. При добавлении или редактировании нового альбома должен быть раскрывающийся жанр с такими параметрами, как Rap, Jazz, Alternative, Rock и Country.

Оказывается, это очень просто, нам просто нужно добавить новое поле в нашу модель и элемент формы Select:

 /**
* @Form\Type("Zend\Form\Element\Select")
* @Form\Options({"label":"Genre", "value_options":{
* "Rap":"Rap",
* "Jazz":"Jazz",
* "Alternative":"Alternative",
* "Rock":"Rock"
* }
* })
*/
    public $genre;

Теперь начинается стандартная часть, добавляя ее к вашим представлениям и AlbumTable (скоро я опубликую блог о замене TableGateways). Я оставлю это как упражнение для читателя. Смотрите тег select-annotated, чтобы увидеть законченную работу здесь. Вам также необходимо добавить колонку жанра в таблицу альбомов самостоятельно (бу … нет миграций).

Получение значений из внешнего источника

Обычно мы получаем жанры из таблицы базы данных или из какого-либо другого внешнего источника, не закодированного в виде списка значений в аннотации. Чтобы понять, как сделать это лучше, давайте сначала извлечем совокупность значений из аннотации формы и сделаем это в другом месте. Очевидное место для манипулирования формой после факта, очевидно, не в контроллере, поэтому давайте выделим создание формы во что-то, что может ее инкапсулировать. Из-за отсутствия каких-либо соглашений наше руководство от фреймворка здесь, давайте просто назовем его AlbumFormBuilder и станем сильными.:)

Сначала мы создаем новый класс с именем FormBuilder в модуле / Album / src / Album / Form / FormBuilder. Ранее (как и во время последнего урока) мы применили метод extract для извлечения общего метода из addAction и editAction для обработки логики построения формы.

 private function form(){
      $builder = new AnnotationBuilder();
      $form = $builder->createForm(new Album());
      $form->add(array(
        'name' => 'submit',
        'attributes' => array(
            'type' => 'submit',
            'value' => 'Add',
            'id' => 'submitbutton',
        ),
      ));
      return $form;
    }

Теперь нам просто нужно переместить этот метод в наш новый класс FormBuilder и назвать его чем-то значимым, например, «newForm».

<?php
namespace Album\Form;

use Zend\Form\Annotation\AnnotationBuilder;
use Album\Model\Album;

class FormBuilder {
    public function newForm(){
        $builder = new AnnotationBuilder();
        $form = $builder->createForm(new Album());
        $form->add(array(
            'name' => 'submit',
            'attributes' => array(
            'type' => 'submit',
            'value' => 'Add',
            'id' => 'submitbutton',
            ),
        ));
        return $form;
    }

}

Теперь мы можем просто поместить этот объект в существующий метод в AlbumController и увидеть, что он все еще работает (и я обычно делаю это во время рефакторинга). Но чтобы действительно использовать мощь, мы должны создать этого нового строителя с помощью заводского механизма ZF2. Итак, мы открываем module / Album / Module.php и следующее в разделе фабрики:

public function getServiceConfig(){
        return array(
            'factories' => array(
                'Album\Model\AlbumTable' => function($sm) {
                    $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                    $table = new AlbumTable($dbAdapter);
                    return $table;
                },
                // add this
                'Album\Form\FormBuilder' => function($sm){
                    return new \Album\Form\FormBuilder();
                }
            ),
        );
    }

И измените AlbumController, чтобы использовать его через сервисный локатор.

 private function form(){
        $sm = $this->getServiceLocator();
        return $sm->get('Album\Form\FormBuilder')->newForm();
    }

Теперь, когда логика лучше инкапсулирована вне контроллера, давайте просто установим параметры значений непосредственно в классе компоновщика.

...
        $form = $builder->createForm(new Album());

        $form->get('genre')->setValueOptions(
            array(
                "Alternative",
                "Country",
                "Jazz",
                "Rap",
                "Rock"
            )
        );
   ...

Теперь, если мы сохраним это и проверим приложение, мы заметим, что наше новое дополнение полностью переопределяет то, что было в аннотации. Так как они нам больше не нужны, мы можем удалить атрибут value_options из нашей аннотации формы. Идите вперед и отредактируйте или добавьте новый альбом, выберите жанр и сохраните его. Заметьте что-нибудь? Если вы используете массив строковых литералов, все значения будут индексами элемента, а фактические значения будут метками.

У нас есть два варианта: либо указать ключи, которые представляют значения параметров, либо явно определить метку и значение (да, это немного странно). Смотрите примеры ниже для быстрого ознакомления.

/**
 * Generates the following:
 * <option value="alt">Alternative</option>
 * <option value="country">Country</option>
 * <option value="jazz">Jazz</option>
 * <option value="rap">Rap</option>
 * <option value="rock">Rock</option>
 */
array(
    'alt' => 'Alternative',
    'country' => 'Country',
    'jazz' => 'Jazz',
    'rap' => 'Rap',
    'rock' => 'Rock'
)

Это на самом деле не дало мне того, чего я изначально ожидал … Я ожидал увидеть ключи массива в виде меток, а значения в качестве значений. Однако использование массива с ключами будет в основном соответствовать тому же левому и правому расположению, которое будут иметь фактические элементы выбора элементов. Если вы хотите быть более явным, вы также можете сделать следующее:

array(
    array('value' => 'alt', 'label' => 'Alternative'),
    array('value' => 'country', 'label' => 'Country'),
    array('value' => 'jazz', 'label' => 'Jazz'),
    array('value' => 'rock', 'label' => 'Rock'),
)

Последний шаг: извлечение ценностей

Была причина, по которой я использовал управление зависимостями ZF2 для извлечения нового экземпляра FormBuilder, и это заключалось в том, чтобы легко извлекать варианты значений для предоставления чего-то другого, возможно, другого шлюза таблиц. Итак, давайте продолжим и переместим этот массив, который будет возвращен новым GenreTable.

<?php
// modules/Album/src/Album/Model/GenreTable.php
namespace Album\Model;


class GenreTable {
    public function fetchAllAsArray(){
        return array(
                'alt' => 'Alternative',
                'country' => 'Country',
                'jazz' => 'Jazz',
                'rap' => 'Rap',
                'rock' => 'Rock'
            );
    }
}

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

class FormBuilder {
    private $genres;
    public function __construct($genres) {
        $this->genres = $genres;
    }
    public function newForm(){
        $builder = new AnnotationBuilder();
        $form = $builder->createForm(new Album());

        $form->get('genre')->setValueOptions(
            $this->genres->fetchAllAsArray()
        );
    ...
    }
}

Наконец, мы обновляем module / Album / Module.php, чтобы добавить экземпляр нового GenreTable.

  'Album\Model\GenreTable' => function($sm){
                    return new \Album\Model\GenreTable();
                },
                'Album\Form\FormBuilder' => function($sm){
                    return new \Album\Form\FormBuilder(
                        $sm->get('Album\Model\GenreTable')
                    );
                }

Теперь мы можем изменить объект, введенный в FormBuilder, для извлечения жанров из базы данных, а не использовать массив в памяти, когда мы будем готовы, но я устал от написания TableGateways. В моем следующем посте я сделаю свой код немного DRY’er, разорвав эти шлюзы таблиц в пользу доктрины.:)

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