vendor/ibexa/taxonomy/src/lib/Event/Subscriber/ContentEditCustomValidationSubscriber.php line 59

Open in your IDE?
  1. <?php
  2. /**
  3. * @copyright Copyright (C) Ibexa AS. All rights reserved.
  4. * @license For full copyright and license information view LICENSE file distributed with this source code.
  5. */
  6. declare(strict_types=1);
  7. namespace Ibexa\Taxonomy\Event\Subscriber;
  8. use Ibexa\ContentForms\Event\ContentCreateFieldOptionsEvent;
  9. use Ibexa\ContentForms\Event\ContentFormEvents;
  10. use Ibexa\ContentForms\Event\ContentUpdateFieldOptionsEvent;
  11. use Ibexa\Contracts\ContentForms\Data\Content\FieldData;
  12. use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType;
  13. use Ibexa\Taxonomy\Exception\TaxonomyNotFoundException;
  14. use Ibexa\Taxonomy\Form\Type\Validation\Constraint\Nested;
  15. use Ibexa\Taxonomy\Form\Type\Validation\Constraint\UniqueIdentifier;
  16. use Ibexa\Taxonomy\Service\TaxonomyConfiguration;
  17. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  18. use Symfony\Component\Validator\Constraints\Length;
  19. use Symfony\Component\Validator\Constraints\NotBlank;
  20. /**
  21. * Adds custom validators to Taxonomy fields in Content update/create form.
  22. *
  23. * @internal
  24. */
  25. final class ContentEditCustomValidationSubscriber implements EventSubscriberInterface
  26. {
  27. private const MAX_IDENTIFIER_LENGTH = 190;
  28. private const MAX_NAME_LENGTH = 255;
  29. private TaxonomyConfiguration $taxonomyConfiguration;
  30. public function __construct(
  31. TaxonomyConfiguration $taxonomyConfiguration
  32. ) {
  33. $this->taxonomyConfiguration = $taxonomyConfiguration;
  34. }
  35. public static function getSubscribedEvents(): array
  36. {
  37. return [
  38. ContentFormEvents::CONTENT_EDIT_FIELD_OPTIONS => 'onEditFieldOptions',
  39. ContentFormEvents::CONTENT_CREATE_FIELD_OPTIONS => 'onCreateFieldOptions',
  40. ];
  41. }
  42. public function onEditFieldOptions(ContentUpdateFieldOptionsEvent $event): void
  43. {
  44. $contentType = $event->getContent()->getContentType();
  45. $event->setOptions(
  46. $this->attachValidator($contentType, $event->getFieldData(), $event->getOptions())
  47. );
  48. }
  49. public function onCreateFieldOptions(ContentCreateFieldOptionsEvent $event): void
  50. {
  51. $contentType = $event->getContentCreateStruct()->contentType;
  52. $event->setOptions(
  53. $this->attachValidator($contentType, $event->getFieldData(), $event->getOptions())
  54. );
  55. }
  56. /**
  57. * @param array<string, mixed> $options
  58. *
  59. * @return array<string, mixed>
  60. */
  61. private function attachValidator(ContentType $contentType, FieldData $fieldData, array $options): array
  62. {
  63. try {
  64. $taxonomy = $this->taxonomyConfiguration->getTaxonomyForContentType($contentType);
  65. $fieldMappings = $this->taxonomyConfiguration->getFieldMappings($taxonomy);
  66. } catch (TaxonomyNotFoundException $e) {
  67. return $options;
  68. }
  69. if ($fieldData->fieldDefinition->identifier === $fieldMappings['identifier']) {
  70. return $this->doAttachValidatorToIdentifier($options, $taxonomy);
  71. }
  72. if ($fieldData->fieldDefinition->identifier === $fieldMappings['name']) {
  73. return $this->doAttachValidatorToName($options);
  74. }
  75. return $options;
  76. }
  77. /**
  78. * @param array<string, mixed> $options
  79. *
  80. * @return array<string, mixed>
  81. */
  82. private function doAttachValidatorToName(array $options): array
  83. {
  84. $options['constraints'] = array_merge(
  85. $options['constraints'] ?? [],
  86. [
  87. $this->createNestedConstraints(
  88. [
  89. new NotBlank(),
  90. new Length(['max' => self::MAX_NAME_LENGTH]),
  91. ]
  92. ),
  93. ]
  94. );
  95. $options['error_mapping'] = array_merge(
  96. $options['error_mapping'] ?? [],
  97. ['.' => 'value']
  98. );
  99. return $options;
  100. }
  101. /**
  102. * @param array<string, mixed> $options
  103. *
  104. * @return array<string, mixed>
  105. */
  106. private function doAttachValidatorToIdentifier(array $options, string $taxonomy): array
  107. {
  108. $options['constraints'] = array_merge(
  109. $options['constraints'] ?? [],
  110. [
  111. $this->createNestedConstraints(
  112. [
  113. new NotBlank(),
  114. new Length(['max' => self::MAX_IDENTIFIER_LENGTH]),
  115. ]
  116. ),
  117. new UniqueIdentifier(['taxonomy' => $taxonomy]),
  118. ]
  119. );
  120. $options['error_mapping'] = array_merge(
  121. $options['error_mapping'] ?? [],
  122. ['.' => 'value']
  123. );
  124. return $options;
  125. }
  126. /**
  127. * @param \Symfony\Component\Validator\Constraint[] $constraints
  128. *
  129. * @return \Ibexa\Taxonomy\Form\Type\Validation\Constraint\Nested
  130. */
  131. private function createNestedConstraints(array $constraints): Nested
  132. {
  133. return new Nested(
  134. static function (?FieldData $value): ?string {
  135. if ($value === null || $value->value === null) {
  136. return null;
  137. }
  138. return $value->value->text;
  139. },
  140. $constraints
  141. );
  142. }
  143. }