<?php
namespace App\EventSubscriber;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
use App\Form\SpecificField\SpecificFieldBuilder;
use App\Form\Type\AbstractViewOrderAwareType;
use App\Model\Field\Field;
use App\Model\FieldType\FieldType;
use App\Model\SpecificField\SpecificField;
use App\Model\SpecificFieldsAwareInterface;
use App\Security\User;
use App\V4\Model\SpecificField\SpecificField as SpecificFieldV4;
use LogicException;
use ReflectionClass;
use ReflectionException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\Event\PostSubmitEvent;
use Symfony\Component\Form\Event\PreSetDataEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Security\Core\Security;
class SpecificFieldsAwareFormTypeEventSubscriber implements EventSubscriberInterface
{
/**
* @var ContextAwareCollectionDataProviderInterface
*/
private $collectionDataProvider;
/**
* @var SpecificFieldBuilder
*/
private $specificFieldBuilder;
/**
* @var Security
*/
private $security;
/**
* @var Field[]
*/
private $fields = [];
/**
* @param CollectionDataProviderInterface $collectionDataProvider
* @param SpecificFieldBuilder $specificFieldBuilder
* @param Security $security
*/
public function __construct(
CollectionDataProviderInterface $collectionDataProvider,
SpecificFieldBuilder $specificFieldBuilder,
Security $security
) {
$this->collectionDataProvider = $collectionDataProvider;
$this->specificFieldBuilder = $specificFieldBuilder;
$this->security = $security;
}
/**
* @return array
*/
public static function getSubscribedEvents(): array
{
return [
FormEvents::PRE_SET_DATA => 'onPreSetData',
FormEvents::POST_SUBMIT => 'onPostSubmit',
];
}
/**
* @param PreSetDataEvent $event
*
* @return bool
*/
public function supports(PreSetDataEvent $event): bool
{
return $event->getForm()->getConfig()->getType()->getInnerType() instanceof AbstractViewOrderAwareType;
}
/**
* @param PreSetDataEvent $event
*
* @return void
*
* @throws LogicException
* @throws ResourceClassNotSupportedException
* @throws ReflectionException
*/
public function onPreSetData(PreSetDataEvent $event): void
{
if (!$this->supports($event)) {
throw new LogicException(sprintf('%s should not be used with %s as it does not extends %s', __CLASS__, get_class($event->getForm()->getConfig()->getType()->getInnerType()), AbstractViewOrderAwareType::class));
}
$entity = $event->getForm()->getConfig()->getOption(AbstractViewOrderAwareType::FORM_CONFIG_SPECIFIC_FIELD_ENTITY);
$entityShortName = (new ReflectionClass($entity))->getShortName();
if (!isset($this->fields[$entityShortName])) {
$this->fields[$entityShortName] = $this->collectionDataProvider->getCollection(Field::class, null, [
'filters' => [
'entity' => $entityShortName,
],
]);
}
/** @var Field $field */
foreach ($this->fields[$entityShortName] as $field) {
foreach ($this->specificFieldBuilder->getSpecificFieldTypeBuilders() as $specificFieldTypeBuilder) {
if ($specificFieldTypeBuilder->supports($field)) {
$specificFieldTypeBuilder->build($event->getForm(), $event->getData(), $field);
continue 2;
}
}
throw new LogicException(sprintf('Unable to build specific field %s (%s)', $field->getId(), $field->getName()));
}
}
/**
* @param PostSubmitEvent $event
*
* @return void
*/
public function onPostSubmit(PostSubmitEvent $event): void
{
$entity = $event->getData();
if (!$entity instanceof SpecificFieldsAwareInterface) {
return;
}
foreach ($event->getForm()->all() as $formChild) {
if (!str_starts_with($formChild->getName(), 'sf_')) {
continue;
}
$fieldId = str_replace('sf_', '', $formChild->getName());
$specificField = $entity->getSpecificFieldByFieldId($fieldId);
if (!$specificField instanceof SpecificField && !$specificField instanceof SpecificFieldV4) {
/** @var User $user */
$user = $this->security->getUser();
/** @var SpecificFieldV4|SpecificField $specificField */
$specificFieldClass = $entity::SPECIFIC_FIELD_CLASS_NAME;
$specificField = new $specificFieldClass();
$specificField->setFieldId($fieldId);
$specificField->setCustomerId($user->getCustomerId());
$specificField->setFieldType(
$formChild->getConfig()->getOption('choices', false) && !$formChild->getConfig()->getOption('multiple', false)
? FieldType::FIELD_TYPE_CHOICE
: null
);
$entity->addSpecificField($specificField);
}
$specificField->setValue(is_array($formChild->getViewData()) ? implode(',', $formChild->getViewData()) : $formChild->getViewData());
}
}
}