vendor/sulu/form-bundle/Form/Type/DynamicFormType.php line 30

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of Sulu.
  4. *
  5. * (c) Sulu GmbH
  6. *
  7. * This source file is subject to the MIT license that is bundled
  8. * with this source code in the file LICENSE.
  9. */
  10. namespace Sulu\Bundle\FormBundle\Form\Type;
  11. use Sulu\Bundle\FormBundle\Dynamic\Checksum;
  12. use Sulu\Bundle\FormBundle\Dynamic\FormFieldTypePool;
  13. use Sulu\Bundle\FormBundle\Dynamic\Types\FreeTextType;
  14. use Sulu\Bundle\FormBundle\Dynamic\Types\HeadlineType;
  15. use Sulu\Bundle\FormBundle\Dynamic\Types\SpacerType;
  16. use Sulu\Bundle\FormBundle\Entity\Dynamic;
  17. use Sulu\Bundle\FormBundle\Entity\Form;
  18. use Sulu\Bundle\FormBundle\Exception\FormNotFoundException;
  19. use Symfony\Component\Form\AbstractType;
  20. use Symfony\Component\Form\Extension\Core\Type\EmailType;
  21. use Symfony\Component\Form\Extension\Core\Type\HiddenType;
  22. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  23. use Symfony\Component\Form\FormBuilderInterface;
  24. use Symfony\Component\OptionsResolver\OptionsResolver;
  25. use Symfony\Component\Validator\Constraints\NotBlank;
  26. class DynamicFormType extends AbstractType
  27. {
  28. /**
  29. * @var FormFieldTypePool
  30. */
  31. private $typePool;
  32. /**
  33. * @var Checksum
  34. */
  35. private $checksum;
  36. /**
  37. * @var string|null
  38. */
  39. private $honeyPotField;
  40. /**
  41. * DynamicFormType constructor.
  42. */
  43. public function __construct(
  44. FormFieldTypePool $typePool,
  45. Checksum $checksum,
  46. ?string $honeyPotField = null
  47. ) {
  48. $this->typePool = $typePool;
  49. $this->checksum = $checksum;
  50. $this->honeyPotField = $honeyPotField;
  51. }
  52. /**
  53. * @return void
  54. */
  55. public function buildForm(FormBuilderInterface $builder, array $options)
  56. {
  57. /** @var Form $formEntity */
  58. $formEntity = $options['formEntity'];
  59. /** @var string $locale */
  60. $locale = $options['locale'];
  61. /** @var string $type */
  62. $type = $options['type'];
  63. /** @var string $typeId */
  64. $typeId = $options['typeId'];
  65. /** @var string $name */
  66. $name = $options['name'];
  67. if (!$translation = $formEntity->getTranslation($locale)) {
  68. throw new FormNotFoundException($formEntity->getId(), $locale);
  69. }
  70. $currentWidthValue = 0;
  71. $fields = $formEntity->getFields();
  72. foreach ($fields as $key => $field) {
  73. $fieldTranslation = $field->getTranslation($locale);
  74. if (!$fieldTranslation) {
  75. continue;
  76. }
  77. $options = [
  78. 'constraints' => [],
  79. 'attr' => [],
  80. 'translation_domain' => false,
  81. 'property_path' => 'data[' . $field->getKey() . ']',
  82. ];
  83. $title = $fieldTranslation->getTitle();
  84. $title = \is_string($title) ? \str_replace(['<!--p-->', '<!--/p-->'], [''], $title) : $title;
  85. $placeholder = $fieldTranslation->getPlaceholder();
  86. $width = $field->getWidth() ?: 'full';
  87. $nextField = null;
  88. $nextWidth = 'full';
  89. if (isset($fields[$key + 1])) {
  90. $nextWidth = $fields[$key + 1]->getWidth();
  91. }
  92. $lastWidth = $this->getLastWidth($currentWidthValue, $width, $nextWidth);
  93. $options['label'] = $title ?: false;
  94. $options['required'] = $field->getRequired();
  95. $options['attr']['width'] = $width;
  96. $options['attr']['widthNumber'] = $this->getItemWidthNumber($width);
  97. $options['attr']['lastWidth'] = $lastWidth;
  98. if ($placeholder) {
  99. $options['attr']['placeholder'] = $placeholder;
  100. }
  101. $formFieldType = $this->typePool->get($field->getType());
  102. // required
  103. if (
  104. !$formFieldType instanceof FreeTextType
  105. && !$formFieldType instanceof HeadlineType
  106. && !$formFieldType instanceof \Sulu\Bundle\FormBundle\Dynamic\Types\HiddenType
  107. && !$formFieldType instanceof SpacerType
  108. && $field->getRequired()
  109. ) {
  110. $options['constraints'][] = new NotBlank();
  111. }
  112. $formFieldType->build($builder, $field, $locale, $options);
  113. }
  114. // Add hidden locale. (de, en, ...)
  115. $builder->add('locale', HiddenType::class, [
  116. 'data' => $locale,
  117. 'mapped' => false,
  118. ]);
  119. // Add hidden type field. (page, article, event, blog, ...)
  120. $builder->add('type', HiddenType::class, [
  121. 'data' => $type,
  122. 'mapped' => false,
  123. ]);
  124. // Add hidden typeId field. (UUID, Database id, ...)
  125. $builder->add('typeId', HiddenType::class, [
  126. 'data' => $typeId,
  127. 'mapped' => false,
  128. ]);
  129. // Add hidden formId. (id, uuid,…)
  130. $builder->add('formId', HiddenType::class, [
  131. 'data' => $formEntity->getId(),
  132. 'mapped' => false,
  133. ]);
  134. // Add hidden formName field. (Name of "form_select"-content-type.)
  135. $builder->add('formName', HiddenType::class, [
  136. 'data' => $name,
  137. 'mapped' => false,
  138. ]);
  139. // Add hidden formName field. (Name of "form_select"-content-type.)
  140. $checksum = $this->checksum->get($type, $typeId, $formEntity->getId(), $name);
  141. $builder->add('checksum', HiddenType::class, [
  142. 'data' => $checksum,
  143. 'mapped' => false,
  144. ]);
  145. if ($this->honeyPotField) {
  146. $builder->add(
  147. \str_replace(' ', '_', \strtolower($this->honeyPotField)),
  148. EmailType::class,
  149. [
  150. 'label' => $this->honeyPotField,
  151. 'mapped' => false,
  152. 'block_prefix' => 'honeypot',
  153. 'required' => false,
  154. ]
  155. );
  156. }
  157. // Add submit button.
  158. $builder->add(
  159. 'submit',
  160. SubmitType::class,
  161. [
  162. 'label' => $translation->getSubmitLabel(),
  163. 'translation_domain' => false,
  164. 'attr' => [
  165. 'width' => 'full',
  166. 'widthNumber' => $this->getItemWidthNumber('full'),
  167. 'lastWidth' => true,
  168. ],
  169. ]
  170. );
  171. }
  172. /**
  173. * @return void
  174. */
  175. public function configureOptions(OptionsResolver $resolver)
  176. {
  177. $defaults = [];
  178. $defaults['csrf_protection'] = true;
  179. $defaults['csrf_field_name'] = '_token';
  180. $defaults['data_class'] = Dynamic::class;
  181. $resolver->setDefaults($defaults);
  182. $resolver->setRequired('locale');
  183. $resolver->setRequired('type');
  184. $resolver->setRequired('typeId');
  185. $resolver->setRequired('name');
  186. $resolver->setRequired('formEntity');
  187. }
  188. public function getBlockPrefix()
  189. {
  190. return 'dynamic';
  191. }
  192. private function getItemWidthNumber(string $width): int
  193. {
  194. switch ($width) {
  195. case 'one-sixth':
  196. $itemWidth = 2;
  197. break;
  198. case 'five-sixths':
  199. $itemWidth = 10;
  200. break;
  201. case 'one-quarter':
  202. $itemWidth = 3;
  203. break;
  204. case 'three-quarters':
  205. $itemWidth = 9;
  206. break;
  207. case 'one-third':
  208. $itemWidth = 4;
  209. break;
  210. case 'two-thirds':
  211. $itemWidth = 8;
  212. break;
  213. case 'half':
  214. $itemWidth = 6;
  215. break;
  216. case 'full':
  217. $itemWidth = 12;
  218. break;
  219. default:
  220. $itemWidth = 12;
  221. }
  222. return $itemWidth;
  223. }
  224. private function getLastWidth(int &$currentWidthValue, string $width, string $nextWidth): bool
  225. {
  226. $widthNumber = $this->getItemWidthNumber($width);
  227. $nextWidthNumber = $this->getItemWidthNumber($nextWidth);
  228. $currentWidthValue += $widthNumber;
  229. if (0 == $currentWidthValue % 12) {
  230. return true;
  231. }
  232. // if next item has no space in current row the current item is last
  233. if (($currentWidthValue % 12) + $nextWidthNumber > 12) {
  234. $currentWidthValue += 12 - $currentWidthValue % 12;
  235. return true;
  236. }
  237. return false;
  238. }
  239. }