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

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