Source of file Fieldset.php
Size: 19,539 Bytes - Last Modified: 2014-03-12T23:21:18+01:00
/home/theseer/Downloads/ZendFramework-2.3.0/library/Zend/Form/Fieldset.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693 | <?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; use Traversable; use Zend\Code\Reflection\ClassReflection; use Zend\Stdlib\Hydrator; use Zend\Stdlib\Hydrator\HydratorAwareInterface; use Zend\Stdlib\Hydrator\HydratorInterface; use Zend\Stdlib\PriorityQueue; class Fieldset extends Element implements FieldsetInterface { /** * @var Factory */ protected $factory; /** * @var ElementInterface[] */ protected $byName = array(); /** * @var array */ protected $elements = array(); /** * @var array */ protected $fieldsets = array(); /** * @var array */ protected $messages = array(); /** * @var PriorityQueue */ protected $iterator; /** * Hydrator to use with bound object * * @var Hydrator\HydratorInterface */ protected $hydrator; /** * The object bound to this fieldset, if any * * @var null|object */ protected $object; /** * Should this fieldset be used as a base fieldset in the parent form ? * * @var bool */ protected $useAsBaseFieldset = false; /** * The class or interface of objects that can be bound to this fieldset. * * @var string */ protected $allowedObjectBindingClass; /** * @param null|int|string $name Optional name for the element * @param array $options Optional options for the element */ public function __construct($name = null, $options = array()) { $this->iterator = new PriorityQueue(); parent::__construct($name, $options); } /** * Set options for a fieldset. Accepted options are: * - use_as_base_fieldset: is this fieldset use as the base fieldset? * * @param array|Traversable $options * @return Element|ElementInterface * @throws Exception\InvalidArgumentException */ public function setOptions($options) { parent::setOptions($options); if (isset($options['use_as_base_fieldset'])) { $this->setUseAsBaseFieldset($options['use_as_base_fieldset']); } if (isset($options['allowed_object_binding_class'])) { $this->setAllowedObjectBindingClass($options['allowed_object_binding_class']); } return $this; } /** * Compose a form factory to use when calling add() with a non-element/fieldset * * @param Factory $factory * @return Form */ public function setFormFactory(Factory $factory) { $this->factory = $factory; return $this; } /** * Retrieve composed form factory * * Lazy-loads one if none present. * * @return Factory */ public function getFormFactory() { if (null === $this->factory) { $this->setFormFactory(new Factory()); } return $this->factory; } /** * Add an element or fieldset * * $flags could contain metadata such as the alias under which to register * the element or fieldset, order in which to prioritize it, etc. * * @todo Should we detect if the element/fieldset name conflicts? * @param array|Traversable|ElementInterface $elementOrFieldset * @param array $flags * @return Fieldset|FieldsetInterface * @throws Exception\InvalidArgumentException */ public function add($elementOrFieldset, array $flags = array()) { 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)) )); } $name = $elementOrFieldset->getName(); if ((null === $name || '' === $name) && (!array_key_exists('name', $flags) || $flags['name'] === '') ) { throw new Exception\InvalidArgumentException(sprintf( '%s: element or fieldset provided is not named, and no name provided in flags', __METHOD__ )); } if (array_key_exists('name', $flags) && $flags['name'] !== '') { $name = $flags['name']; // Rename the element or fieldset to the specified alias $elementOrFieldset->setName($name); } $order = 0; if (array_key_exists('priority', $flags)) { $order = $flags['priority']; } $this->iterator->insert($elementOrFieldset, $order); $this->byName[$name] = $elementOrFieldset; if ($elementOrFieldset instanceof FieldsetInterface) { $this->fieldsets[$name] = $elementOrFieldset; return $this; } $this->elements[$name] = $elementOrFieldset; return $this; } /** * Does the fieldset have an element/fieldset by the given name? * * @param string $elementOrFieldset * @return bool */ public function has($elementOrFieldset) { return array_key_exists($elementOrFieldset, $this->byName); } /** * Retrieve a named element or fieldset * * @param string $elementOrFieldset * @return ElementInterface */ public function get($elementOrFieldset) { if (!$this->has($elementOrFieldset)) { throw new Exception\InvalidElementException(sprintf( "No element by the name of [%s] found in form", $elementOrFieldset )); } return $this->byName[$elementOrFieldset]; } /** * Remove a named element or fieldset * * @param string $elementOrFieldset * @return FieldsetInterface */ public function remove($elementOrFieldset) { if (!$this->has($elementOrFieldset)) { return $this; } $entry = $this->byName[$elementOrFieldset]; unset($this->byName[$elementOrFieldset]); $this->iterator->remove($entry); if ($entry instanceof FieldsetInterface) { unset($this->fieldsets[$elementOrFieldset]); return $this; } unset($this->elements[$elementOrFieldset]); return $this; } /** * Set/change the priority of an element or fieldset * * @param string $elementOrFieldset * @param int $priority * @return FieldsetInterface */ public function setPriority($elementOrFieldset, $priority) { $element = $this->get($elementOrFieldset); $this->remove($elementOrFieldset); $this->add($element, array('priority' => $priority)); return $this; } /** * Retrieve all attached elements * * Storage is an implementation detail of the concrete class. * * @return array|Traversable */ public function getElements() { return $this->elements; } /** * Retrieve all attached fieldsets * * Storage is an implementation detail of the concrete class. * * @return array|Traversable */ public function getFieldsets() { return $this->fieldsets; } /** * Set a hash of element names/messages to use when validation fails * * @param array|Traversable $messages * @return Element|ElementInterface|FieldsetInterface * @throws Exception\InvalidArgumentException */ public function setMessages($messages) { if (!is_array($messages) && !$messages instanceof Traversable) { throw new Exception\InvalidArgumentException(sprintf( '%s expects an array or Traversable object of messages; received "%s"', __METHOD__, (is_object($messages) ? get_class($messages) : gettype($messages)) )); } foreach ($messages as $key => $messageSet) { if (!$this->has($key)) { continue; } $element = $this->get($key); $element->setMessages($messageSet); } return $this; } /** * Get validation error messages, if any * * Returns a hash of element names/messages for all elements failing * validation, or, if $elementName is provided, messages for that element * only. * * @param null|string $elementName * @return array|Traversable * @throws Exception\InvalidArgumentException */ public function getMessages($elementName = null) { if (null === $elementName) { $messages = array(); foreach ($this->byName as $name => $element) { $messageSet = $element->getMessages(); if (!is_array($messageSet) && !$messageSet instanceof Traversable || empty($messageSet)) { continue; } $messages[$name] = $messageSet; } return $messages; } if (!$this->has($elementName)) { throw new Exception\InvalidArgumentException(sprintf( 'Invalid element name "%s" provided to %s', $elementName, __METHOD__ )); } $element = $this->get($elementName); return $element->getMessages(); } /** * Ensures state is ready for use. Here, we append the name of the fieldsets to every elements in order to avoid * name clashes if the same fieldset is used multiple times * * @param FormInterface $form * @return mixed|void */ public function prepareElement(FormInterface $form) { $name = $this->getName(); foreach ($this->byName as $elementOrFieldset) { $elementOrFieldset->setName($name . '[' . $elementOrFieldset->getName() . ']'); // Recursively prepare elements if ($elementOrFieldset instanceof ElementPrepareAwareInterface) { $elementOrFieldset->prepareElement($form); } } } /** * Recursively populate values of attached elements and fieldsets * * @param array|Traversable $data * @return void * @throws Exception\InvalidArgumentException */ 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)) )); } foreach ($this->byName as $name => $elementOrFieldset) { $valueExists = array_key_exists($name, $data); if ($elementOrFieldset instanceof FieldsetInterface) { if ($valueExists && (is_array($data[$name]) || $data[$name] instanceof Traversable)) { $elementOrFieldset->populateValues($data[$name]); continue; } if ($elementOrFieldset instanceof Element\Collection) { if ($valueExists && null !== $data[$name]) { $elementOrFieldset->populateValues($data[$name]); continue; } /* This ensures that collections with allow_remove don't re-create child * elements if they all were removed */ $elementOrFieldset->populateValues(array()); continue; } } if ($valueExists) { $elementOrFieldset->setValue($data[$name]); } } } /** * Countable: return count of attached elements/fieldsets * * @return int */ public function count() { return $this->iterator->count(); } /** * IteratorAggregate: return internal iterator * * @return PriorityQueue */ public function getIterator() { return $this->iterator; } /** * Set the object used by the hydrator * * @param object $object * @return Fieldset|FieldsetInterface * @throws Exception\InvalidArgumentException */ public function setObject($object) { if (!is_object($object)) { throw new Exception\InvalidArgumentException(sprintf( '%s expects an object argument; received "%s"', __METHOD__, $object )); } $this->object = $object; return $this; } /** * Get the object used by the hydrator * * @return mixed */ public function getObject() { return $this->object; } /** * Set the class or interface of objects that can be bound to this fieldset. * * @param string $allowObjectBindingClass */ public function setAllowedObjectBindingClass($allowObjectBindingClass) { $this->allowedObjectBindingClass = $allowObjectBindingClass; } /** * Get The class or interface of objects that can be bound to this fieldset. * * @return string */ public function allowedObjectBindingClass() { return $this->allowedObjectBindingClass; } /** * Checks if the object can be set in this fieldset * * @param object $object * @return bool */ public function allowObjectBinding($object) { $validBindingClass = false; if (is_object($object) && $this->allowedObjectBindingClass()) { $objectClass = ltrim($this->allowedObjectBindingClass(), '\\'); $reflection = new ClassReflection($object); $validBindingClass = ( $reflection->getName() == $objectClass || $reflection->isSubclassOf($this->allowedObjectBindingClass()) ); } return ($validBindingClass || $this->object && $object instanceof $this->object); } /** * Set the hydrator to use when binding an object to the element * * @param HydratorInterface $hydrator * @return FieldsetInterface */ public function setHydrator(HydratorInterface $hydrator) { $this->hydrator = $hydrator; return $this; } /** * Get the hydrator used when binding an object to the fieldset * * If no hydrator is present and object implements HydratorAwareInterface, * hydrator will be retrieved from the object. * * Will lazy-load Hydrator\ArraySerializable if none is present. * * @return HydratorInterface */ public function getHydrator() { if (!$this->hydrator instanceof HydratorInterface) { if ($this->object instanceof HydratorAwareInterface) { $this->setHydrator($this->object->getHydrator()); } else { $this->setHydrator(new Hydrator\ArraySerializable()); } } return $this->hydrator; } /** * Checks if this fieldset can bind data * * @return bool */ public function allowValueBinding() { return is_object($this->object); } /** * Bind values to the bound object * * @param array $values * @return mixed|void */ public function bindValues(array $values = array()) { $hydrator = $this->getHydrator(); $hydratableData = array(); foreach ($values as $name => $value) { if (!$this->has($name)) { continue; } $element = $this->byName[$name]; if ($element instanceof FieldsetInterface && $element->allowValueBinding()) { $value = $element->bindValues($value); } $hydratableData[$name] = $value; } if (!empty($hydratableData)) { $this->object = $hydrator->hydrate($hydratableData, $this->object); } return $this->object; } /** * Set if this fieldset is used as a base fieldset * * @param bool $useAsBaseFieldset * @return Fieldset */ public function setUseAsBaseFieldset($useAsBaseFieldset) { $this->useAsBaseFieldset = (bool) $useAsBaseFieldset; return $this; } /** * Is this fieldset use as a base fieldset for a form ? * * @return bool */ public function useAsBaseFieldset() { return $this->useAsBaseFieldset; } /** * Extract values from the bound object * * @return array */ protected function extract() { if (!is_object($this->object)) { return array(); } $hydrator = $this->getHydrator(); if (!$hydrator instanceof Hydrator\HydratorInterface) { return array(); } $values = $hydrator->extract($this->object); if (!is_array($values)) { // Do nothing if the hydrator returned a non-array return array(); } // Recursively extract and populate values for nested fieldsets foreach ($this->fieldsets as $fieldset) { $name = $fieldset->getName(); if (isset($values[$name])) { $object = $values[$name]; if ($fieldset->allowObjectBinding($object)) { $fieldset->setObject($object); $values[$name] = $fieldset->extract(); } } } return $values; } /** * Make a deep clone of a fieldset * * @return void */ public function __clone() { $items = $this->iterator->toArray(PriorityQueue::EXTR_BOTH); $this->byName = array(); $this->elements = array(); $this->fieldsets = array(); $this->iterator = new PriorityQueue(); foreach ($items as $item) { $elementOrFieldset = clone $item['data']; $name = $elementOrFieldset->getName(); $this->iterator->insert($elementOrFieldset, $item['priority']); $this->byName[$name] = $elementOrFieldset; if ($elementOrFieldset instanceof FieldsetInterface) { $this->fieldsets[$name] = $elementOrFieldset; } elseif ($elementOrFieldset instanceof ElementInterface) { $this->elements[$name] = $elementOrFieldset; } } // Also make a deep copy of the object in case it's used within a collection if (is_object($this->object)) { $this->object = clone $this->object; } } } |