vendor/shopware/core/Content/ImportExport/DataAbstractionLayer/Serializer/Entity/MediaSerializer.php line 151

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\ImportExport\DataAbstractionLayer\Serializer\Entity;
  3. use Shopware\Core\Content\ImportExport\Exception\InvalidMediaUrlException;
  4. use Shopware\Core\Content\ImportExport\Exception\MediaDownloadException;
  5. use Shopware\Core\Content\ImportExport\Struct\Config;
  6. use Shopware\Core\Content\Media\Aggregate\MediaFolder\MediaFolderEntity;
  7. use Shopware\Core\Content\Media\File\FileSaver;
  8. use Shopware\Core\Content\Media\File\MediaFile;
  9. use Shopware\Core\Content\Media\MediaEvents;
  10. use Shopware\Core\Content\Media\MediaService;
  11. use Shopware\Core\Framework\Context;
  12. use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
  13. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  17. use Shopware\Core\Framework\Struct\Struct;
  18. use Shopware\Core\Framework\Uuid\Uuid;
  19. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  20. use Symfony\Component\HttpFoundation\Request;
  21. use Symfony\Contracts\Service\ResetInterface;
  22. /**
  23.  * @deprecated tag:v6.5.0 - reason:becomes-internal - EventSubscribers will become internal in v6.5.0
  24.  */
  25. class MediaSerializer extends EntitySerializer implements EventSubscriberInterfaceResetInterface
  26. {
  27.     private FileSaver $fileSaver;
  28.     private MediaService $mediaService;
  29.     private EntityRepositoryInterface $mediaFolderRepository;
  30.     private EntityRepositoryInterface $mediaRepository;
  31.     /**
  32.      * @var array<string, array{media: MediaFile, destination: string}>
  33.      */
  34.     private array $cacheMediaFiles = [];
  35.     /**
  36.      * @internal
  37.      */
  38.     public function __construct(
  39.         MediaService $mediaService,
  40.         FileSaver $fileSaver,
  41.         EntityRepositoryInterface $mediaFolderRepository,
  42.         EntityRepositoryInterface $mediaRepository
  43.     ) {
  44.         $this->mediaService $mediaService;
  45.         $this->fileSaver $fileSaver;
  46.         $this->mediaFolderRepository $mediaFolderRepository;
  47.         $this->mediaRepository $mediaRepository;
  48.     }
  49.     /**
  50.      * @param array<mixed>|Struct|null $entity
  51.      *
  52.      * @return \Generator
  53.      */
  54.     public function serialize(Config $configEntityDefinition $definition$entity): iterable
  55.     {
  56.         yield from parent::serialize($config$definition$entity);
  57.     }
  58.     /**
  59.      * @param array<mixed>|\Traversable<mixed> $entity
  60.      *
  61.      * @return array<mixed>|\Traversable<mixed>
  62.      */
  63.     public function deserialize(Config $configEntityDefinition $definition$entity)
  64.     {
  65.         $entity = \is_array($entity) ? $entity iterator_to_array($entity);
  66.         $deserialized parent::deserialize($config$definition$entity);
  67.         $deserialized = \is_array($deserialized) ? $deserialized iterator_to_array($deserialized);
  68.         $url $entity['url'] ?? null;
  69.         if (empty($url)) {
  70.             return $deserialized;
  71.         }
  72.         if (!filter_var($url, \FILTER_VALIDATE_URL)) {
  73.             $deserialized['_error'] = new InvalidMediaUrlException($url);
  74.             return $deserialized;
  75.         }
  76.         $media null;
  77.         if (isset($deserialized['id'])) {
  78.             $media $this->mediaRepository->search(new Criteria([$deserialized['id']]), Context::createDefaultContext())->first();
  79.         }
  80.         $isNew $media === null;
  81.         if ($isNew || $media->getUrl() !== $url) {
  82.             $entityName $config->get('sourceEntity') ?? $definition->getEntityName();
  83.             $deserialized['mediaFolderId'] = $deserialized['mediaFolderId']
  84.                 ?? $this->getMediaFolderId($deserialized['id'] ?? null$entityName);
  85.             $deserialized['id'] = $deserialized['id'] ?? Uuid::randomHex();
  86.             $parsed parse_url($url);
  87.             if (!$parsed) {
  88.                 throw new \RuntimeException('Error parsing media URL: ' $url);
  89.             }
  90.             $pathInfo pathinfo($parsed['path'] ?? '');
  91.             $media $this->fetchFileFromURL((string) $url$pathInfo['extension'] ?? '');
  92.             if ($media === null) {
  93.                 $deserialized['_error'] = new MediaDownloadException($url);
  94.                 return $deserialized;
  95.             }
  96.             if ($isNew && $media->getHash()) {
  97.                 $deserialized $this->fetchExistingMediaByHash($deserialized$media->getHash());
  98.             }
  99.             $this->cacheMediaFiles[(string) $deserialized['id']] = [
  100.                 'media' => $media,
  101.                 'destination' => $pathInfo['filename'],
  102.             ];
  103.         }
  104.         return $deserialized;
  105.     }
  106.     public function supports(string $entity): bool
  107.     {
  108.         return $entity === 'media';
  109.     }
  110.     /**
  111.      * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
  112.      */
  113.     public static function getSubscribedEvents()
  114.     {
  115.         return [
  116.             MediaEvents::MEDIA_WRITTEN_EVENT => 'persistMedia',
  117.         ];
  118.     }
  119.     /**
  120.      * @internal
  121.      */
  122.     public function persistMedia(EntityWrittenEvent $event): void
  123.     {
  124.         if (empty($this->cacheMediaFiles)) {
  125.             return;
  126.         }
  127.         $mediaFiles $this->cacheMediaFiles;
  128.         // prevent recursion
  129.         $this->cacheMediaFiles = [];
  130.         foreach ($event->getIds() as $id) {
  131.             if (!isset($mediaFiles[$id])) {
  132.                 continue;
  133.             }
  134.             $mediaFile $mediaFiles[$id];
  135.             $this->fileSaver->persistFileToMedia(
  136.                 $mediaFile['media'],
  137.                 $mediaFile['destination'],
  138.                 $id,
  139.                 $event->getContext()
  140.             );
  141.         }
  142.     }
  143.     public function reset(): void
  144.     {
  145.         $this->cacheMediaFiles = [];
  146.     }
  147.     private function getMediaFolderId(?string $idstring $entity): string
  148.     {
  149.         if ($id !== null) {
  150.             /** @var MediaFolderEntity|null $folder */
  151.             $folder $this->mediaFolderRepository->search(new Criteria([$id]), Context::createDefaultContext())->first();
  152.             if ($folder !== null) {
  153.                 return $folder->getId();
  154.             }
  155.         }
  156.         $criteria = new Criteria();
  157.         $criteria->addFilter(new EqualsFilter('media_folder.defaultFolder.entity'$entity));
  158.         $criteria->addAssociation('defaultFolder');
  159.         /** @var MediaFolderEntity|null $default */
  160.         $default $this->mediaFolderRepository->search($criteriaContext::createDefaultContext())->first();
  161.         if ($default !== null) {
  162.             return $default->getId();
  163.         }
  164.         $criteria = new Criteria();
  165.         $criteria->addFilter(new EqualsFilter('media_folder.defaultFolder.entity''import_export_profile'));
  166.         $criteria->addAssociation('defaultFolder');
  167.         /** @var MediaFolderEntity|null $fallback */
  168.         $fallback $this->mediaFolderRepository->search($criteriaContext::createDefaultContext())->first();
  169.         if ($fallback === null) {
  170.             throw new \RuntimeException('Failed to find default media folder for import_export_profile');
  171.         }
  172.         return $fallback->getId();
  173.     }
  174.     private function fetchFileFromURL(string $urlstring $extension): ?MediaFile
  175.     {
  176.         $request = new Request();
  177.         $request->query->set('url'$url);
  178.         $request->query->set('extension'$extension);
  179.         $request->request->set('url'$url);
  180.         $request->request->set('extension'$extension);
  181.         $request->headers->set('content-type''application/json');
  182.         try {
  183.             $file $this->mediaService->fetchFile($request);
  184.             if ($file !== null && $file->getFileSize() > 0) {
  185.                 return $file;
  186.             }
  187.         } catch (\Throwable $throwable) {
  188.         }
  189.         return null;
  190.     }
  191.     /**
  192.      * @param array<string, mixed> $deserialized
  193.      *
  194.      * @return array<string, mixed>
  195.      */
  196.     private function fetchExistingMediaByHash(array $deserializedstring $hash): array
  197.     {
  198.         $criteria = new Criteria();
  199.         $criteria->addFilter(new EqualsFilter('metaData.hash'$hash));
  200.         $media $this->mediaRepository->search($criteriaContext::createDefaultContext())->first();
  201.         if ($media) {
  202.             $deserialized['id'] = $media->getId();
  203.         }
  204.         return $deserialized;
  205.     }
  206. }