src/DataPersister/AbstractDataPersister.php line 363

Open in your IDE?
  1. <?php
  2. namespace App\DataPersister;
  3. use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
  4. use App\Security\User;
  5. use App\Service\ApiWebService;
  6. use App\Service\Cache\CacheManager;
  7. use App\Service\DataWrapper\BasicDataWrapper;
  8. use App\Service\Form\FormUtils;
  9. use App\Service\PreSendSerializer;
  10. use App\Service\Provider\ApiProviderInterface;
  11. use App\Service\TokenDataWrapper\TokenDataWrapper;
  12. use App\V4\DataPersister\AbstractWithoutRequestDataPersister;
  13. use App\V4\Event\PostPersistEvent;
  14. use App\V4\Event\PostRemoveEvent;
  15. use App\V4\Form\Type\Prospect\ProspectBusinessType;
  16. use App\V4\Form\Type\Prospect\ProspectIndividualType;
  17. use App\V4\Logger\SentryLogger;
  18. use App\V4\Messenger\Message\ResynchronizeBadgesRequest;
  19. use Doctrine\Common\Annotations\AnnotationException;
  20. use LogicException;
  21. use Psr\Cache\InvalidArgumentException;
  22. use Psr\Log\LogLevel;
  23. use RuntimeException;
  24. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  25. use Symfony\Component\Form\FormFactoryInterface;
  26. use Symfony\Component\HttpFoundation\JsonResponse;
  27. use Symfony\Component\HttpFoundation\Request;
  28. use Symfony\Component\HttpFoundation\RequestStack;
  29. use Symfony\Component\HttpFoundation\Response;
  30. use Symfony\Component\Messenger\MessageBusInterface;
  31. use Symfony\Component\Security\Core\Security;
  32. use Symfony\Component\Serializer\Exception\ExceptionInterface;
  33. use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
  34. use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
  35. use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
  36. use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
  37. use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
  38. use Throwable;
  39. abstract class AbstractDataPersister implements ContextAwareDataPersisterInterface
  40. {
  41.     use FormUtils;
  42.     protected const ENTITY null;
  43.     protected const FORM_TYPE null;
  44.     protected const ENDPOINT '/';
  45.     protected const POST_SERIALIZATION_GROUPS = [];
  46.     protected const PUT_SERIALIZATION_GROUPS = [];
  47.     public const CONTEXT_ORIGIN 'origin';
  48.     /**
  49.      * @var ApiWebService
  50.      */
  51.     protected $apiWebService;
  52.     /**
  53.      * @var ApiProviderInterface
  54.      */
  55.     protected $apiProvider;
  56.     /**
  57.      * @var TokenDataWrapper
  58.      */
  59.     protected $tokenDataWrapper;
  60.     /**
  61.      * @var BasicDataWrapper
  62.      */
  63.     protected $basicDataWrapper;
  64.     /**
  65.      * @var RequestStack
  66.      */
  67.     protected $request;
  68.     /**
  69.      * @var FormFactoryInterface
  70.      */
  71.     protected $formFactory;
  72.     /**
  73.      * @var PreSendSerializer
  74.      */
  75.     protected $serializer;
  76.     /**
  77.      * @var CacheManager
  78.      */
  79.     protected $cacheManager;
  80.     /**
  81.      * @var SentryLogger
  82.      */
  83.     protected $sentryLogger;
  84.     /**
  85.      * @var MessageBusInterface
  86.      */
  87.     private $bus;
  88.     /**
  89.      * @var Security
  90.      */
  91.     private $security;
  92.     /**
  93.      * @var EventDispatcherInterface
  94.      */
  95.     private $eventDispatcher;
  96.     /**
  97.      * @param RequestStack             $request
  98.      * @param ApiWebService            $apiWebService
  99.      * @param ApiProviderInterface     $apiProvider
  100.      * @param TokenDataWrapper         $tokenDataWrapper
  101.      * @param BasicDataWrapper         $basicDataWrapper
  102.      * @param FormFactoryInterface     $formFactory
  103.      * @param PreSendSerializer        $serializer
  104.      * @param CacheManager             $cacheManager
  105.      * @param MessageBusInterface      $bus
  106.      * @param Security                 $security
  107.      * @param EventDispatcherInterface $eventDispatcher
  108.      * @param SentryLogger             $sentryLogger
  109.      */
  110.     public function __construct(
  111.         RequestStack $request,
  112.         ApiWebService $apiWebService,
  113.         ApiProviderInterface $apiProvider,
  114.         TokenDataWrapper $tokenDataWrapper,
  115.         BasicDataWrapper $basicDataWrapper,
  116.         FormFactoryInterface $formFactory,
  117.         PreSendSerializer $serializer,
  118.         CacheManager $cacheManager,
  119.         MessageBusInterface $bus,
  120.         Security $security,
  121.         EventDispatcherInterface $eventDispatcher,
  122.         SentryLogger $sentryLogger
  123.     ) {
  124.         $this->request $request;
  125.         $this->apiWebService $apiWebService;
  126.         $this->apiProvider $apiProvider;
  127.         $this->tokenDataWrapper $tokenDataWrapper;
  128.         $this->basicDataWrapper $basicDataWrapper;
  129.         $this->formFactory $formFactory;
  130.         $this->serializer $serializer;
  131.         $this->cacheManager $cacheManager;
  132.         $this->bus $bus;
  133.         $this->security $security;
  134.         $this->eventDispatcher $eventDispatcher;
  135.         $this->sentryLogger $sentryLogger;
  136.     }
  137.     /**
  138.      * @param $data
  139.      * @param array $context
  140.      *
  141.      * @return bool
  142.      */
  143.     public function supports($data, array $context = []): bool
  144.     {
  145.         $entity $this::ENTITY;
  146.         return $data instanceof $entity && !isset($context[AbstractWithoutRequestDataPersister::CONTEXT_IS_V4]);
  147.     }
  148.     /**
  149.      * @param object $data
  150.      * @param array  $context
  151.      *
  152.      * @return object
  153.      *
  154.      * @throws InvalidArgumentException
  155.      * @throws AnnotationException
  156.      * @throws ExceptionInterface
  157.      * @throws ClientExceptionInterface
  158.      * @throws DecodingExceptionInterface
  159.      * @throws RedirectionExceptionInterface
  160.      * @throws ServerExceptionInterface
  161.      * @throws TransportExceptionInterface
  162.      * @throws \Exception
  163.      */
  164.     public function persist($data, array $context = [])
  165.     {
  166.         // using serialize method to deep clone, as simple clone did not work ¯\_(ツ)_/¯
  167.         $dataBefore is_object($data) ? unserialize(serialize($data)) : null;
  168.         $isEdit = ($data && $data->getId()) || isset($context['id']);
  169.         //try {
  170.         $request $this->request->getCurrentRequest();
  171.         if (!$request) {
  172.             throw new LogicException('There was not request, which is unexpected');
  173.         }
  174.         $url $this::ENDPOINT;
  175.         $serializationGroups $this::POST_SERIALIZATION_GROUPS;
  176.         $method Request::METHOD_POST;
  177.         if ($isEdit) {
  178.             $method Request::METHOD_PUT;
  179.             $url sprintf('%s/%s'$this::ENDPOINT$context['id'] ?? $data->getId());
  180.             $serializationGroups $this::PUT_SERIALIZATION_GROUPS;
  181.         }
  182.         $options $request->get('prospectId') ? ['prospectId' => $request->get('prospectId')] : [];
  183.         if ($request->get('quoteId')) {
  184.             $options['quoteId'] = $request->get('quoteId');
  185.         }
  186.         $form $this->formFactory->create($this::FORM_TYPE$data$options);
  187.         $submittedData $request->getContent()
  188.                 ? json_decode($request->getContent(), true)
  189.                 : array_merge_recursive($request->files->all(), $request->request->all())
  190.             ;
  191.         if (!empty($request->get('prospectId'))) {
  192.             if ($form->has('prospect') && !isset($submittedData['prospect'])) {
  193.                 $submittedData['prospect'] = $request->get('prospectId');
  194.             } elseif ($form->has('prospectId') && !isset($submittedData['prospectId'])) {
  195.                 $submittedData['prospectId'] = $request->get('prospectId');
  196.             }
  197.         }
  198.         if (!empty($request->get('quoteId'))) {
  199.             if ($form->has('quotes') && !isset($submittedData['quotes'])) {
  200.                 $submittedData['quotes'] = [$request->get('quoteId')];
  201.             }
  202.         }
  203.         $submittedData $this->reformatSubmittedData($submittedData$request);
  204.         $form->submit($context['dataAlreadySubmitted'] ?? $submittedDatafalse);
  205.         if ($form->isSubmitted() && !$form->isValid()) {
  206.             $this->sentryLogger->captureMessage(
  207.                 SentryLogger::CHANNEL_DATA_PERSISTER,
  208.                 sprintf('Cannot persist entity of model %s'$this::ENTITY),
  209.                 [
  210.                     'catchOnClass' => get_class($this),
  211.                     'objectId' => $isEdit $data->getId() : null,
  212.                     'entity' => $this::ENTITY,
  213.                     'requestContent' => $request->getContent()
  214.                         ? json_decode($request->getContent(), true)
  215.                         : $request->request->all(),
  216.                     'context' => $context,
  217.                     'form' => $this->getFormErrorsForLog($form$submittedData),
  218.                 ],
  219.                 LogLevel::ERROR
  220.             );
  221.             return new JsonResponse($this->convertFormErrorsToArrayV4($form), Response::HTTP_UNPROCESSABLE_ENTITY);
  222.         }
  223.         // Add customerId, userId, createdAt, createdBy, updatedAt, updatedBy information
  224.         $requestContent $this->serializer->serialize($form->getData(), $serializationGroups);
  225.         $serializedData $this->basicDataWrapper->wrapData($requestContent);
  226.         $serializedData $this->tokenDataWrapper->wrapData($serializedData);
  227.         $response $this
  228.                 ->apiWebService
  229.                 ->send($this->apiProvider$method$url$serializedData)
  230.             ;
  231.         $this->cacheManager->invalidateTag([$this->cacheManager->getCustomerPrefix().'_'.$this::ENTITY]);
  232.         $entity $form->getData();
  233.         $entity->setId($response->toArray()['id']);
  234.         $this->eventDispatcher->dispatch(
  235.             (new PostPersistEvent())->setBefore($dataBefore)->setAfter($entity)->setContext($context),
  236.             PostPersistEvent::NAME
  237.         );
  238.         $this->resynchronizeBadges();
  239.         return $entity;
  240.         /*} catch (Throwable | InvalidArgumentException $exception) {
  241.             captureException($exception);
  242.             throw new RuntimeException('Unable to persist '.$this::ENTITY, 0, $exception);
  243.         }*/
  244.     }
  245.     /**
  246.      * @param $data
  247.      * @param array $context
  248.      *
  249.      * @return void
  250.      *
  251.      * @throws \Exception
  252.      */
  253.     public function remove($data, array $context = []): void
  254.     {
  255.         try {
  256.             $response $this
  257.                 ->apiWebService
  258.                 ->send($this->apiProviderRequest::METHOD_DELETEsprintf('%s/%s'$this::ENDPOINT$data->getId()), [])
  259.             ;
  260.             if (Response::HTTP_OK === $response->getStatusCode() || Response::HTTP_NO_CONTENT === $response->getStatusCode()) {
  261.                 $this->eventDispatcher->dispatch((new PostRemoveEvent())->setAfter($data), PostRemoveEvent::NAME);
  262.             }
  263.             $this->resynchronizeBadges();
  264.             $this->cacheManager->invalidateTag([$this::ENTITY]);
  265.         } catch (Throwable InvalidArgumentException $exception) {
  266.             $this->sentryLogger->captureException(
  267.                 SentryLogger::CHANNEL_DATA_PERSISTER,
  268.                 $exception,
  269.                 [
  270.                     'catchOnClass' => self::class,
  271.                     'apiCalled' => $this->apiProvider->getHost(),
  272.                     'urlCalled' => sprintf('%s/%s'$this::ENDPOINT$data->getId()),
  273.                     'method' => Request::METHOD_DELETE,
  274.                     'message' => 'Unable to delete '.$this::ENTITY.' with id '.$data->getId(),
  275.                 ]
  276.             );
  277.             throw new RuntimeException('Unable to delete '.$this::ENTITY.' with id '.$data->getId(), 0$exception);
  278.         }
  279.     }
  280.     /**
  281.      * @param $submittedData
  282.      * @param Request|null $request
  283.      *
  284.      * @return array
  285.      */
  286.     protected function reformatSubmittedData($submittedData, ?Request $request null): array
  287.     {
  288.         $submittedDataFormatted = [];
  289.         foreach ($submittedData as $fieldName => $value) {
  290.             //@todo refacto V4 Admin : cas à supprimer lorsque les formulaires admin (ViewOrder) seront en v4
  291.             if (ProspectBusinessType::class === $this::FORM_TYPE || ProspectIndividualType::class === $this::FORM_TYPE) {
  292.                 $fieldName str_replace('unmappedAddress_''mainAddress_'$fieldName);
  293.             }
  294.             if (str_starts_with($fieldName'sf_')) {
  295.                 $submittedDataFormatted array_merge($submittedDataFormatted, [$fieldName => $value]);
  296.                 continue;
  297.             }
  298.             $temp = &$submittedDataFormatted;
  299.             foreach (explode('_'$fieldName) as $key) {
  300.                 $temp = &$temp[$key];
  301.             }
  302.             $temp $value;
  303.         }
  304.         return $submittedDataFormatted;
  305.     }
  306.     /**
  307.      * @return void
  308.      */
  309.     public function resynchronizeBadges()
  310.     {
  311.         $user $this->security->getUser();
  312.         if ($user instanceof User) {
  313.             $this->bus->dispatch((new ResynchronizeBadgesRequest())->setCustomerId($user->getCustomerId()));
  314.         }
  315.     }
  316. }