Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,44 @@

namespace Ibexa\AdminUi\Form\Type\Extension\EventSubscriber;

use Ibexa\AdminUi\Form\Data\FieldDefinitionData;
use Ibexa\AdminUi\Form\Type\FieldDefinition\FieldDefinitionType;
use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft;
use Ibexa\Contracts\Core\Specification\SpecificationInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;

/**
* Modifies CT editing form by rebuilding field definition list with custom options on given field type.
* Rebuilds specific field definitions in the Content Type editing form with custom options for a given field type and set of field identifiers.
*/
final class ModifyFieldDefinitionFieldsSubscriber implements EventSubscriberInterface
{
private string $fieldTypeIdentifier;
private ?string $fieldTypeIdentifier;

/** @var string[] */
private array $fieldIdentifiers;

/** @var array<string, mixed> */
private array $modifiedOptions;

private ?SpecificationInterface $contentTypeSpecification;

/**
* @param array<string, mixed> $modifiedOptions
* @param array<string> $fieldIdentifiers
*/
public function __construct(string $fieldTypeIdentifier, array $modifiedOptions)
{
public function __construct(
?string $fieldTypeIdentifier,
array $modifiedOptions,
array $fieldIdentifiers = [],
?SpecificationInterface $contentTypeSpecification = null
) {
$this->fieldTypeIdentifier = $fieldTypeIdentifier;
$this->modifiedOptions = $modifiedOptions;
$this->fieldIdentifiers = $fieldIdentifiers;
$this->contentTypeSpecification = $contentTypeSpecification;
}

public static function getSubscribedEvents(): array
Expand All @@ -45,30 +61,75 @@ public function onPreSetData(FormEvent $event): void
$data = $event->getData();
$form = $event->getForm();

if (null === $data) {
if (empty($data)) {
return;
}

if (!$this->isApplicableToContentTypeDraft($this->getContentTypeDraft($data))) {
return;
}

foreach ($data as $fieldTypeIdentifier => $fieldTypeData) {
if ($this->fieldTypeIdentifier !== $fieldTypeData->fieldDefinition->fieldTypeIdentifier) {
foreach ($data as $fieldIdentifier => $fieldTypeData) {
if (!$form->has($fieldIdentifier)) {
continue;
}

if (!$form->has($fieldTypeIdentifier)) {
return;
if (!$this->acceptsFieldDefinition($fieldTypeData, $fieldIdentifier)) {
continue;
}

$baseFieldForm = $form->get($fieldTypeIdentifier);
$baseFieldFormName = $baseFieldForm->getName();
$this->rebuildFieldForm($form, $fieldIdentifier);
}
}

$form->remove($baseFieldFormName);
private function isApplicableToContentTypeDraft(?ContentTypeDraft $contentTypeDraft): bool
{
if ($this->contentTypeSpecification === null) {
return true;
}

$options = array_merge(
$baseFieldForm->getConfig()->getOptions(),
$this->modifiedOptions
);
if ($contentTypeDraft === null) {
return false;
}

$form->add($baseFieldFormName, FieldDefinitionType::class, $options);
return $this->contentTypeSpecification->isSatisfiedBy($contentTypeDraft);
}

/**
* @param array<string, \Ibexa\AdminUi\Form\Data\FieldDefinitionData> $data
*/
private function getContentTypeDraft(array $data): ?ContentTypeDraft
{
$firstField = reset($data);
if ($firstField instanceof FieldDefinitionData && isset($firstField->contentTypeData)) {
return $firstField->contentTypeData->contentTypeDraft ?? null;
}

return null;
}

private function acceptsFieldDefinition(FieldDefinitionData $field, string $identifier): bool
{
$matchesType = $this->fieldTypeIdentifier === $field->getFieldTypeIdentifier();
$matchesIdentifier = in_array($identifier, $this->fieldIdentifiers, true);

return $matchesType || $matchesIdentifier;
}

/**
* @param \Symfony\Component\Form\FormInterface<\Ibexa\AdminUi\Form\Data\FieldDefinitionData[]> $form
*/
private function rebuildFieldForm(FormInterface $form, string $name): void
{
$baseFieldForm = $form->get($name);
$baseFieldFormName = $baseFieldForm->getName();

$options = array_merge(
$baseFieldForm->getConfig()->getOptions(),
$this->modifiedOptions
);

$form->remove($baseFieldFormName);
$form->add($baseFieldFormName, FieldDefinitionType::class, $options);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use Ibexa\AdminUi\Form\Type\ContentType\FieldDefinitionsCollectionType;
use Ibexa\AdminUi\Form\Type\Extension\EventSubscriber\ModifyFieldDefinitionFieldsSubscriber;
use Ibexa\Contracts\Core\Specification\SpecificationInterface;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;

Expand All @@ -20,24 +21,40 @@
*/
final class ModifyFieldDefinitionsCollectionTypeExtension extends AbstractTypeExtension
{
private string $fieldTypeIdentifier;
private ?string $fieldTypeIdentifier;

/** @var string[] */
private array $fieldIdentifiers;

/** @var array<string, mixed> */
private array $modifiedOptions;

private ?SpecificationInterface $contentTypeSpecification;

/**
* @param string $fieldTypeIdentifier
* @param array<string, mixed> $modifiedOptions
* @param array<string> $fieldIdentifiers
*/
public function __construct(string $fieldTypeIdentifier, array $modifiedOptions)
{
public function __construct(
?string $fieldTypeIdentifier,
array $modifiedOptions,
array $fieldIdentifiers = [],
?SpecificationInterface $contentTypeSpecification = null
) {
$this->fieldTypeIdentifier = $fieldTypeIdentifier;
$this->fieldIdentifiers = $fieldIdentifiers;
$this->modifiedOptions = $modifiedOptions;
$this->contentTypeSpecification = $contentTypeSpecification;
}

public function buildForm(FormBuilderInterface $builder, array $options): void
{
$subscriber = new ModifyFieldDefinitionFieldsSubscriber($this->fieldTypeIdentifier, $this->modifiedOptions);
$subscriber = new ModifyFieldDefinitionFieldsSubscriber(
$this->fieldTypeIdentifier,
$this->modifiedOptions,
$this->fieldIdentifiers,
$this->contentTypeSpecification
);

foreach ($builder->all() as $fieldTypeGroup) {
$fieldTypeGroup->addEventSubscriber($subscriber);
Expand Down
Loading