FormBuilder.php 16 KB

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