Skip to content

Instantly share code, notes, and snippets.

@lav45
Last active February 26, 2018 17:37
Show Gist options
  • Save lav45/50562bbdd6e7acbcd783a5b36b4e1535 to your computer and use it in GitHub Desktop.
Save lav45/50562bbdd6e7acbcd783a5b36b4e1535 to your computer and use it in GitHub Desktop.
MultipleInput verification through Model
<?= $form->field($model, 'other_software', [
'options' => [
'class' => 'fix-width'
]
])->widget(MultipleInput::class, [
'columns' => [
[
'name' => 'other_software',
'type' => FormBuilder::class,
'value' => function ($data) {
return $data;
},
'options' => [
'form' => $form,
'modelClass' => $model->otherSoftwareClass,
'fields' => [
'name' => [
'type' => FormBuilder::INPUT_DROPDOWN_LIST,
'items' => $anotherSystemList,
'options' => ['prompt' => ''],
],
'units_count' => [
'type' => FormBuilder::INPUT_TEXT,
],
'transferring_units' => [
'type' => FormBuilder::INPUT_DROPDOWN_LIST,
'items' => $model->getTransferringUnitsList(),
'options' => ['prompt' => Yii::t('app', '-Select one of the options-')],
],
'comment' => [
'type' => FormBuilder::INPUT_TEXTAREA,
]
],
],
],
]
]) ?>
<?php
class Partner extends ActiveRecord
{
/**
* @var string
*/
public $otherSoftwareClass = OtherSoftwareModel::class;
public function rules()
{
return [
[['other_software'], 'otherSoftwareValidator'],
];
}
public function otherSoftwareValidator($attribute)
{
$model = new $this->otherSoftwareClass();
$this->dynamicValidator($model, $attribute);
}
/**
* @param Model $model
* @param string $attribute
* @return array
*/
protected function dynamicValidator($model, $attribute)
{
$items = $this->$attribute;
foreach ($items as $index => $item) {
if ($this->isEmpty($item)) {
unset($items[$index]);
continue;
}
}
// Reset key
$items = array_values($items);
foreach ($items as $index => $item) {
$clone_model = clone $model;
$clone_model->setAttributes($item);
if ($clone_model->validate() === false) {
foreach ($clone_model->getErrors() as $attr => $errors) {
$this->addErrors(["{$attribute}[{$index}][{$attr}]" => $errors]);
}
}
foreach ($item as $key => $val) {
if ($clone_model->$key != $val) {
$items[$index][$key] = $clone_model->$key;
}
}
}
$this->$attribute = $items;
}
/**
* @param mixed $items
* @return bool
*/
protected function isEmpty($items)
{
foreach ($items as $value) {
if (is_array($value)) {
return $this->isEmpty($value);
} else {
if (!empty($value)) {
return false;
}
}
}
return true;
}
public function getTransferringUnitsList()
{
return [
1 => Yii::t('app', 'Yes'),
2 => Yii::t('app', 'No'),
3 => Yii::t('app', 'Planned transfer'),
];
}
}
class OtherSoftwareModel extends Model implements ConfigureFormInterface
{
use ConfigureFormTrait;
/**
* @var integer
*/
public $name;
/**
* @var string
*/
public $units_count;
/**
* @var integer
*/
public $transferring_units;
/**
* @var string
*/
public $comment;
/**
* @return array
*/
public function rules()
{
return [
[['units_count', 'comment'], 'string'],
[['name', 'transferring_units'], 'integer'],
];
}
/**
* @return array
*/
public function attributeLabels()
{
return [
'name' => 'Software',
'units_count' => 'Number of units',
'comment' => 'Comment',
'transferring_units' => 'Transferring units',
];
}
}
interface ConfigureFormInterface
{
public function setFormName($name);
}
trait ConfigureFormTrait
{
private $_formName;
public function formName()
{
return $this->_formName ? : parent::formName();
}
public function setFormName($name)
{
$this->_formName = $name;
}
}
<?php
use yii\base\Model;
use yii\base\Widget;
use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper;
use yii\widgets\ActiveField;
use yii\widgets\InputWidget;
class FormBuilder extends Widget
{
const INPUT_HIDDEN = 'hiddenInput';
const INPUT_TEXT = 'textInput';
const INPUT_TEXTAREA = 'textarea';
const INPUT_PASSWORD = 'passwordInput';
const INPUT_DROPDOWN_LIST = 'dropdownList';
const INPUT_LIST_BOX = 'listBox';
const INPUT_CHECKBOX = 'checkbox';
const INPUT_RADIO = 'radio';
const INPUT_CHECKBOX_LIST = 'checkboxList';
const INPUT_RADIO_LIST = 'radioList';
const INPUT_FILE = 'fileInput';
const INPUT_HTML5 = 'input';
const INPUT_WIDGET = 'widget';
const INPUT_RAW = 'raw';
/**
* @var \yii\widgets\ActiveForm
*/
public $form;
/**
* @var Model|ConfigureFormInterface
*/
private $model;
/**
* @var string
*/
public $modelClass;
/**
* @var array
*/
public $fields = [];
/**
* @var array
*/
public $options = [];
/**
* @var string
*/
public $name;
/**
* @var mixed
*/
public $value;
/**
* @var array
*/
public $fieldsOptions = [];
public function init()
{
parent::init();
$this->model = new $this->modelClass;
if (!empty($this->value)) {
$this->model->setAttributes($this->value, false);
}
if ($this->model instanceof ConfigureFormInterface) {
$this->model->setFormName($this->name);
}
}
public function run()
{
$enableClientValidation = $this->form->enableClientValidation;
$this->form->enableClientValidation = false;
$content = $this->renderForm($this->model, $this->fields);
$this->form->enableClientValidation = $enableClientValidation;
return $content;
}
/**
* @param Model $model
* @param array $config
*
* @return null|string
*/
public function renderForm(Model $model, array $config)
{
$content = null;
foreach ($config as $attribute => $options) {
$options = array_merge($this->fieldsOptions, $options);
$content .= $this->renderField($model, $attribute, $options);
}
return $content;
}
/**
* @param string $attribute
* @param array $settings
* @param Model $model
*
* @return ActiveField
* @throws InvalidConfigException
*/
public function renderField(Model $model, $attribute, array $settings = [])
{
$fieldOptions = ArrayHelper::getValue($settings, 'fieldOptions', []);
$field = $this->form->field($model, $attribute, $fieldOptions);
if (($label = ArrayHelper::getValue($settings, 'label')) !== null) {
$field->label($label, ArrayHelper::getValue($settings, 'labelOptions', []));
}
if (($hint = ArrayHelper::getValue($settings, 'hint')) !== null) {
$field->hint($hint, ArrayHelper::getValue($settings, 'hintOptions', []));
}
$type = ArrayHelper::getValue($settings, 'type', static::INPUT_TEXT);
$this->prepareField($field, $type, $settings);
return $field;
}
/**
* @param ActiveField $field
* @param $type
* @param array $settings
* @throws InvalidConfigException
*/
protected function prepareField($field, $type, array $settings)
{
$options = ArrayHelper::getValue($settings, 'options', []);
switch ($type) {
case static::INPUT_HIDDEN:
case static::INPUT_TEXT:
case static::INPUT_TEXTAREA:
case static::INPUT_PASSWORD:
case static::INPUT_FILE:
$field->$type($options);
break;
case static::INPUT_DROPDOWN_LIST:
case static::INPUT_LIST_BOX:
case static::INPUT_CHECKBOX_LIST:
case static::INPUT_RADIO_LIST:
$items = ArrayHelper::getValue($settings, 'items', []);
$field->$type($items, $options);
break;
case static::INPUT_CHECKBOX:
case static::INPUT_RADIO:
$enclosedByLabel = ArrayHelper::getValue($settings, 'enclosedByLabel', true);
$field->$type($options, $enclosedByLabel);
break;
case static::INPUT_HTML5:
$html5type = ArrayHelper::getValue($settings, 'html5type', 'text');
$field->$type($html5type, $options);
break;
case static::INPUT_WIDGET:
$widgetClass = $this->getWidgetClass($settings);
$field->$type($widgetClass, $options);
break;
case static::INPUT_RAW:
$field->parts['{input}'] = $this->getValue($settings);
break;
default:
throw new InvalidConfigException("Invalid input type '{$type}' configured for the attribute.");
}
}
/**
* @param array $settings
* @return mixed
* @throws InvalidConfigException
*/
protected function getWidgetClass(array $settings)
{
$widgetClass = ArrayHelper::getValue($settings, 'widgetClass');
if (empty($widgetClass) && !$widgetClass instanceof InputWidget) {
throw new InvalidConfigException("A valid 'widgetClass' must be setup and extend from '\\yii\\widgets\\InputWidget'.");
}
return $widgetClass;
}
/**
* @param array $settings
* @return mixed|string
*/
protected function getValue(array $settings)
{
$value = ArrayHelper::getValue($settings, 'value', '');
if (is_callable($value)) {
return call_user_func($value);
} elseif (!is_string($value)) {
return '';
}
return $value;
}
}
.fix-width > [class*="col-"] {
width: 100%;
padding-right: 0;
padding-left: 0;
}
.fix-width > div > .multiple-input {
margin-right: -15px;
margin-left: -15px;
}
.fix-width > div > .multiple-input > .multiple-input-list > tbody > .multiple-input-list__item > .list-cell__button {
position: absolute;
right: 20px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment