Source of file Collection.php
Size: 17,174 Bytes - Last Modified: 2014-03-12T23:21:18+01:00
/home/theseer/Downloads/ZendFramework-2.3.0/library/Zend/Form/Element/Collection.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630 | <?php /** * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ namespace Zend\Form\Element; use Traversable; use Zend\Form\Element; use Zend\Form\ElementInterface; use Zend\Form\Exception; use Zend\Form\Fieldset; use Zend\Form\FieldsetInterface; use Zend\Form\FormInterface; use Zend\Stdlib\ArrayUtils; class Collection extends Fieldset { /** * Default template placeholder */ const DEFAULT_TEMPLATE_PLACEHOLDER = '__index__'; /** * Element used in the collection * * @var ElementInterface */ protected $targetElement; /** * Initial count of target element * * @var int */ protected $count = 1; /** * Are new elements allowed to be added dynamically ? * * @var bool */ protected $allowAdd = true; /** * Are existing elements allowed to be removed dynamically ? * * @var bool */ protected $allowRemove = true; /** * Is the template generated ? * * @var bool */ protected $shouldCreateTemplate = false; /** * Placeholder used in template content for making your life easier with JavaScript * * @var string */ protected $templatePlaceholder = self::DEFAULT_TEMPLATE_PLACEHOLDER; /** * Whether or not to create new objects during modify * * @var bool */ protected $createNewObjects = false; /** * Element used as a template * * @var ElementInterface|FieldsetInterface */ protected $templateElement; /** * The index of the last child element or fieldset * * @var int */ protected $lastChildIndex = -1; /** * Should child elements must be created on self::prepareElement()? * * @var bool */ protected $shouldCreateChildrenOnPrepareElement = true; /** * Accepted options for Collection: * - target_element: an array or element used in the collection * - count: number of times the element is added initially * - allow_add: if set to true, elements can be added to the form dynamically (using JavaScript) * - allow_remove: if set to true, elements can be removed to the form * - should_create_template: if set to true, a template is generated (inside a <span>) * - template_placeholder: placeholder used in the data template * * @param array|Traversable $options * @return Collection */ public function setOptions($options) { parent::setOptions($options); if (isset($options['target_element'])) { $this->setTargetElement($options['target_element']); } if (isset($options['count'])) { $this->setCount($options['count']); } if (isset($options['allow_add'])) { $this->setAllowAdd($options['allow_add']); } if (isset($options['allow_remove'])) { $this->setAllowRemove($options['allow_remove']); } if (isset($options['should_create_template'])) { $this->setShouldCreateTemplate($options['should_create_template']); } if (isset($options['template_placeholder'])) { $this->setTemplatePlaceholder($options['template_placeholder']); } if (isset($options['create_new_objects'])) { $this->setCreateNewObjects($options['create_new_objects']); } return $this; } /** * Checks if the object can be set in this fieldset * * @param object $object * @return bool */ public function allowObjectBinding($object) { return true; } /** * Set the object used by the hydrator * In this case the "object" is a collection of objects * * @param array|Traversable $object * @return Fieldset|FieldsetInterface * @throws Exception\InvalidArgumentException */ public function setObject($object) { if (!is_array($object) && !$object instanceof Traversable) { throw new Exception\InvalidArgumentException(sprintf( '%s expects an array or Traversable object argument; received "%s"', __METHOD__, (is_object($object) ? get_class($object) : gettype($object)) )); } $this->object = $object; $this->count = count($object); return $this; } /** * Populate values * * @param array|Traversable $data * @throws \Zend\Form\Exception\InvalidArgumentException * @throws \Zend\Form\Exception\DomainException * @return void */ public function populateValues($data) { if (!is_array($data) && !$data instanceof Traversable) { throw new Exception\InvalidArgumentException(sprintf( '%s expects an array or Traversable set of data; received "%s"', __METHOD__, (is_object($data) ? get_class($data) : gettype($data)) )); } // Can't do anything with empty data if (empty($data)) { return; } if (!$this->allowRemove && count($data) < $this->count) { throw new Exception\DomainException(sprintf( 'There are fewer elements than specified in the collection (%s). Either set the allow_remove option ' . 'to true, or re-submit the form.', get_class($this) ) ); } // Check to see if elements have been replaced or removed foreach ($this->byName as $name => $elementOrFieldset) { if (isset($data[$name])) { continue; } if (!$this->allowRemove) { throw new Exception\DomainException(sprintf( 'Elements have been removed from the collection (%s) but the allow_remove option is not true.', get_class($this) )); } $this->remove($name); } foreach ($data as $key => $value) { if ($this->has($key)) { $elementOrFieldset = $this->get($key); } else { $elementOrFieldset = $this->addNewTargetElementInstance($key); if ($key > $this->lastChildIndex) { $this->lastChildIndex = $key; } } if ($elementOrFieldset instanceof FieldsetInterface) { $elementOrFieldset->populateValues($value); } else { $elementOrFieldset->setAttribute('value', $value); } } if (!$this->createNewObjects()) { $this->replaceTemplateObjects(); } } /** * Checks if this fieldset can bind data * * @return bool */ public function allowValueBinding() { return true; } /** * Bind values to the object * * @param array $values * @return array|mixed|void */ public function bindValues(array $values = array()) { $collection = array(); foreach ($values as $name => $value) { $element = $this->get($name); if ($element instanceof FieldsetInterface) { $collection[] = $element->bindValues($value); } else { $collection[] = $value; } } return $collection; } /** * Set the initial count of target element * * @param $count * @return Collection */ public function setCount($count) { $this->count = $count > 0 ? $count : 0; return $this; } /** * Get the initial count of target element * * @return int */ public function getCount() { return $this->count; } /** * Set the target element * * @param ElementInterface|array|Traversable $elementOrFieldset * @return Collection * @throws \Zend\Form\Exception\InvalidArgumentException */ public function setTargetElement($elementOrFieldset) { if (is_array($elementOrFieldset) || ($elementOrFieldset instanceof Traversable && !$elementOrFieldset instanceof ElementInterface) ) { $factory = $this->getFormFactory(); $elementOrFieldset = $factory->create($elementOrFieldset); } if (!$elementOrFieldset instanceof ElementInterface) { throw new Exception\InvalidArgumentException(sprintf( '%s requires that $elementOrFieldset be an object implementing %s; received "%s"', __METHOD__, __NAMESPACE__ . '\ElementInterface', (is_object($elementOrFieldset) ? get_class($elementOrFieldset) : gettype($elementOrFieldset)) )); } $this->targetElement = $elementOrFieldset; return $this; } /** * Get target element * * @return ElementInterface|null */ public function getTargetElement() { return $this->targetElement; } /** * Get allow add * * @param bool $allowAdd * @return Collection */ public function setAllowAdd($allowAdd) { $this->allowAdd = (bool) $allowAdd; return $this; } /** * Get allow add * * @return bool */ public function allowAdd() { return $this->allowAdd; } /** * @param bool $allowRemove * @return Collection */ public function setAllowRemove($allowRemove) { $this->allowRemove = (bool) $allowRemove; return $this; } /** * @return bool */ public function allowRemove() { return $this->allowRemove; } /** * If set to true, a template prototype is automatically added to the form to ease the creation of dynamic elements through JavaScript * * @param bool $shouldCreateTemplate * @return Collection */ public function setShouldCreateTemplate($shouldCreateTemplate) { $this->shouldCreateTemplate = (bool) $shouldCreateTemplate; return $this; } /** * Get if the collection should create a template * * @return bool */ public function shouldCreateTemplate() { return $this->shouldCreateTemplate; } /** * Set the placeholder used in the template generated to help create new elements in JavaScript * * @param string $templatePlaceholder * @return Collection */ public function setTemplatePlaceholder($templatePlaceholder) { if (is_string($templatePlaceholder)) { $this->templatePlaceholder = $templatePlaceholder; } return $this; } /** * Get the template placeholder * * @return string */ public function getTemplatePlaceholder() { return $this->templatePlaceholder; } /** * @param bool $createNewObjects * @return Collection */ public function setCreateNewObjects($createNewObjects) { $this->createNewObjects = (bool) $createNewObjects; return $this; } /** * @return bool */ public function createNewObjects() { return $this->createNewObjects; } /** * Get a template element used for rendering purposes only * * @return null|ElementInterface|FieldsetInterface */ public function getTemplateElement() { if ($this->templateElement === null) { $this->templateElement = $this->createTemplateElement(); } return $this->templateElement; } /** * Prepare the collection by adding a dummy template element if the user want one * * @param FormInterface $form * @return mixed|void */ public function prepareElement(FormInterface $form) { if (true === $this->shouldCreateChildrenOnPrepareElement) { if ($this->targetElement !== null && $this->count > 0) { while ($this->count > $this->lastChildIndex + 1) { $this->addNewTargetElementInstance(++$this->lastChildIndex); } } } // Create a template that will also be prepared if ($this->shouldCreateTemplate) { $templateElement = $this->getTemplateElement(); $this->add($templateElement); } parent::prepareElement($form); // The template element has been prepared, but we don't want it to be rendered nor validated, so remove it from the list if ($this->shouldCreateTemplate) { $this->remove($this->templatePlaceholder); } } /** * @return array */ public function extract() { if ($this->object instanceof Traversable) { $this->object = ArrayUtils::iteratorToArray($this->object, false); } if (!is_array($this->object)) { return array(); } $values = array(); foreach ($this->object as $key => $value) { if ($this->hydrator) { $values[$key] = $this->hydrator->extract($value); } elseif ($value instanceof $this->targetElement->object) { // @see https://github.com/zendframework/zf2/pull/2848 $targetElement = clone $this->targetElement; $targetElement->object = $value; $values[$key] = $targetElement->extract(); if (!$this->createNewObjects() && $this->has($key)) { $fieldset = $this->get($key); if ($fieldset instanceof Fieldset && $fieldset->allowObjectBinding($value)) { $fieldset->setObject($value); } } } } foreach ($values as $name => $object) { $fieldset = $this->addNewTargetElementInstance($name); if ($fieldset->allowObjectBinding($object)) { $fieldset->setObject($object); $values[$name] = $fieldset->extract(); } else { foreach ($fieldset->fieldsets as $childFieldset) { $childName = $childFieldset->getName(); if (isset($object[$childName])) { $childObject = $object[$childName]; if ($childFieldset->allowObjectBinding($childObject)) { $childFieldset->setObject($childObject); $values[$name][$childName] = $childFieldset->extract(); } } } } } return $values; } /** * Create a new instance of the target element * * @return ElementInterface */ protected function createNewTargetElementInstance() { return clone $this->targetElement; } /** * Add a new instance of the target element * * @return ElementInterface * @throws Exception\DomainException */ protected function addNewTargetElementInstance($name) { $this->shouldCreateChildrenOnPrepareElement = false; $elementOrFieldset = $this->createNewTargetElementInstance(); $elementOrFieldset->setName($name); $this->add($elementOrFieldset); if (!$this->allowAdd && $this->count() > $this->count) { throw new Exception\DomainException(sprintf( 'There are more elements than specified in the collection (%s). Either set the allow_add option ' . 'to true, or re-submit the form.', get_class($this) )); } return $elementOrFieldset; } /** * Create a dummy template element * * @return null|ElementInterface|FieldsetInterface */ protected function createTemplateElement() { if (!$this->shouldCreateTemplate) { return null; } if ($this->templateElement) { return $this->templateElement; } $elementOrFieldset = $this->createNewTargetElementInstance(); $elementOrFieldset->setName($this->templatePlaceholder); return $elementOrFieldset; } /** * Replaces the default template object of a sub element with the corresponding * real entity so that all properties are preserved. * * @return void */ protected function replaceTemplateObjects() { $fieldsets = $this->getFieldsets(); if (!count($fieldsets) || !$this->object) { return; } foreach ($fieldsets as $fieldset) { $i = $fieldset->getName(); if (isset($this->object[$i])) { $fieldset->setObject($this->object[$i]); } } } } |