📜 ⬆️ ⬇️

Form Designer in Yii

Hi habrayuzer!

In the majority of projects on Yii that I saw, work with forms was organized in the simplest way, where the rendering of the form was defined in the view file through the ActiveForm widget. Yes, this is certainly justified for complex shapes that are problematic to fit into a pattern. But today I want to talk about the form designer and show how we apply it.

If you have little idea what a form designer is, then first I advise you to familiarize yourself with the relevant section of the documentation www.yiiframework.ru/doc/guide/ru/form.builder .

It's simple. We work with forms based on the principle “do not mix form models and table models”. And the idea is to add form design functionality to the form model.
')
To do this, create a class from which our forms will be inherited. Those. let's make it so that the form model itself determines the rendering based on the shape configuration.

/** * 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; } } 


Go through the class in more detail. The array "$ _formConfig" allows you to set the configuration of the form. For example:

 /** * 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' => '' ) ) ); 

You can also override the "_formConfig ()" method to configure a form. This is convenient when you need to implement some logic during configuration.

The property "$ _formClass" allows you to change the class that is used to display the form (CForm or its descendant).

Note that now you do not need to specify the attributes of the form. To do this, we redefined the “attributeLabels ()” method and now attributes are equal to labels from the form configuration.

You can get the form object (CForm) using the getForm () method.

Using.

Rendering Forms:
 $formModel = new RegistrationForm(); $form = $formModel->getForm(); echo $form; 


Use with table model.
 $user = new User(); $user->setAttributes($formModel->getAttributes()); $result = $user->save(); 


Customize the display form. For each element in "$ _formConfig" we can specify our template with overriding the "layout" property:
 protected $_formConfig = array( .... 'elements' => array( .... 'name' => array( 'type' => 'text', 'label' => '', 'layout' => "{input}\n{label}\n{hint}\n{error}" ), .... ), 


Auto add colon.

One of the problems that arose when using the form designer is the inability to automatically add a colon after the field header. If a colon is inserted into the form's configuration in the label, it will also be visible when displaying errors. To fix this, we expand the classes “CForm” and “CFormInputElement” and slightly fix our “FormModel”.

In the "FormModel" add:
  /** * Automatically add a colon in time rendering * @var boolean */ public $autoAddColonForRender = true; 

And change $ _formClass
  /** * Class name of form * @var string */ protected $_formClass = 'Form'; 


Create a new class “Form”, a successor of “CForm”, override the property "$ inputElementClass".
 class Form extends CForm { /** * The name of the class for representing a form input element. * @var string */ public $inputElementClass = 'FormInputElement'; } 


Create a class "FormInputElement" (the successor of "CFormInputElement"), override the method "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); } } 


Now using the property "$ autoAddColonForRender" in the "FormModel" it is possible to automatically add a colon after the label for all form elements. Or define it for each form element separately during the configuration of the form, by setting the corresponding value in "addColon".
  protected $_formConfig = array( .... 'elements' => array( .... 'name' => array( 'type' => 'text', 'label' => '', 'addColon' => true ), .... ), 


That's all for today, thanks to everyone who read to the end. Objective criticism is welcome.

Source: https://habr.com/ru/post/155127/


All Articles