FormBuilder.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Form;
  11. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  12. use Symfony\Component\Form\Exception\BadMethodCallException;
  13. use Symfony\Component\Form\Exception\InvalidArgumentException;
  14. use Symfony\Component\Form\Exception\UnexpectedTypeException;
  15. /**
  16. * A builder for creating {@link Form} instances.
  17. *
  18. * @author Bernhard Schussek <bschussek@gmail.com>
  19. */
  20. class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormBuilderInterface
  21. {
  22. /**
  23. * The children of the form builder.
  24. *
  25. * @var FormBuilderInterface[]
  26. */
  27. private $children = array();
  28. /**
  29. * The data of children who haven't been converted to form builders yet.
  30. *
  31. * @var array
  32. */
  33. private $unresolvedChildren = array();
  34. /**
  35. * Creates a new form builder.
  36. *
  37. * @param string $name
  38. * @param string $dataClass
  39. * @param EventDispatcherInterface $dispatcher
  40. * @param FormFactoryInterface $factory
  41. * @param array $options
  42. */
  43. public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher, FormFactoryInterface $factory, array $options = array())
  44. {
  45. parent::__construct($name, $dataClass, $dispatcher, $options);
  46. $this->setFormFactory($factory);
  47. }
  48. /**
  49. * {@inheritdoc}
  50. */
  51. public function add($child, $type = null, array $options = array())
  52. {
  53. if ($this->locked) {
  54. throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  55. }
  56. if ($child instanceof FormBuilderInterface) {
  57. $this->children[$child->getName()] = $child;
  58. // In case an unresolved child with the same name exists
  59. unset($this->unresolvedChildren[$child->getName()]);
  60. return $this;
  61. }
  62. if (!\is_string($child) && !\is_int($child)) {
  63. throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormBuilderInterface');
  64. }
  65. if (null !== $type && !\is_string($type) && !$type instanceof FormTypeInterface) {
  66. throw new UnexpectedTypeException($type, 'string or Symfony\Component\Form\FormTypeInterface');
  67. }
  68. // Add to "children" to maintain order
  69. $this->children[$child] = null;
  70. $this->unresolvedChildren[$child] = array(
  71. 'type' => $type,
  72. 'options' => $options,
  73. );
  74. return $this;
  75. }
  76. /**
  77. * {@inheritdoc}
  78. */
  79. public function create($name, $type = null, array $options = array())
  80. {
  81. if ($this->locked) {
  82. throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  83. }
  84. if (null === $type && null === $this->getDataClass()) {
  85. $type = 'Symfony\Component\Form\Extension\Core\Type\TextType';
  86. }
  87. if (null !== $type) {
  88. return $this->getFormFactory()->createNamedBuilder($name, $type, null, $options);
  89. }
  90. return $this->getFormFactory()->createBuilderForProperty($this->getDataClass(), $name, null, $options);
  91. }
  92. /**
  93. * {@inheritdoc}
  94. */
  95. public function get($name)
  96. {
  97. if ($this->locked) {
  98. throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  99. }
  100. if (isset($this->unresolvedChildren[$name])) {
  101. return $this->resolveChild($name);
  102. }
  103. if (isset($this->children[$name])) {
  104. return $this->children[$name];
  105. }
  106. throw new InvalidArgumentException(sprintf('The child with the name "%s" does not exist.', $name));
  107. }
  108. /**
  109. * {@inheritdoc}
  110. */
  111. public function remove($name)
  112. {
  113. if ($this->locked) {
  114. throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  115. }
  116. unset($this->unresolvedChildren[$name], $this->children[$name]);
  117. return $this;
  118. }
  119. /**
  120. * {@inheritdoc}
  121. */
  122. public function has($name)
  123. {
  124. if ($this->locked) {
  125. throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  126. }
  127. if (isset($this->unresolvedChildren[$name])) {
  128. return true;
  129. }
  130. if (isset($this->children[$name])) {
  131. return true;
  132. }
  133. return false;
  134. }
  135. /**
  136. * {@inheritdoc}
  137. */
  138. public function all()
  139. {
  140. if ($this->locked) {
  141. throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  142. }
  143. $this->resolveChildren();
  144. return $this->children;
  145. }
  146. /**
  147. * {@inheritdoc}
  148. */
  149. public function count()
  150. {
  151. if ($this->locked) {
  152. throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  153. }
  154. return \count($this->children);
  155. }
  156. /**
  157. * {@inheritdoc}
  158. */
  159. public function getFormConfig()
  160. {
  161. /** @var $config self */
  162. $config = parent::getFormConfig();
  163. $config->children = array();
  164. $config->unresolvedChildren = array();
  165. return $config;
  166. }
  167. /**
  168. * {@inheritdoc}
  169. */
  170. public function getForm()
  171. {
  172. if ($this->locked) {
  173. throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  174. }
  175. $this->resolveChildren();
  176. $form = new Form($this->getFormConfig());
  177. foreach ($this->children as $child) {
  178. // Automatic initialization is only supported on root forms
  179. $form->add($child->setAutoInitialize(false)->getForm());
  180. }
  181. if ($this->getAutoInitialize()) {
  182. // Automatically initialize the form if it is configured so
  183. $form->initialize();
  184. }
  185. return $form;
  186. }
  187. /**
  188. * {@inheritdoc}
  189. *
  190. * @return FormBuilderInterface[]
  191. */
  192. public function getIterator()
  193. {
  194. if ($this->locked) {
  195. throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  196. }
  197. return new \ArrayIterator($this->all());
  198. }
  199. /**
  200. * Converts an unresolved child into a {@link FormBuilder} instance.
  201. *
  202. * @param string $name The name of the unresolved child
  203. *
  204. * @return self The created instance
  205. */
  206. private function resolveChild($name)
  207. {
  208. $info = $this->unresolvedChildren[$name];
  209. $child = $this->create($name, $info['type'], $info['options']);
  210. $this->children[$name] = $child;
  211. unset($this->unresolvedChildren[$name]);
  212. return $child;
  213. }
  214. /**
  215. * Converts all unresolved children into {@link FormBuilder} instances.
  216. */
  217. private function resolveChildren()
  218. {
  219. foreach ($this->unresolvedChildren as $name => $info) {
  220. $this->children[$name] = $this->create($name, $info['type'], $info['options']);
  221. }
  222. $this->unresolvedChildren = array();
  223. }
  224. }