Конструктор форм в Yii

    Привет хабраюзеры!

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

    Если вы плохо представляете, что такое конструктор форм, то прежде советую ознакомиться с соответствующим разделом документации www.yiiframework.ru/doc/guide/ru/form.builder.

    Все просто. Работа с формами у нас построена по принципу «не смешивать модели формы и модели таблиц». А идея состоит в том, что бы в модель формы добавить функциональность конструктора форм.

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

    /**
     * FormModel.
     *
     * @author     Andrey Nilov <nilov@glavweb.ru>
     * @copyright  Copyright (c) 2007-2012 Glavweb.Soft, Russia. (http://glavweb.ru)
     */
    class FormModel extends CFormModel
    {
        /**
         * Config of the form
         * @var array
         */
        protected $_formConfig = array();
    
        /**
         * Config of the form by default
         * @var array
         */
        private $_defaultFormConfig = array(
            'method' => 'post'
        );
           
        /**
         * Class name of form
         * @var string
         */
        protected $_formClass = 'CForm';
    
        /**
         * Object of Form
         * @var Form
         */
        private $_form = null;    
        
        
        /**
         * Constructor
         * 
         * @param string $scenario Name of the scenario that this model is used in
         * @return void
         */
        public function __construct($scenario = '') 
        {
            parent::__construct($scenario);
            $this->_setFormConfig();
        }
    
        /**
         * Sets config of the form
         * 
         * @return void
         */
        private function _setFormConfig() 
        {
            $this->_formConfig = array_replace_recursive(
                $this->_defaultFormConfig, 
                $this->_formConfig()
            );
        }
    
        /**
         * Returns config of the form
         * 
         * @return array
         */
        protected function _formConfig() 
        {
            return $this->_formConfig;
        }
        
        /**
         * Returns the attribute labels
         *
         * @return array Attribute labels (name=>label)
         */
        public function attributeLabels()
        {
            return $this->getLabels();
        }    
        
        /**
         * Returns the config of the form
         * 
         * @return array
         */
        public function getFromConfig() 
        {
            return $this->_formConfig;
        }
        
        /**
         * Sets the config of the form
         * 
         * @param array $config
         * @return void
         */
        public function setFromConfig(array $config) 
        {
            $this->_formConfig = $config;
        }
        
        /**
         * Returns labels
         * 
         * @return array
         */
        public function getLabels() 
        {
            $labels = array();
            if (!empty($this->_formConfig['elements'])) {
                foreach ($this->_formConfig['elements'] as $name => $data) {
                    if (isset($data['label'])) {
                        $labels[$name] = $data['label'];
                    }
                }
            }
            
            return $labels;
        }
        
        /**
         * Returns object of Form
         * 
         * @return Form
         */
        public function getForm() 
        {
            if ($this->_form === null) {
                $this->_form = new $this->_formClass($this->_formConfig, $this);
            }
            
            return $this->_form;
        }    
    }
    


    Пройдемся по классу более подробно. Массив "$_formConfig" позволяет задать конфигурацию формы. Например:

    /**
     * Config of the form
     * @var array
     */
    protected $_formConfig = array(
        'activeForm' => array(
            'class'                  => 'CActiveForm',
            'id'                     => 'registration_form',
            'enableClientValidation' => true,
            'clientOptions' => array(
                'validateOnSubmit' => true
            )
        ),
    
        'elements' => array(
            ....
            'name' => array(
                'type' => 'text',
                'label' => 'ФИО'
            ),
            'organization' => array(
                'type' => 'text',
                'label' => 'Организация'
            ),
           ....
        ),
    
        'buttons' => array(
            'register' => array(
                'type'  => 'submit',
                'label' => 'Регистрация'
            )
        )
    );
    

    Так же для конфигурации формы можно переопределить метод "_formConfig()". Это удобно когда нужно во время конфигурации реализовать какую-то логику.

    Свойство "$_formClass" позволяет изменить класс который используется для отображения формы (CForm или его потомок).

    Заметьте, что теперь вам не обязательно указывать атрибуты формы. Для этого мы переопределили метод «attributeLabels()» и теперь атрибуты равны лейблам из конфигурации формы.

    Получить объект формы (CForm) можно методом «getForm()».

    Использование.

    Рендеринг формы:
    $formModel = new RegistrationForm();
    $form = $formModel->getForm();
    echo $form;
    


    Использование совместно с моделью таблицы.
    $user = new User();
    $user->setAttributes($formModel->getAttributes());
    $result = $user->save();
    


    Настройка отображения формы. Для каждого элемента в "$_formConfig" мы можем указать свой шаблон с переопределив свойство «layout»:
    protected $_formConfig = array(
        ....        
        'elements' => array(
            ....
            'name' => array(
                'type' => 'text',
                'label' => 'ФИО',
                'layout' => "{input}\n{label}\n{hint}\n{error}"
            ),
           ....
        ),
    


    Автоматическое добавление двоеточия.

    Одна из неприятностей которая встала при использовании конструктора форм, это невозможность автоматически добавлять двоеточие после заголовка поля. Если двоеточие вставить в конфиг формы в label, то оно будет видно и при отображении ошибок. Что бы исправить это, расширим классы «CForm» и «CFormInputElement» и чуть-чуть поправим наш «FormModel».

    В «FormModel» добавляем:
        /**
         * Automatically add a colon in time rendering
         * @var boolean
         */
        public $autoAddColonForRender = true;
    

    И меняем $_formClass
        /**
         * Class name of form
         * @var string
         */
        protected $_formClass = 'Form';
    


    Создадим новый класс «Form» наследник «CForm», в нем переопределим свойство "$inputElementClass".
    class Form extends CForm
    {
        /**
         * The name of the class for representing a form input element.
         * @var string 
         */
        public $inputElementClass = 'FormInputElement';    
    }
    


    Создадим класс «FormInputElement» (наследник «CFormInputElement»), в нем переопределим метод «renderLabel()»
    class FormInputElement extends CFormInputElement
    {
        /**
         * @var string the layout used to render label, input, hint and error. They correspond to the placeholders
         * "{label}", "{input}", "{hint}" and "{error}".
         */
        public $layout = "{label}\n{input}\n{error}\n{hint}";
            
        /**
         * Automatically add a colon in time rendering
         * @var boolean
         */
        public $addColon = null;
        
        
        /**
         * Renders the label for this input.
         * 
         * @return string 
         */
        public function renderLabel() 
        {
            $model = $this->getParent()->getModel();
            
            $addColon = $this->addColon !== null ?
                $this->addColon :
                $model instanceof FormModel && $model->autoAddColonForRender;
            
            $label = $addColon ? $this->getLabel() . ':' : $this->getLabel();
            $options = array(
                'label'    => $label,
                'required' => $this->getRequired()
            );
    
            if (!empty($this->attributes['id'])) {
                $options['for'] = $this->attributes['id'];
            }
    
            return CHtml::activeLabel($this->getParent()->getModel(), $this->name, $options);
        }
    }
    


    Теперь используя свойство "$autoAddColonForRender" в «FormModel» возможно автоматически добавлять двоеточие после лейбла для всех элементов формы. Либо определять это для каждого элемента формы в отдельности во время конфиграции формы, установив соответствующее значение в «addColon».
       protected $_formConfig = array(
            ....        
            'elements' => array(
                ....
                'name' => array(
                    'type' => 'text',
                    'label' => 'ФИО',
                    'addColon' => true
                ),
               ....
            ),
    


    На этом сегодня всё, спасибо всем кто дочитал до конца. Объективная критика приветствуется.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 10

      0
      Аналог zend декораторов получается?
          0
          Есть еще EFormModelBehavior. Он выполняет аналогичную задачу, но в виде поведения + доп. плюшки
          0
          Вы что издеваетесь? Слили 2 файла в 1 и выдаете это за мегарешение, мда скоро будет наплыв «топкодеров» в фв? yiiframework.ru/doc/guide/ru/form.builder
            0
            Я и не заявлял, что это «мегорешение». Это не более чем подход к работе с моделями. Суть которого не смешивать модели формы и таблицы + использование конструктора формы. Да, это очевидное решение. Но с другой стороны это и компромис и значит могут быть и свои недостатки. Было бы здорого услышать объективную критику такого подхода.
              0
              Тут критиковать то нечего, взяли смешали билдер стандартный и все. как вы можете например покритиковать, что 2+2=4? Редко на хабре бывают нормальные статьи по Yii, в основном либо перевод документации либо такие.
                0
                Вообще я не вижу ничего плохого в этой статье. Тем, кто только начал, очень даже полезна.
                  0
                  Ну да, новичкам полезно, только вот стоящих статей мало( давно они были, от 2гис например, Саша может ты напишешь что-то интересное по Yii хоть?) книги не в счет, я про хабр)
          0
          для себя, не вижу никакого удобства в конструкторе форм. то ли формы у меня сложные, то ли я не вижу пользы.
          из всего что понравилось вижу только
          $user->setAttributes($formModel->getAttributes());
          и опять же, к моим формам далеко не всегда применимо.
          эти конструкторы больше подходят тем кто юзает Crud Generator

          Only users with full accounts can post comments. Log in, please.