vendor/symfony/form/FormBuilder.php line 193

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