<?php
namespace App\V4\EventSubscriber;
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
use App\Model\SpecificFieldsAwareInterface;
use App\Model\ViewOrderInfo\ViewOrderInfo;
use App\Service\ApiWebServiceFilterBuilder\PaginationFilterBuilder;
use App\Service\Form\FormUtils;
use App\Service\PreSendSerializer;
use App\Service\ViewOrders\ViewOrdersManager;
use App\Transformer\EntityToFormTypeTransformer;
use App\V4\DataPersister\AbstractWithoutRequestDataPersister;
use App\V4\Event\PostPersistEvent;
use App\V4\Logger\SentryLogger;
use App\V4\Model\ChildrenAwareInterface;
use App\V4\Model\Quote\Quote;
use App\V4\Model\QuoteState\QuoteState;
use App\V4\Model\SpecificField\SpecificField;
use DateTime;
use Doctrine\Common\Annotations\AnnotationException;
use Exception;
use Psr\Log\LogLevel;
use ReflectionClass;
use ReflectionException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
class SynchronizeChildrenFieldsEventSubscriber extends AbstractSubscriber implements EventSubscriberInterface
{
use FormUtils;
/**
* @var CollectionDataProviderInterface
*/
private $collectionDataProvider;
/**
* @var DataPersisterInterface
*/
private $dataPersister;
/**
* @var ViewOrdersManager
*/
private $viewOrdersManager;
/**
* @var FormFactoryInterface
*/
private $formFactory;
/**
* @var PreSendSerializer
*/
private $serializer;
/**
* @var SentryLogger
*/
private $sentryLogger;
/**
* @param CollectionDataProviderInterface $collectionDataProvider
* @param DataPersisterInterface $dataPersister
* @param ViewOrdersManager $viewOrdersManager
* @param FormFactoryInterface $formFactory
* @param PreSendSerializer $serializer
* @param SentryLogger $sentryLogger
*/
public function __construct(
CollectionDataProviderInterface $collectionDataProvider,
DataPersisterInterface $dataPersister,
ViewOrdersManager $viewOrdersManager,
FormFactoryInterface $formFactory,
PreSendSerializer $serializer,
SentryLogger $sentryLogger
) {
$this->collectionDataProvider = $collectionDataProvider;
$this->dataPersister = $dataPersister;
$this->viewOrdersManager = $viewOrdersManager;
$this->formFactory = $formFactory;
$this->serializer = $serializer;
$this->sentryLogger = $sentryLogger;
}
/**
* @param $entity
*
* @return bool
*/
public function supports($entity): bool
{
return $entity instanceof ChildrenAwareInterface
&& $entity->isParent()
&& method_exists($entity, 'getId')
&& null !== $entity->getId()
;
}
/**
* @return string[]
*/
public static function getSubscribedEvents(): array
{
return [
PostPersistEvent::NAME => 'onPostPersist',
];
}
/**
* @param PostPersistEvent $event
*
* @throws AnnotationException
* @throws ClientExceptionInterface
* @throws DecodingExceptionInterface
* @throws ExceptionInterface
* @throws RedirectionExceptionInterface
* @throws ServerExceptionInterface
* @throws TransportExceptionInterface
* @throws ResourceClassNotSupportedException
* @throws ReflectionException
* @throws Exception
*/
public function onPostPersist(PostPersistEvent $event): void
{
$objPersisted = $event->getAfter();
if (!$this->supports($objPersisted) || $this->isIdAlreadyManaged($objPersisted->getId(), get_class($this))) {
return;
}
$rc = new ReflectionClass($objPersisted);
$voiParentFields = $this->viewOrdersManager->getParentFieldNames($rc->getShortName());
if (empty($voiParentFields)) {
return;
}
$children = $this->collectionDataProvider->getCollection($rc->getName(), null, [
'filters' => [
PaginationFilterBuilder::ACTIVATE_PAGNIATION_KEY => false,
'parent' => $objPersisted->getId(),
],
]);
/** @var SpecificFieldsAwareInterface $child */
foreach ($children as $child) {
$updateData = [];
/* @var ViewOrderInfo $voParentField */
foreach ($voiParentFields as $voiParentField) {
$getter = 'get'.ucfirst($voiParentField->getFieldKey());
if (method_exists($event->getAfter(), $getter)) {
$value = $objPersisted->$getter();
if ($value instanceof DateTime) {
$value = $value->format('Y-m-d H:i');
}
$updateData[$voiParentField->getFieldKey()] = $value;
continue;
}
$sf = $objPersisted->getSpecificFieldByFieldId($voiParentField->getFieldKey());
if ($sf instanceof SpecificField) {
$updateData['sf_'.$voiParentField->getFieldKey()] = $sf->getValue();
}
}
try {
$formType = (new EntityToFormTypeTransformer())->transform(get_class($child));
$form = $this->formFactory->create($formType, $child, ['allow_extra_fields' => true]);
$form->submit($updateData, false);
if ($form->isSubmitted()) {
if (!$form->isValid()) {
$this->sentryLogger->captureMessage(
SentryLogger::CHANNEL_SUBSCRIBER,
sprintf('Invalid submission form for child with id %s', $child->getId()),
[
'catchOnClass' => self::class,
'form' => $this->getFormErrorsForLog($form, $this->serializer->serialize($form->getData(), 'quote:update')),
],
LogLevel::ERROR
);
throw new Exception('Invalid submission form');
}
$data = $form->getData();
if ($data instanceof Quote) {
if ($data->getStatus() instanceof QuoteState) {
$data->getStatus()->setNormalizeAsIRI(true);
}
foreach ($data->getQuoteLines() as $quoteLine) {
$quoteLine->setNormalizeAsIRI(true);
}
}
$this->dataPersister->persist($form->getData(), [
AbstractWithoutRequestDataPersister::CONTEXT_IS_V4 => true,
]);
}
} catch (Exception $exception) {
$this->sentryLogger->captureException(
SentryLogger::CHANNEL_SUBSCRIBER,
$exception,
[
'catchOnClass' => self::class,
]
);
}
}
}
}