'Empty List', ]; protected $_enabledHeaders = true; protected $_label; protected $_labelAttribs = []; protected $_startIndex = 0; protected $_itemName; protected $_itemsName; protected $_maxItems; protected $_minCount; protected $_maxCount; protected $_itemClass; protected $_itemOptions = []; protected $_specificRowOptions = []; protected $_required; protected $_description; protected $_columnsCount; protected $_itemSubForm; protected $fixedRows = false; public function init() { $this->addScript('js/qs/form/subform/table.js'); return parent::init(); } public function getLabel() { return $this->_label; } public function setLabel($label) { $this->_label = $label; return $this; } public function getLabelAttribs() { return $this->_labelAttribs; } public function setLabelAttribs(array $labelAttribs) { $this->_labelAttribs = $labelAttribs; return $this; } public function setLabelAttrib($name, $value) { $this->_labelAttribs[$name] = $value; return $this; } public function setText($name, $text) { $this->_text[$name] = $text; return $this; } public function getText($name) { return Qs_Array::get($this->_text, $name); } public function isEnabledHeaders() { return $this->_enabledHeaders; } public function setEnabledHeaders($flag) { $this->_enabledHeaders = $flag; return $this; } public function getStartIndex() { return $this->_startIndex; } public function setStartIndex($itemStartIndex) { $this->_startIndex = $itemStartIndex; return $this; } public function getItemName() { return $this->_itemName; } public function setItemName($itemName) { $this->_itemName = $itemName; return $this; } public function getItemsName() { return $this->_itemsName; } public function getItemNamePlural($count) { return Qs_Translate::getPlural([$this->getItemName(), $this->getItemsName()], $count); } public function getMaxItems() { return $this->_maxItems; } public function setMaxItems($maxItems) { $this->_maxItems = $maxItems; return $this; } public function getMinCount() { return $this->_minCount; } public function setMinCount($minPersons) { $this->_minCount = $minPersons; return $this; } public function getMaxCount() { return $this->_maxCount; } public function setMaxCount($maxPersons) { $this->_maxCount = $maxPersons; return $this; } public function setItemsName($itemsName) { $this->_itemsName = $itemsName; return $this; } public function getItemClass() { return $this->_itemClass; } public function setItemClass($itemClass) { $this->_itemClass = $itemClass; return $this; } public function getItemOptions() { return $this->_itemOptions; } public function setItemOptions(array $itemOptions) { $this->_itemOptions = $itemOptions; return $this; } public function getSpecificRowOptions($index = null) { return Qs_Array::get($this->_specificRowOptions, $index, []); } public function setSpecificRowOptions($specificRowOptions) { $this->_specificRowOptions = $specificRowOptions; return $this; } public function isRequired() { return $this->_required; } public function setRequired($required = true) { $this->_required = $required; return $this; } public function getDescription() { return $this->_description; } public function setDescription($description) { $this->_description = $description; return $this; } protected function _initElements() { $index = $this->getStartIndex(); $i = 0; $count = count($this->getRows()); foreach ($this->getRows() as $name => $defaults) { $first = $i === 0; $last = $i === $count - 1; $this->addSubForm($this->createRow(compact('name', 'index', 'defaults', 'first', 'last')), $name); $index++; $i++; } $this->addAddNewButton(); return parent::_initElements(); } public function createRow(array $options, $itemClass = null) { $options = array_merge( $this->getItemOptions(), $this->getSpecificRowOptions(Qs_Array::get($options, 'index')), [ 'orientation' => $this->getOrientation(), 'sortable' => $this->isSortable(), 'numerable' => $this->isNumerable(), 'removable' => $this->isRemovable(), ], $options ); if (null === $itemClass) { $itemClass = $this->_itemClass; } return new $itemClass($options); } protected function getRows() { return $this->_defaults; } protected function addAddNewButton() { if ($this->fixedRows) { return $this; } $decorators = [ 'ViewHelper', 'Errors', [ ['td' => 'HtmlTag'], [ 'tag' => 'td', 'class' => 'bottom-row', 'colspan' => $this->getColumnsCount(), 'id' => ['callback' => ['Zend_Form_Element', 'resolveElementId']], ], ], [['tr' => 'HtmlTag'], ['tag' => 'tr']], ]; $this->addElement( 'button', 'btnAddNew', [ 'label' => 'Add New ' . $this->getItemName(), 'data-action-add' => '', 'class' => 'btn btn-default btn-small', 'decorators' => $decorators, ] ); return $this; } protected function getColumnsCount() { if (null === $this->_columnsCount) { $this->_columnsCount = ($form = $this->getItemSubForm()) ? $form->count() : 1; } return $this->_columnsCount; } /** * @return Table\Row * @throws \Exception */ public function getItemSubForm() { if (null === $this->_itemSubForm) { $this->_itemSubForm = current($this->getSubForms()); if (!$this->_itemSubForm) { if (null === $this->_itemClass) { throw new Exception('itemClass is not deifned'); } $this->_itemSubForm = new $this->_itemClass(); if (!$this->_itemSubForm instanceof Table\Row) { throw new Exception('itemClass should be instance of ' . Table\Row::CLASS); } } } return $this->_itemSubForm; } public function isFixedRows() { return $this->fixedRows; } public function setFixedRows($fixedRows) { $this->fixedRows = $fixedRows; return $this; } public function loadDefaultDecorators() { if ($this->loadDefaultDecoratorsIsDisabled()) { return $this; } if (($decorators = $this->getDecorators())) { return $this; } $labelAttribs = $this->getLabelAttribs(); $labelAttribs['class'] = Html::addClass( Qs_Array::get($labelAttribs, 'class'), $this->isRequired() ? 'required' : 'optional' ); if (!$this->getLabel()) { $labelAttribs['class'] .= ' hidden'; } if ($this->getOrientation() == self::ORIENTATION_HORIZONTAL && $this->isEnabledHeaders()) { $this->addDecorator('TableHeader'); $this->addDecorator(['theadTr' => 'HtmlTag'], ['tag' => 'tr']); $this->addDecorator(['thead' => 'HtmlTag'], ['tag' => 'thead']); } $this->addDecorator(['tbodyOpen' => 'HtmlTag'], ['tag' => 'tbody', 'openOnly' => true, 'placement' => 'append']); $this->addDecorator('FormElements'); $this->addDecorator(['tbodyClose' => 'HtmlTag'], ['tag' => 'tbody', 'closeOnly' => true, 'placement' => 'append']); $self = $this; $tableAttribs = [ 'tag' => 'table', 'id' => ['callback' => function () use ($self) { return $self->getId(); }], 'class' => 'table table-bordered table-striped table-form', ]; if (empty($this->getRows())) { $tableAttribs['class'] .= ' table-empty'; } $this->addDecorator(['table' => 'HtmlTag'], $tableAttribs); $this->addDecorator(['table-wrapper' => 'HtmlTag'], ['tag' => 'div', 'class' => 'table-responsive']); $this->addDecorator('Description'); $this->addDecorator('FormErrorMessages'); $this->addDecorator('DtDdWrapper', ['label' => $this->getLabel(), 'labelAttribs' => $labelAttribs]); return $this; } protected function _addResources() { parent::_addResources(); $options = [ 'id' => $this->getId(), 'name' => $this->getElementsBelongTo(), 'itemName' => $this->getItemName(), 'maxItems' => $this->getMaxItems(), 'sortable' => $this->isSortable(), 'numerable' => $this->isNumerable(), 'startIndex' => $this->getStartIndex(), 'removable' => $this->isRemovable(), 'nodes' => [ 'items' => '>tbody>[data-collection-item]', 'add' => '>tbody>tr>td>[data-action-add]', ], ]; if ($this->isSortable()) { $options['nodes']['moveUp'] = '>td>[data-action-up]'; $options['nodes']['moveDown'] = '>td>[data-action-down]'; } if ($this->isRemovable()) { $options['nodes']['delete'] = '>td>[data-action-delete]'; } if ($this->isNumerable()) { $options['nodes']['rowNumber'] = '>[data-row-number]'; } $this->getDoc()->addInitObject('qs.form.subform.Table', [$options]); // Add related scripts if table has no rows if (empty($this->_defaults)) { $this->_addRelatedResources($this->createRow([])); } return $this; } protected function _addRelatedResources($item) { if ($item instanceof ResourceInterface && ($scripts = $item->getScripts())) { $this->getDoc()->addScripts($scripts); } if ($item instanceof Row) { /** @var Zend_Form_Element $element */ foreach ($item->getElements() as $element) { foreach ($element->getDecorators() as $decorator) { if ($decorator instanceof ResourceInterface && ($scripts = $decorator->getScripts())) { $this->getDoc()->addScripts($scripts); } } } foreach ($item->getSubForms() as $subForm) { $this->_addRelatedResources($subForm); } } if ($item instanceof self) { $this->_addRelatedResources($item->getItemSubForm()); } return $this; } public function isValid($data) { $isValid = $this->isValidTrait($data); return $this->areValidRules($data) && $isValid; } public function isValidPartial(array $data) { $isValid = $this->isValidPartialTrait($data); return $this->areValidRules($data) && $isValid; } protected function areValidRules(array $data) { $isValid = $this->isValidPersonsCount($data); return $isValid; } protected function isValidPersonsCount(array $data) { $count = count($data); if ($this->getMinCount() && $count < $this->getMinCount() || $this->getMaxCount() && $count > $this->getMaxCount() ) { if ($this->getMinCount()) { if ($this->getMaxCount()) { $this->addError(Qs_String::fill('You must provide data for at least {min} and up to {max} {itemsName}', [ 'min' => $this->getMinCount(), 'max' => $this->getMaxCount(), 'itemsName' => $this->getItemsName(), ])); } else { $this->addError(Qs_String::fill('You must provide data for at least {min} {itemName}', [ 'min' => $this->getMinCount(), 'itemName' => $this->getItemNamePlural($this->getMinCount()), ])); } } else if ($this->getMaxCount()) { $this->addError(Qs_String::fill('You must provide data for up to {max} persons', [ 'max' => $this->getMaxCount(), 'itemName' => $this->getItemNamePlural($this->getMaxCount()), ])); } return false; } return true; } }