vendor/shopware/core/Content/Flow/Dispatching/Action/SendMailAction.php line 151

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Flow\Dispatching\Action;
  3. use Doctrine\DBAL\Connection;
  4. use Psr\Log\LoggerInterface;
  5. use Shopware\Core\Checkout\Document\DocumentCollection;
  6. use Shopware\Core\Checkout\Document\DocumentService;
  7. use Shopware\Core\Checkout\Document\Service\DocumentGenerator;
  8. use Shopware\Core\Content\ContactForm\Event\ContactFormEvent;
  9. use Shopware\Core\Content\Flow\Dispatching\DelayableAction;
  10. use Shopware\Core\Content\Flow\Dispatching\StorableFlow;
  11. use Shopware\Core\Content\Flow\Events\FlowSendMailActionEvent;
  12. use Shopware\Core\Content\Mail\Service\AbstractMailService;
  13. use Shopware\Core\Content\MailTemplate\Exception\MailEventConfigurationException;
  14. use Shopware\Core\Content\MailTemplate\Exception\SalesChannelNotFoundException;
  15. use Shopware\Core\Content\MailTemplate\MailTemplateActions;
  16. use Shopware\Core\Content\MailTemplate\MailTemplateEntity;
  17. use Shopware\Core\Content\MailTemplate\Subscriber\MailSendSubscriberConfig;
  18. use Shopware\Core\Content\Media\MediaCollection;
  19. use Shopware\Core\Content\Media\MediaEntity;
  20. use Shopware\Core\Content\Media\MediaService;
  21. use Shopware\Core\Defaults;
  22. use Shopware\Core\Framework\Adapter\Translation\Translator;
  23. use Shopware\Core\Framework\Context;
  24. use Shopware\Core\Framework\DataAbstractionLayer\Doctrine\FetchModeHelper;
  25. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  26. use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
  27. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  28. use Shopware\Core\Framework\Event\FlowEvent;
  29. use Shopware\Core\Framework\Event\MailAware;
  30. use Shopware\Core\Framework\Event\OrderAware;
  31. use Shopware\Core\Framework\Feature;
  32. use Shopware\Core\Framework\Uuid\Uuid;
  33. use Shopware\Core\Framework\Validation\DataBag\DataBag;
  34. use Shopware\Core\System\Locale\LanguageLocaleCodeProvider;
  35. use Symfony\Contracts\EventDispatcher\Event;
  36. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  37. /**
  38.  * @package business-ops
  39.  *
  40.  * @deprecated tag:v6.5.0 - reason:remove-subscriber - FlowActions won't be executed over the event system anymore,
  41.  * therefore the actions won't implement the EventSubscriberInterface anymore.
  42.  */
  43. class SendMailAction extends FlowAction implements DelayableAction
  44. {
  45.     public const ACTION_NAME MailTemplateActions::MAIL_TEMPLATE_MAIL_SEND_ACTION;
  46.     public const MAIL_CONFIG_EXTENSION 'mail-attachments';
  47.     private const RECIPIENT_CONFIG_ADMIN 'admin';
  48.     private const RECIPIENT_CONFIG_CUSTOM 'custom';
  49.     private const RECIPIENT_CONFIG_CONTACT_FORM_MAIL 'contactFormMail';
  50.     private EntityRepositoryInterface $mailTemplateRepository;
  51.     private MediaService $mediaService;
  52.     private EntityRepositoryInterface $mediaRepository;
  53.     private EntityRepositoryInterface $documentRepository;
  54.     private LoggerInterface $logger;
  55.     private AbstractMailService $emailService;
  56.     private EventDispatcherInterface $eventDispatcher;
  57.     private EntityRepositoryInterface $mailTemplateTypeRepository;
  58.     private Translator $translator;
  59.     private Connection $connection;
  60.     private LanguageLocaleCodeProvider $languageLocaleProvider;
  61.     private bool $updateMailTemplate;
  62.     private DocumentGenerator $documentGenerator;
  63.     private DocumentService $documentService;
  64.     /**
  65.      * @internal
  66.      */
  67.     public function __construct(
  68.         AbstractMailService $emailService,
  69.         EntityRepositoryInterface $mailTemplateRepository,
  70.         MediaService $mediaService,
  71.         EntityRepositoryInterface $mediaRepository,
  72.         EntityRepositoryInterface $documentRepository,
  73.         DocumentService $documentService,
  74.         DocumentGenerator $documentGenerator,
  75.         LoggerInterface $logger,
  76.         EventDispatcherInterface $eventDispatcher,
  77.         EntityRepositoryInterface $mailTemplateTypeRepository,
  78.         Translator $translator,
  79.         Connection $connection,
  80.         LanguageLocaleCodeProvider $languageLocaleProvider,
  81.         bool $updateMailTemplate
  82.     ) {
  83.         $this->mailTemplateRepository $mailTemplateRepository;
  84.         $this->mediaService $mediaService;
  85.         $this->mediaRepository $mediaRepository;
  86.         $this->documentRepository $documentRepository;
  87.         $this->logger $logger;
  88.         $this->emailService $emailService;
  89.         $this->eventDispatcher $eventDispatcher;
  90.         $this->mailTemplateTypeRepository $mailTemplateTypeRepository;
  91.         $this->translator $translator;
  92.         $this->connection $connection;
  93.         $this->languageLocaleProvider $languageLocaleProvider;
  94.         $this->updateMailTemplate $updateMailTemplate;
  95.         $this->documentGenerator $documentGenerator;
  96.         $this->documentService $documentService;
  97.     }
  98.     public static function getName(): string
  99.     {
  100.         return 'action.mail.send';
  101.     }
  102.     /**
  103.      * @deprecated tag:v6.5.0 - reason:remove-subscriber - Will be removed
  104.      */
  105.     public static function getSubscribedEvents(): array
  106.     {
  107.         if (Feature::isActive('v6.5.0.0')) {
  108.             return [];
  109.         }
  110.         return [
  111.             self::getName() => 'handle',
  112.         ];
  113.     }
  114.     /**
  115.      * @return array<string>
  116.      */
  117.     public function requirements(): array
  118.     {
  119.         return [MailAware::class];
  120.     }
  121.     /**
  122.      * @deprecated tag:v6.5.0 Will be removed, implement handleFlow instead
  123.      *
  124.      * @throws MailEventConfigurationException
  125.      * @throws SalesChannelNotFoundException
  126.      * @throws InconsistentCriteriaIdsException
  127.      */
  128.     public function handle(Event $event): void
  129.     {
  130.         Feature::triggerDeprecationOrThrow(
  131.             'v6.5.0.0',
  132.             Feature::deprecatedMethodMessage(__CLASS____METHOD__'v6.5.0.0')
  133.         );
  134.         if (!$event instanceof FlowEvent) {
  135.             return;
  136.         }
  137.         $mailEvent $event->getEvent();
  138.         $extension $event->getContext()->getExtension(self::MAIL_CONFIG_EXTENSION);
  139.         if (!$extension instanceof MailSendSubscriberConfig) {
  140.             $extension = new MailSendSubscriberConfig(false, [], []);
  141.         }
  142.         if ($extension->skip()) {
  143.             return;
  144.         }
  145.         if (!$mailEvent instanceof MailAware) {
  146.             throw new MailEventConfigurationException('Not an instance of MailAware'\get_class($mailEvent));
  147.         }
  148.         $eventConfig $event->getConfig();
  149.         if (empty($eventConfig['recipient'])) {
  150.             throw new MailEventConfigurationException('The recipient value in the flow action configuration is missing.'\get_class($mailEvent));
  151.         }
  152.         if (!isset($eventConfig['mailTemplateId'])) {
  153.             return;
  154.         }
  155.         $mailTemplate $this->getMailTemplate($eventConfig['mailTemplateId'], $event->getContext());
  156.         if ($mailTemplate === null) {
  157.             return;
  158.         }
  159.         $injectedTranslator $this->injectTranslator($mailEvent->getContext(), $mailEvent->getSalesChannelId());
  160.         $data = new DataBag();
  161.         $contactFormData = [];
  162.         if ($mailEvent instanceof ContactFormEvent) {
  163.             $contactFormData $mailEvent->getContactFormData();
  164.         }
  165.         $recipients $this->getRecipients($eventConfig['recipient'], $mailEvent->getMailStruct()->getRecipients(), $contactFormData);
  166.         if (empty($recipients)) {
  167.             return;
  168.         }
  169.         $data->set('recipients'$recipients);
  170.         $data->set('senderName'$mailTemplate->getTranslation('senderName'));
  171.         $data->set('salesChannelId'$mailEvent->getSalesChannelId());
  172.         $data->set('templateId'$mailTemplate->getId());
  173.         $data->set('customFields'$mailTemplate->getCustomFields());
  174.         $data->set('contentHtml'$mailTemplate->getTranslation('contentHtml'));
  175.         $data->set('contentPlain'$mailTemplate->getTranslation('contentPlain'));
  176.         $data->set('subject'$mailTemplate->getTranslation('subject'));
  177.         $data->set('mediaIds', []);
  178.         $attachments array_unique($this->buildAttachments(
  179.             $event->getContext(),
  180.             $mailTemplate,
  181.             $extension,
  182.             $eventConfig,
  183.             $mailEvent instanceof OrderAware $mailEvent->getOrderId() : null
  184.         ), \SORT_REGULAR);
  185.         if (!empty($attachments)) {
  186.             $data->set('binAttachments'$attachments);
  187.         }
  188.         $this->eventDispatcher->dispatch(new FlowSendMailActionEvent($data$mailTemplate$event));
  189.         if ($data->has('templateId')) {
  190.             $this->updateMailTemplateType(
  191.                 $event->getContext(),
  192.                 $event,
  193.                 $this->getTemplateData($mailEvent),
  194.                 $mailTemplate
  195.             );
  196.         }
  197.         $templateData array_merge([
  198.             'eventName' => $mailEvent->getName(),
  199.         ], $this->getTemplateData($mailEvent));
  200.         $this->send($data$event->getContext(), $templateData$attachments$extension$injectedTranslator);
  201.     }
  202.     /**
  203.      * @throws MailEventConfigurationException
  204.      * @throws SalesChannelNotFoundException
  205.      * @throws InconsistentCriteriaIdsException
  206.      */
  207.     public function handleFlow(StorableFlow $flow): void
  208.     {
  209.         $extension $flow->getContext()->getExtension(self::MAIL_CONFIG_EXTENSION);
  210.         if (!$extension instanceof MailSendSubscriberConfig) {
  211.             $extension = new MailSendSubscriberConfig(false, [], []);
  212.         }
  213.         if ($extension->skip()) {
  214.             return;
  215.         }
  216.         if (!$flow->hasStore(MailAware::MAIL_STRUCT) || !$flow->hasStore(MailAware::SALES_CHANNEL_ID)) {
  217.             throw new MailEventConfigurationException('Not have data from MailAware'\get_class($flow));
  218.         }
  219.         $eventConfig $flow->getConfig();
  220.         if (empty($eventConfig['recipient'])) {
  221.             throw new MailEventConfigurationException('The recipient value in the flow action configuration is missing.'\get_class($flow));
  222.         }
  223.         if (!isset($eventConfig['mailTemplateId'])) {
  224.             return;
  225.         }
  226.         $mailTemplate $this->getMailTemplate($eventConfig['mailTemplateId'], $flow->getContext());
  227.         if ($mailTemplate === null) {
  228.             return;
  229.         }
  230.         $injectedTranslator $this->injectTranslator($flow->getContext(), $flow->getStore(MailAware::SALES_CHANNEL_ID));
  231.         $data = new DataBag();
  232.         $recipients $this->getRecipients(
  233.             $eventConfig['recipient'],
  234.             $flow->getStore(MailAware::MAIL_STRUCT)['recipients'],
  235.             $flow->getStore('contactFormData', []),
  236.         );
  237.         if (empty($recipients)) {
  238.             return;
  239.         }
  240.         $data->set('recipients'$recipients);
  241.         $data->set('senderName'$mailTemplate->getTranslation('senderName'));
  242.         $data->set('salesChannelId'$flow->getStore(MailAware::SALES_CHANNEL_ID));
  243.         $data->set('templateId'$mailTemplate->getId());
  244.         $data->set('customFields'$mailTemplate->getCustomFields());
  245.         $data->set('contentHtml'$mailTemplate->getTranslation('contentHtml'));
  246.         $data->set('contentPlain'$mailTemplate->getTranslation('contentPlain'));
  247.         $data->set('subject'$mailTemplate->getTranslation('subject'));
  248.         $data->set('mediaIds', []);
  249.         $attachments array_unique($this->buildAttachments(
  250.             $flow->getContext(),
  251.             $mailTemplate,
  252.             $extension,
  253.             $eventConfig,
  254.             $flow->getStore(OrderAware::ORDER_ID),
  255.         ), \SORT_REGULAR);
  256.         if (!empty($attachments)) {
  257.             $data->set('binAttachments'$attachments);
  258.         }
  259.         $this->eventDispatcher->dispatch(new FlowSendMailActionEvent($data$mailTemplate$flow));
  260.         if ($data->has('templateId')) {
  261.             $this->updateMailTemplateType(
  262.                 $flow->getContext(),
  263.                 $flow,
  264.                 $flow->data(),
  265.                 $mailTemplate
  266.             );
  267.         }
  268.         $templateData array_merge([
  269.             'eventName' => $flow->getName(),
  270.         ], $flow->data());
  271.         $this->send($data$flow->getContext(), $templateData$attachments$extension$injectedTranslator);
  272.     }
  273.     /**
  274.      * @param array<string, mixed> $templateData
  275.      * @param array<mixed, mixed> $attachments
  276.      */
  277.     private function send(DataBag $dataContext $context, array $templateData, array $attachmentsMailSendSubscriberConfig $extensionbool $injectedTranslator): void
  278.     {
  279.         try {
  280.             $this->emailService->send(
  281.                 $data->all(),
  282.                 $context,
  283.                 $templateData
  284.             );
  285.             $documentAttachments array_filter($attachments, function (array $attachment) use ($extension) {
  286.                 return \array_key_exists('id'$attachment) && \in_array($attachment['id'], $extension->getDocumentIds(), true);
  287.             });
  288.             $documentAttachments array_column($documentAttachments'id');
  289.             if (!empty($documentAttachments)) {
  290.                 $this->connection->executeStatement(
  291.                     'UPDATE `document` SET `updated_at` = :now, `sent` = 1 WHERE `id` IN (:ids)',
  292.                     ['ids' => Uuid::fromHexToBytesList($documentAttachments), 'now' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT)],
  293.                     ['ids' => Connection::PARAM_STR_ARRAY]
  294.                 );
  295.             }
  296.         } catch (\Exception $e) {
  297.             $this->logger->error(
  298.                 "Could not send mail:\n"
  299.                 $e->getMessage() . "\n"
  300.                 'Error Code:' $e->getCode() . "\n"
  301.                 "Template data: \n"
  302.                 json_encode($data->all()) . "\n"
  303.             );
  304.         }
  305.         if ($injectedTranslator) {
  306.             $this->translator->resetInjection();
  307.         }
  308.     }
  309.     /**
  310.      * @param FlowEvent|StorableFlow $event
  311.      * @param array<string, mixed> $templateData
  312.      */
  313.     private function updateMailTemplateType(
  314.         Context $context,
  315.         $event,
  316.         array $templateData,
  317.         MailTemplateEntity $mailTemplate
  318.     ): void {
  319.         if (!$mailTemplate->getMailTemplateTypeId()) {
  320.             return;
  321.         }
  322.         if (!$this->updateMailTemplate) {
  323.             return;
  324.         }
  325.         $mailTemplateTypeTranslation $this->connection->fetchOne(
  326.             'SELECT 1 FROM mail_template_type_translation WHERE language_id = :languageId AND mail_template_type_id =:mailTemplateTypeId',
  327.             [
  328.                 'languageId' => Uuid::fromHexToBytes($context->getLanguageId()),
  329.                 'mailTemplateTypeId' => Uuid::fromHexToBytes($mailTemplate->getMailTemplateTypeId()),
  330.             ]
  331.         );
  332.         if (!$mailTemplateTypeTranslation) {
  333.             // Don't throw errors if this fails // Fix with NEXT-15475
  334.             $this->logger->error(
  335.                 "Could not update mail template type, because translation for this language does not exits:\n"
  336.                 'Flow id: ' $event->getFlowState()->flowId "\n"
  337.                 'Sequence id: ' $event->getFlowState()->getSequenceId()
  338.             );
  339.             return;
  340.         }
  341.         $this->mailTemplateTypeRepository->update([[
  342.             'id' => $mailTemplate->getMailTemplateTypeId(),
  343.             'templateData' => $templateData,
  344.         ]], $context);
  345.     }
  346.     private function getMailTemplate(string $idContext $context): ?MailTemplateEntity
  347.     {
  348.         $criteria = new Criteria([$id]);
  349.         $criteria->setTitle('send-mail::load-mail-template');
  350.         $criteria->addAssociation('media.media');
  351.         $criteria->setLimit(1);
  352.         return $this->mailTemplateRepository
  353.             ->search($criteria$context)
  354.             ->first();
  355.     }
  356.     /**
  357.      * @throws MailEventConfigurationException
  358.      *
  359.      * @return array<string, mixed>
  360.      */
  361.     private function getTemplateData(MailAware $event): array
  362.     {
  363.         $data = [];
  364.         foreach (array_keys($event::getAvailableData()->toArray()) as $key) {
  365.             $getter 'get' ucfirst($key);
  366.             if (!method_exists($event$getter)) {
  367.                 throw new MailEventConfigurationException('Data for ' $key ' not available.'\get_class($event));
  368.             }
  369.             $data[$key] = $event->$getter();
  370.         }
  371.         return $data;
  372.     }
  373.     /**
  374.      * @param array<string, mixed> $eventConfig
  375.      *
  376.      * @return array<mixed, mixed>
  377.      */
  378.     private function buildAttachments(
  379.         Context $context,
  380.         MailTemplateEntity $mailTemplate,
  381.         MailSendSubscriberConfig $extensions,
  382.         array $eventConfig,
  383.         ?string $orderId
  384.     ): array {
  385.         $attachments = [];
  386.         if ($mailTemplate->getMedia() !== null) {
  387.             foreach ($mailTemplate->getMedia() as $mailTemplateMedia) {
  388.                 if ($mailTemplateMedia->getMedia() === null) {
  389.                     continue;
  390.                 }
  391.                 if ($mailTemplateMedia->getLanguageId() !== null && $mailTemplateMedia->getLanguageId() !== $context->getLanguageId()) {
  392.                     continue;
  393.                 }
  394.                 $attachments[] = $this->mediaService->getAttachment(
  395.                     $mailTemplateMedia->getMedia(),
  396.                     $context
  397.                 );
  398.             }
  399.         }
  400.         if (!empty($extensions->getMediaIds())) {
  401.             $criteria = new Criteria($extensions->getMediaIds());
  402.             $criteria->setTitle('send-mail::load-media');
  403.             /** @var MediaCollection<MediaEntity> $entities */
  404.             $entities $this->mediaRepository->search($criteria$context);
  405.             foreach ($entities as $media) {
  406.                 $attachments[] = $this->mediaService->getAttachment($media$context);
  407.             }
  408.         }
  409.         $documentIds $extensions->getDocumentIds();
  410.         if (!empty($eventConfig['documentTypeIds']) && \is_array($eventConfig['documentTypeIds']) && $orderId) {
  411.             $latestDocuments $this->getLatestDocumentsOfTypes($orderId$eventConfig['documentTypeIds']);
  412.             $documentIds array_unique(array_merge($documentIds$latestDocuments));
  413.         }
  414.         if (!empty($documentIds)) {
  415.             $extensions->setDocumentIds($documentIds);
  416.             if (Feature::isActive('v6.5.0.0')) {
  417.                 $attachments $this->mappingAttachments($documentIds$attachments$context);
  418.             } else {
  419.                 $attachments $this->buildOrderAttachments($documentIds$attachments$context);
  420.             }
  421.         }
  422.         return $attachments;
  423.     }
  424.     private function injectTranslator(Context $context, ?string $salesChannelId): bool
  425.     {
  426.         if ($salesChannelId === null) {
  427.             return false;
  428.         }
  429.         if ($this->translator->getSnippetSetId() !== null) {
  430.             return false;
  431.         }
  432.         $this->translator->injectSettings(
  433.             $salesChannelId,
  434.             $context->getLanguageId(),
  435.             $this->languageLocaleProvider->getLocaleForLanguageId($context->getLanguageId()),
  436.             $context
  437.         );
  438.         return true;
  439.     }
  440.     /**
  441.      * @param array<string, mixed> $recipients
  442.      * @param array<string, mixed> $mailStructRecipients
  443.      * @param array<int|string, mixed> $contactFormData
  444.      *
  445.      * @return array<int|string, string>
  446.      */
  447.     private function getRecipients(array $recipients, array $mailStructRecipients, array $contactFormData): array
  448.     {
  449.         switch ($recipients['type']) {
  450.             case self::RECIPIENT_CONFIG_CUSTOM:
  451.                 return $recipients['data'];
  452.             case self::RECIPIENT_CONFIG_ADMIN:
  453.                 $admins $this->connection->fetchAllAssociative(
  454.                     'SELECT first_name, last_name, email FROM user WHERE admin = true'
  455.                 );
  456.                 $emails = [];
  457.                 foreach ($admins as $admin) {
  458.                     $emails[$admin['email']] = $admin['first_name'] . ' ' $admin['last_name'];
  459.                 }
  460.                 return $emails;
  461.             case self::RECIPIENT_CONFIG_CONTACT_FORM_MAIL:
  462.                 if (empty($contactFormData)) {
  463.                     return [];
  464.                 }
  465.                 if (!\array_key_exists('email'$contactFormData)) {
  466.                     return [];
  467.                 }
  468.                 return [$contactFormData['email'] => ($contactFormData['firstName'] ?? '') . ' ' . ($contactFormData['lastName'] ?? '')];
  469.             default:
  470.                 return $mailStructRecipients;
  471.         }
  472.     }
  473.     /**
  474.      * @param array<string> $documentIds
  475.      * @param array<mixed, mixed> $attachments
  476.      *
  477.      * @return array<mixed, mixed>
  478.      */
  479.     private function buildOrderAttachments(array $documentIds, array $attachmentsContext $context): array
  480.     {
  481.         $criteria = new Criteria($documentIds);
  482.         $criteria->setTitle('send-mail::load-attachments');
  483.         $criteria->addAssociation('documentMediaFile');
  484.         $criteria->addAssociation('documentType');
  485.         /** @var DocumentCollection $documents */
  486.         $documents $this->documentRepository->search($criteria$context)->getEntities();
  487.         return $this->mappingAttachmentsInfo($documents$attachments$context);
  488.     }
  489.     /**
  490.      * @param array<string> $documentTypeIds
  491.      *
  492.      * @return array<string>
  493.      */
  494.     private function getLatestDocumentsOfTypes(string $orderId, array $documentTypeIds): array
  495.     {
  496.         $documents $this->connection->fetchAllAssociative(
  497.             'SELECT
  498.                 LOWER(hex(`document`.`document_type_id`)) as doc_type,
  499.                 LOWER(hex(`document`.`id`)) as doc_id,
  500.                 `document`.`created_at` as newest_date
  501.             FROM
  502.                 `document`
  503.             WHERE
  504.                 HEX(`document`.`order_id`) = :orderId
  505.                 AND HEX(`document`.`document_type_id`) IN (:documentTypeIds)
  506.             ORDER BY `document`.`created_at` DESC',
  507.             [
  508.                 'orderId' => $orderId,
  509.                 'documentTypeIds' => $documentTypeIds,
  510.             ],
  511.             [
  512.                 'documentTypeIds' => Connection::PARAM_STR_ARRAY,
  513.             ]
  514.         );
  515.         $documentsGroupByType FetchModeHelper::group($documents);
  516.         $documentIds = [];
  517.         foreach ($documentsGroupByType as $document) {
  518.             $documentIds[] = array_shift($document)['doc_id'];
  519.         }
  520.         return $documentIds;
  521.     }
  522.     /**
  523.      * @param array<mixed, mixed> $attachments
  524.      *
  525.      * @return array<mixed, mixed>
  526.      */
  527.     private function mappingAttachmentsInfo(DocumentCollection $documents, array $attachmentsContext $context): array
  528.     {
  529.         foreach ($documents as $document) {
  530.             $documentId $document->getId();
  531.             $document $this->documentService->getDocument($document$context);
  532.             $attachments[] = [
  533.                 'id' => $documentId,
  534.                 'content' => $document->getFileBlob(),
  535.                 'fileName' => $document->getFilename(),
  536.                 'mimeType' => $document->getContentType(),
  537.             ];
  538.         }
  539.         return $attachments;
  540.     }
  541.     /**
  542.      * @param array<string> $documentIds
  543.      * @param array<mixed, mixed> $attachments
  544.      *
  545.      * @return array<mixed, mixed>
  546.      */
  547.     private function mappingAttachments(array $documentIds, array $attachmentsContext $context): array
  548.     {
  549.         foreach ($documentIds as $documentId) {
  550.             $document $this->documentGenerator->readDocument($documentId$context);
  551.             if ($document === null) {
  552.                 continue;
  553.             }
  554.             $attachments[] = [
  555.                 'id' => $documentId,
  556.                 'content' => $document->getContent(),
  557.                 'fileName' => $document->getName(),
  558.                 'mimeType' => $document->getContentType(),
  559.             ];
  560.         }
  561.         return $attachments;
  562.     }
  563. }