FormMapper.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <?php
  2. /*
  3. * This file is part of the Sonata Project package.
  4. *
  5. * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  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 Sonata\AdminBundle\Form;
  11. use Sonata\AdminBundle\Admin\AdminInterface;
  12. use Sonata\AdminBundle\Builder\FormContractorInterface;
  13. use Sonata\AdminBundle\Mapper\BaseGroupedMapper;
  14. use Symfony\Component\Form\FormBuilderInterface;
  15. /**
  16. * This class is use to simulate the Form API.
  17. *
  18. * @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
  19. */
  20. class FormMapper extends BaseGroupedMapper
  21. {
  22. /**
  23. * @var FormBuilderInterface
  24. */
  25. protected $formBuilder;
  26. /**
  27. * @param FormContractorInterface $formContractor
  28. * @param FormBuilderInterface $formBuilder
  29. * @param AdminInterface $admin
  30. */
  31. public function __construct(FormContractorInterface $formContractor, FormBuilderInterface $formBuilder, AdminInterface $admin)
  32. {
  33. parent::__construct($formContractor, $admin);
  34. $this->formBuilder = $formBuilder;
  35. }
  36. /**
  37. * {@inheritdoc}
  38. */
  39. public function reorder(array $keys)
  40. {
  41. $this->admin->reorderFormGroup($this->getCurrentGroupName(), $keys);
  42. return $this;
  43. }
  44. /**
  45. * @param string $name
  46. * @param string $type
  47. * @param array $options
  48. * @param array $fieldDescriptionOptions
  49. *
  50. * @return $this
  51. */
  52. public function add($name, $type = null, array $options = array(), array $fieldDescriptionOptions = array())
  53. {
  54. if ($this->apply !== null && !$this->apply) {
  55. return $this;
  56. }
  57. if ($name instanceof FormBuilderInterface) {
  58. $fieldName = $name->getName();
  59. } else {
  60. $fieldName = $name;
  61. }
  62. // "Dot" notation is not allowed as form name, but can be used as property path to access nested data.
  63. if (!$name instanceof FormBuilderInterface && !isset($options['property_path'])) {
  64. $options['property_path'] = $fieldName;
  65. // fix the form name
  66. $fieldName = $this->sanitizeFieldName($fieldName);
  67. }
  68. // change `collection` to `sonata_type_native_collection` form type to
  69. // avoid BC break problems
  70. if ($type === 'collection' || $type === 'Symfony\Component\Form\Extension\Core\Type\CollectionType') {
  71. // the field name is used to preserve Symfony <2.8 compatibility, the FQCN should be used instead
  72. $type = 'sonata_type_native_collection';
  73. }
  74. $label = $fieldName;
  75. $group = $this->addFieldToCurrentGroup($label);
  76. // Try to autodetect type
  77. if ($name instanceof FormBuilderInterface && null === $type) {
  78. $fieldDescriptionOptions['type'] = get_class($name->getType()->getInnerType());
  79. }
  80. if (!isset($fieldDescriptionOptions['type']) && is_string($type)) {
  81. $fieldDescriptionOptions['type'] = $type;
  82. }
  83. if ($group['translation_domain'] && !isset($fieldDescriptionOptions['translation_domain'])) {
  84. $fieldDescriptionOptions['translation_domain'] = $group['translation_domain'];
  85. }
  86. $fieldDescription = $this->admin->getModelManager()->getNewFieldDescriptionInstance(
  87. $this->admin->getClass(),
  88. $name instanceof FormBuilderInterface ? $name->getName() : $name,
  89. $fieldDescriptionOptions
  90. );
  91. // Note that the builder var is actually the formContractor:
  92. $this->builder->fixFieldDescription($this->admin, $fieldDescription, $fieldDescriptionOptions);
  93. if ($fieldName != $name) {
  94. $fieldDescription->setName($fieldName);
  95. }
  96. $this->admin->addFormFieldDescription($fieldName, $fieldDescription);
  97. if ($name instanceof FormBuilderInterface) {
  98. $this->formBuilder->add($name);
  99. } else {
  100. // Note that the builder var is actually the formContractor:
  101. $options = array_replace_recursive($this->builder->getDefaultOptions($type, $fieldDescription), $options);
  102. // be compatible with mopa if not installed, avoid generating an exception for invalid option
  103. // force the default to false ...
  104. if (!isset($options['label_render'])) {
  105. $options['label_render'] = false;
  106. }
  107. if (!isset($options['label'])) {
  108. $options['label'] = $this->admin->getLabelTranslatorStrategy()->getLabel($fieldDescription->getName(), 'form', 'label');
  109. }
  110. $help = null;
  111. if (isset($options['help'])) {
  112. $help = $options['help'];
  113. unset($options['help']);
  114. }
  115. $this->formBuilder->add($fieldDescription->getName(), $type, $options);
  116. if (null !== $help) {
  117. $this->admin->getFormFieldDescription($fieldDescription->getName())->setHelp($help);
  118. }
  119. }
  120. return $this;
  121. }
  122. /**
  123. * {@inheritdoc}
  124. */
  125. public function get($name)
  126. {
  127. $name = $this->sanitizeFieldName($name);
  128. return $this->formBuilder->get($name);
  129. }
  130. /**
  131. * {@inheritdoc}
  132. */
  133. public function has($key)
  134. {
  135. $key = $this->sanitizeFieldName($key);
  136. return $this->formBuilder->has($key);
  137. }
  138. /**
  139. * {@inheritdoc}
  140. */
  141. final public function keys()
  142. {
  143. return array_keys($this->formBuilder->all());
  144. }
  145. /**
  146. * {@inheritdoc}
  147. */
  148. public function remove($key)
  149. {
  150. $key = $this->sanitizeFieldName($key);
  151. $this->admin->removeFormFieldDescription($key);
  152. $this->admin->removeFieldFromFormGroup($key);
  153. $this->formBuilder->remove($key);
  154. return $this;
  155. }
  156. /**
  157. * Removes a group.
  158. *
  159. * @param string $group The group to delete
  160. * @param string $tab The tab the group belongs to, defaults to 'default'
  161. * @param bool $deleteEmptyTab Whether or not the Tab should be deleted, when the deleted group leaves the tab empty after deletion
  162. *
  163. * @return $this
  164. */
  165. public function removeGroup($group, $tab = 'default', $deleteEmptyTab = false)
  166. {
  167. $groups = $this->getGroups();
  168. // When the default tab is used, the tabname is not prepended to the index in the group array
  169. if ($tab !== 'default') {
  170. $group = $tab.'.'.$group;
  171. }
  172. if (isset($groups[$group])) {
  173. foreach ($groups[$group]['fields'] as $field) {
  174. $this->remove($field);
  175. }
  176. }
  177. unset($groups[$group]);
  178. $tabs = $this->getTabs();
  179. $key = array_search($group, $tabs[$tab]['groups']);
  180. if (false !== $key) {
  181. unset($tabs[$tab]['groups'][$key]);
  182. }
  183. if ($deleteEmptyTab && count($tabs[$tab]['groups']) == 0) {
  184. unset($tabs[$tab]);
  185. }
  186. $this->setTabs($tabs);
  187. $this->setGroups($groups);
  188. return $this;
  189. }
  190. /**
  191. * @return FormBuilderInterface
  192. */
  193. public function getFormBuilder()
  194. {
  195. return $this->formBuilder;
  196. }
  197. /**
  198. * @param string $name
  199. * @param mixed $type
  200. * @param array $options
  201. *
  202. * @return FormBuilderInterface
  203. */
  204. public function create($name, $type = null, array $options = array())
  205. {
  206. return $this->formBuilder->create($name, $type, $options);
  207. }
  208. /**
  209. * @param array $helps
  210. *
  211. * @return FormMapper
  212. */
  213. public function setHelps(array $helps = array())
  214. {
  215. foreach ($helps as $name => $help) {
  216. $this->addHelp($name, $help);
  217. }
  218. return $this;
  219. }
  220. /**
  221. * @param $name
  222. * @param $help
  223. *
  224. * @return FormMapper
  225. */
  226. public function addHelp($name, $help)
  227. {
  228. if ($this->admin->hasFormFieldDescription($name)) {
  229. $this->admin->getFormFieldDescription($name)->setHelp($help);
  230. }
  231. return $this;
  232. }
  233. /**
  234. * Symfony default form class sadly can't handle
  235. * form element with dots in its name (when data
  236. * get bound, the default dataMapper is a PropertyPathMapper).
  237. * So use this trick to avoid any issue.
  238. *
  239. * @param string $fieldName
  240. *
  241. * @return string
  242. */
  243. protected function sanitizeFieldName($fieldName)
  244. {
  245. return str_replace(array('__', '.'), array('____', '__'), $fieldName);
  246. }
  247. /**
  248. * {@inheritdoc}
  249. */
  250. protected function getGroups()
  251. {
  252. return $this->admin->getFormGroups();
  253. }
  254. /**
  255. * {@inheritdoc}
  256. */
  257. protected function setGroups(array $groups)
  258. {
  259. $this->admin->setFormGroups($groups);
  260. }
  261. /**
  262. * {@inheritdoc}
  263. */
  264. protected function getTabs()
  265. {
  266. return $this->admin->getFormTabs();
  267. }
  268. /**
  269. * {@inheritdoc}
  270. */
  271. protected function setTabs(array $tabs)
  272. {
  273. $this->admin->setFormTabs($tabs);
  274. }
  275. /**
  276. * {@inheritdoc}
  277. */
  278. protected function getName()
  279. {
  280. return 'form';
  281. }
  282. }