src/DataPersister/AbstractDataPersister.php line 368

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