/src/Symfony/Component/Form/FormBuilder.php

https://github.com/Exercise/symfony · PHP · 737 lines · 294 code · 102 blank · 341 comment · 17 complexity · 14368859f926d4be46a1caf0adf0121f MD5 · raw file

  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\Form\Exception\FormException;
  12. use Symfony\Component\Form\Exception\UnexpectedTypeException;
  13. use Symfony\Component\Form\Exception\CircularReferenceException;
  14. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  15. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  16. class FormBuilder
  17. {
  18. /**
  19. * @var string
  20. */
  21. private $name;
  22. /**
  23. * The form data in application format
  24. * @var mixed
  25. */
  26. private $appData;
  27. /**
  28. * The event dispatcher
  29. *
  30. * @var EventDispatcherInterface
  31. */
  32. private $dispatcher;
  33. /**
  34. * The form factory
  35. * @var FormFactoryInterface
  36. */
  37. private $factory;
  38. /**
  39. * @var Boolean
  40. */
  41. private $disabled;
  42. /**
  43. * @var Boolean
  44. */
  45. private $required;
  46. /**
  47. * The transformers for transforming from normalized to client format and
  48. * back
  49. * @var array An array of DataTransformerInterface
  50. */
  51. private $clientTransformers = array();
  52. /**
  53. * The transformers for transforming from application to normalized format
  54. * and back
  55. * @var array An array of DataTransformerInterface
  56. */
  57. private $normTransformers = array();
  58. /**
  59. * @var array An array of FormValidatorInterface
  60. */
  61. private $validators = array();
  62. /**
  63. * Key-value store for arbitrary attributes attached to the form
  64. * @var array
  65. */
  66. private $attributes = array();
  67. /**
  68. * @var array An array of FormTypeInterface
  69. */
  70. private $types = array();
  71. /**
  72. * @var string
  73. */
  74. private $dataClass;
  75. /**
  76. * The children of the form
  77. * @var array
  78. */
  79. private $children = array();
  80. /**
  81. * @var DataMapperInterface
  82. */
  83. private $dataMapper;
  84. /**
  85. * Whether added errors should bubble up to the parent
  86. * @var Boolean
  87. */
  88. private $errorBubbling = false;
  89. /**
  90. * Data used for the client data when no value is bound
  91. * @var mixed
  92. */
  93. private $emptyData = '';
  94. private $currentLoadingType;
  95. /**
  96. * The parent of this builder
  97. * @var FormBuilder
  98. */
  99. private $parent;
  100. /**
  101. * Constructor.
  102. *
  103. * @param string $name
  104. * @param FormFactoryInterface $factory
  105. * @param EventDispatcherInterface $dispatcher
  106. * @param string $dataClass
  107. */
  108. public function __construct($name, FormFactoryInterface $factory, EventDispatcherInterface $dispatcher, $dataClass = null)
  109. {
  110. $name = (string) $name;
  111. Form::validateName($name);
  112. $this->name = $name;
  113. $this->factory = $factory;
  114. $this->dispatcher = $dispatcher;
  115. $this->dataClass = $dataClass;
  116. }
  117. /**
  118. * Returns the associated form factory.
  119. *
  120. * @return FormFactoryInterface The factory
  121. */
  122. public function getFormFactory()
  123. {
  124. return $this->factory;
  125. }
  126. /**
  127. * Returns the name of the form.
  128. *
  129. * @return string The form name
  130. */
  131. public function getName()
  132. {
  133. return $this->name;
  134. }
  135. /**
  136. * Updates the field with default data.
  137. *
  138. * @param array $appData The data formatted as expected for the underlying object
  139. *
  140. * @return FormBuilder The current builder
  141. */
  142. public function setData($appData)
  143. {
  144. $this->appData = $appData;
  145. return $this;
  146. }
  147. /**
  148. * Returns the data in the format needed for the underlying object.
  149. *
  150. * @return mixed
  151. */
  152. public function getData()
  153. {
  154. return $this->appData;
  155. }
  156. /**
  157. * Set whether the form is disabled.
  158. *
  159. * @param Boolean $disabled Whether the form is disabled
  160. *
  161. * @return FormBuilder The current builder
  162. */
  163. public function setDisabled($disabled)
  164. {
  165. $this->disabled = (Boolean) $disabled;
  166. return $this;
  167. }
  168. /**
  169. * Returns whether the form is disabled.
  170. *
  171. * @return Boolean Whether the form is disabled
  172. */
  173. public function getDisabled()
  174. {
  175. return $this->disabled;
  176. }
  177. /**
  178. * Sets whether this field is required to be filled out when bound.
  179. *
  180. * @param Boolean $required
  181. *
  182. * @return FormBuilder The current builder
  183. */
  184. public function setRequired($required)
  185. {
  186. $this->required = (Boolean) $required;
  187. return $this;
  188. }
  189. /**
  190. * Returns whether this field is required to be filled out when bound.
  191. *
  192. * @return Boolean Whether this field is required
  193. */
  194. public function getRequired()
  195. {
  196. return $this->required;
  197. }
  198. /**
  199. * Sets whether errors bubble up to the parent.
  200. *
  201. * @param type $errorBubbling
  202. *
  203. * @return FormBuilder The current builder
  204. */
  205. public function setErrorBubbling($errorBubbling)
  206. {
  207. $this->errorBubbling = (Boolean) $errorBubbling;
  208. return $this;
  209. }
  210. /**
  211. * Returns whether errors bubble up to the parent.
  212. *
  213. * @return Boolean
  214. */
  215. public function getErrorBubbling()
  216. {
  217. return $this->errorBubbling;
  218. }
  219. /**
  220. * Adds a validator to the form.
  221. *
  222. * @param FormValidatorInterface $validator The validator
  223. *
  224. * @return FormBuilder The current builder
  225. */
  226. public function addValidator(FormValidatorInterface $validator)
  227. {
  228. $this->validators[] = $validator;
  229. return $this;
  230. }
  231. /**
  232. * Returns the validators used by the form.
  233. *
  234. * @return array An array of FormValidatorInterface
  235. */
  236. public function getValidators()
  237. {
  238. return $this->validators;
  239. }
  240. /**
  241. * Adds an event listener for events on this field
  242. *
  243. * @see Symfony\Component\EventDispatcher\EventDispatcherInterface::addListener
  244. *
  245. * @return FormBuilder The current builder
  246. */
  247. public function addEventListener($eventName, $listener, $priority = 0)
  248. {
  249. $this->dispatcher->addListener($eventName, $listener, $priority);
  250. return $this;
  251. }
  252. /**
  253. * Adds an event subscriber for events on this field
  254. *
  255. * @see Symfony\Component\EventDispatcher\EventDispatcherInterface::addSubscriber
  256. *
  257. * @return FormBuilder The current builder
  258. */
  259. public function addEventSubscriber(EventSubscriberInterface $subscriber)
  260. {
  261. $this->dispatcher->addSubscriber($subscriber);
  262. return $this;
  263. }
  264. /**
  265. * Appends a transformer to the normalization transformer chain
  266. *
  267. * @param DataTransformerInterface $normTransformer
  268. *
  269. * @return FormBuilder The current builder
  270. */
  271. public function appendNormTransformer(DataTransformerInterface $normTransformer)
  272. {
  273. $this->normTransformers[] = $normTransformer;
  274. return $this;
  275. }
  276. /**
  277. * Prepends a transformer to the normalization transformer chain
  278. *
  279. * @param DataTransformerInterface $normTransformer
  280. *
  281. * @return FormBuilder The current builder
  282. */
  283. public function prependNormTransformer(DataTransformerInterface $normTransformer)
  284. {
  285. array_unshift($this->normTransformers, $normTransformer);
  286. return $this;
  287. }
  288. /**
  289. * Clears the normalization transformers.
  290. *
  291. * @return FormBuilder The current builder
  292. */
  293. public function resetNormTransformers()
  294. {
  295. $this->normTransformers = array();
  296. return $this;
  297. }
  298. /**
  299. * Returns all the normalization transformers.
  300. *
  301. * @return array An array of DataTransformerInterface
  302. */
  303. public function getNormTransformers()
  304. {
  305. return $this->normTransformers;
  306. }
  307. /**
  308. * Appends a transformer to the client transformer chain
  309. *
  310. * @param DataTransformerInterface $clientTransformer
  311. *
  312. * @return FormBuilder The current builder
  313. */
  314. public function appendClientTransformer(DataTransformerInterface $clientTransformer)
  315. {
  316. $this->clientTransformers[] = $clientTransformer;
  317. return $this;
  318. }
  319. /**
  320. * Prepends a transformer to the client transformer chain
  321. *
  322. * @param DataTransformerInterface $clientTransformer
  323. *
  324. * @return FormBuilder The current builder
  325. */
  326. public function prependClientTransformer(DataTransformerInterface $clientTransformer)
  327. {
  328. array_unshift($this->clientTransformers, $clientTransformer);
  329. return $this;
  330. }
  331. /**
  332. * Clears the client transformers.
  333. *
  334. * @return FormBuilder The current builder
  335. */
  336. public function resetClientTransformers()
  337. {
  338. $this->clientTransformers = array();
  339. return $this;
  340. }
  341. /**
  342. * Returns all the client transformers.
  343. *
  344. * @return array An array of DataTransformerInterface
  345. */
  346. public function getClientTransformers()
  347. {
  348. return $this->clientTransformers;
  349. }
  350. /**
  351. * Sets the value for an attribute.
  352. *
  353. * @param string $name The name of the attribute
  354. * @param string $value The value of the attribute
  355. *
  356. * @return FormBuilder The current builder
  357. */
  358. public function setAttribute($name, $value)
  359. {
  360. $this->attributes[$name] = $value;
  361. return $this;
  362. }
  363. /**
  364. * Returns the value of the attributes with the given name.
  365. *
  366. * @param string $name The name of the attribute
  367. */
  368. public function getAttribute($name)
  369. {
  370. return $this->attributes[$name];
  371. }
  372. /**
  373. * Returns whether the form has an attribute with the given name.
  374. *
  375. * @param string $name The name of the attribute
  376. */
  377. public function hasAttribute($name)
  378. {
  379. return isset($this->attributes[$name]);
  380. }
  381. /**
  382. * Returns all the attributes.
  383. *
  384. * @return array An array of attributes
  385. */
  386. public function getAttributes()
  387. {
  388. return $this->attributes;
  389. }
  390. /**
  391. * Sets the data mapper used by the form.
  392. *
  393. * @param DataMapperInterface $dataMapper
  394. *
  395. * @return FormBuilder The current builder
  396. */
  397. public function setDataMapper(DataMapperInterface $dataMapper = null)
  398. {
  399. $this->dataMapper = $dataMapper;
  400. return $this;
  401. }
  402. /**
  403. * Returns the data mapper used by the form.
  404. *
  405. * @return array An array of DataMapperInterface
  406. */
  407. public function getDataMapper()
  408. {
  409. return $this->dataMapper;
  410. }
  411. /**
  412. * Set the types.
  413. *
  414. * @param array $types An array FormTypeInterface
  415. *
  416. * @return FormBuilder The current builder
  417. */
  418. public function setTypes(array $types)
  419. {
  420. $this->types = $types;
  421. return $this;
  422. }
  423. /**
  424. * Return the types.
  425. *
  426. * @return array An array of FormTypeInterface
  427. */
  428. public function getTypes()
  429. {
  430. return $this->types;
  431. }
  432. /**
  433. * Sets the data used for the client data when no value is bound.
  434. *
  435. * @param mixed $emptyData
  436. */
  437. public function setEmptyData($emptyData)
  438. {
  439. $this->emptyData = $emptyData;
  440. return $this;
  441. }
  442. /**
  443. * Returns the data used for the client data when no value is bound.
  444. *
  445. * @return mixed
  446. */
  447. public function getEmptyData()
  448. {
  449. return $this->emptyData;
  450. }
  451. /**
  452. * Adds a new field to this group. A field must have a unique name within
  453. * the group. Otherwise the existing field is overwritten.
  454. *
  455. * If you add a nested group, this group should also be represented in the
  456. * object hierarchy.
  457. *
  458. * @param string|FormBuilder $child
  459. * @param string|FormTypeInterface $type
  460. * @param array $options
  461. *
  462. * @return FormBuilder The current builder
  463. */
  464. public function add($child, $type = null, array $options = array())
  465. {
  466. if ($child instanceof self) {
  467. $child->setParent($this);
  468. $this->children[$child->getName()] = $child;
  469. return $this;
  470. }
  471. if (!is_string($child)) {
  472. throw new UnexpectedTypeException($child, 'string or Symfony\Component\Form\FormBuilder');
  473. }
  474. if (null !== $type && !is_string($type) && !$type instanceof FormTypeInterface) {
  475. throw new UnexpectedTypeException($type, 'string or Symfony\Component\Form\FormTypeInterface');
  476. }
  477. if ($this->currentLoadingType && ($type instanceof FormTypeInterface ? $type->getName() : $type) == $this->currentLoadingType) {
  478. throw new CircularReferenceException(is_string($type) ? $this->getFormFactory()->getType($type) : $type);
  479. }
  480. $this->children[$child] = array(
  481. 'type' => $type,
  482. 'options' => $options,
  483. );
  484. return $this;
  485. }
  486. /**
  487. * Creates a form builder.
  488. *
  489. * @param string $name The name of the form or the name of the property
  490. * @param string|FormTypeInterface $type The type of the form or null if name is a property
  491. * @param array $options The options
  492. *
  493. * @return FormBuilder The builder
  494. */
  495. public function create($name, $type = null, array $options = array())
  496. {
  497. if (null === $type && !$this->dataClass) {
  498. $type = 'text';
  499. }
  500. if (null !== $type) {
  501. return $this->getFormFactory()->createNamedBuilder($type, $name, null, $options, $this);
  502. }
  503. return $this->getFormFactory()->createBuilderForProperty($this->dataClass, $name, null, $options, $this);
  504. }
  505. /**
  506. * Returns a child by name.
  507. *
  508. * @param string $name The name of the child
  509. *
  510. * @return FormBuilder The builder for the child
  511. *
  512. * @throws FormException if the given child does not exist
  513. */
  514. public function get($name)
  515. {
  516. if (!isset($this->children[$name])) {
  517. throw new FormException(sprintf('The field "%s" does not exist', $name));
  518. }
  519. if (!$this->children[$name] instanceof FormBuilder) {
  520. $this->children[$name] = $this->create(
  521. $name,
  522. $this->children[$name]['type'],
  523. $this->children[$name]['options']
  524. );
  525. }
  526. return $this->children[$name];
  527. }
  528. /**
  529. * Removes the field with the given name.
  530. *
  531. * @param string $name
  532. *
  533. * @return FormBuilder The current builder
  534. */
  535. public function remove($name)
  536. {
  537. if (isset($this->children[$name])) {
  538. if ($this->children[$name] instanceof self) {
  539. $this->children[$name]->setParent(null);
  540. }
  541. unset($this->children[$name]);
  542. }
  543. return $this;
  544. }
  545. /**
  546. * Returns whether a field with the given name exists.
  547. *
  548. * @param string $name
  549. *
  550. * @return Boolean
  551. */
  552. public function has($name)
  553. {
  554. return isset($this->children[$name]);
  555. }
  556. /**
  557. * Creates the form.
  558. *
  559. * @return Form The form
  560. */
  561. public function getForm()
  562. {
  563. $instance = new Form(
  564. $this->getName(),
  565. $this->buildDispatcher(),
  566. $this->getTypes(),
  567. $this->getClientTransformers(),
  568. $this->getNormTransformers(),
  569. $this->getDataMapper(),
  570. $this->getValidators(),
  571. $this->getRequired(),
  572. $this->getDisabled(),
  573. $this->getErrorBubbling(),
  574. $this->getEmptyData(),
  575. $this->getAttributes()
  576. );
  577. foreach ($this->buildChildren() as $child) {
  578. $instance->add($child);
  579. }
  580. if (null !== $this->getData()) {
  581. $instance->setData($this->getData());
  582. }
  583. return $instance;
  584. }
  585. public function setCurrentLoadingType($type)
  586. {
  587. $this->currentLoadingType = $type;
  588. }
  589. /**
  590. * Returns the parent builder.
  591. *
  592. * @return FormBuilder The parent builder
  593. */
  594. public function getParent()
  595. {
  596. return $this->parent;
  597. }
  598. /**
  599. * Sets the parent builder.
  600. *
  601. * @param FormBuilder $parent The parent builder
  602. *
  603. * @return FormBuilder The current builder
  604. */
  605. public function setParent(FormBuilder $parent = null)
  606. {
  607. $this->parent = $parent;
  608. return $this;
  609. }
  610. /**
  611. * Returns the event dispatcher.
  612. *
  613. * @return type
  614. */
  615. protected function buildDispatcher()
  616. {
  617. return $this->dispatcher;
  618. }
  619. /**
  620. * Creates the children.
  621. *
  622. * @return array An array of Form
  623. */
  624. protected function buildChildren()
  625. {
  626. $children = array();
  627. foreach ($this->children as $name => $builder) {
  628. if (!$builder instanceof FormBuilder) {
  629. $builder = $this->create($name, $builder['type'], $builder['options']);
  630. }
  631. $children[$builder->getName()] = $builder->getForm();
  632. }
  633. return $children;
  634. }
  635. }