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

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. class MediaSerializer extends EntitySerializer implements EventSubscriberInterface
  22. {
  23.     private FileSaver $fileSaver;
  24.     private MediaService $mediaService;
  25.     private EntityRepositoryInterface $mediaFolderRepository;
  26.     private EntityRepositoryInterface $mediaRepository;
  27.     /**
  28.      * @var array[]
  29.      */
  30.     private array $cacheMediaFiles = [];
  31.     public function __construct(
  32.         MediaService $mediaService,
  33.         FileSaver $fileSaver,
  34.         EntityRepositoryInterface $mediaFolderRepository,
  35.         EntityRepositoryInterface $mediaRepository
  36.     ) {
  37.         $this->mediaService $mediaService;
  38.         $this->fileSaver $fileSaver;
  39.         $this->mediaFolderRepository $mediaFolderRepository;
  40.         $this->mediaRepository $mediaRepository;
  41.     }
  42.     /**
  43.      * @param array|Struct|null $entity
  44.      *
  45.      * @return \Generator
  46.      */
  47.     public function serialize(Config $configEntityDefinition $definition$entity): iterable
  48.     {
  49.         yield from parent::serialize($config$definition$entity);
  50.     }
  51.     /**
  52.      * @param array|\Traversable $entity
  53.      *
  54.      * @return array|\Traversable
  55.      */
  56.     public function deserialize(Config $configEntityDefinition $definition$entity)
  57.     {
  58.         $entity = \is_array($entity) ? $entity iterator_to_array($entity);
  59.         $deserialized parent::deserialize($config$definition$entity);
  60.         $deserialized = \is_array($deserialized) ? $deserialized iterator_to_array($deserialized);
  61.         $url $entity['url'] ?? null;
  62.         if (empty($url)) {
  63.             return $deserialized;
  64.         }
  65.         if (!filter_var($url, \FILTER_VALIDATE_URL)) {
  66.             $deserialized['_error'] = new InvalidMediaUrlException($url);
  67.             return $deserialized;
  68.         }
  69.         $media null;
  70.         if (isset($deserialized['id'])) {
  71.             $media $this->mediaRepository->search(new Criteria([$deserialized['id']]), Context::createDefaultContext())->first();
  72.         }
  73.         $isNew $media === null;
  74.         if ($isNew || $media->getUrl() !== $url) {
  75.             $entityName $config->get('sourceEntity') ?? $definition->getEntityName();
  76.             $deserialized['mediaFolderId'] = $deserialized['mediaFolderId']
  77.                 ?? $this->getMediaFolderId($deserialized['id'] ?? null$entityName);
  78.             $deserialized['id'] = $deserialized['id'] ?? Uuid::randomHex();
  79.             $parsed parse_url($url);
  80.             $pathInfo pathinfo($parsed['path']);
  81.             $media $this->fetchFileFromURL((string) $url$pathInfo['extension'] ?? '');
  82.             if ($media === null) {
  83.                 $deserialized['_error'] = new MediaDownloadException($url);
  84.                 return $deserialized;
  85.             }
  86.             if ($isNew && $media->getHash()) {
  87.                 $deserialized $this->fetchExistingMediaByHash($deserialized$media->getHash());
  88.             }
  89.             $this->cacheMediaFiles[$deserialized['id']] = [
  90.                 'media' => $media,
  91.                 'destination' => $pathInfo['filename'],
  92.             ];
  93.         }
  94.         return $deserialized;
  95.     }
  96.     public function supports(string $entity): bool
  97.     {
  98.         return $entity === 'media';
  99.     }
  100.     /**
  101.      * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
  102.      */
  103.     public static function getSubscribedEvents()
  104.     {
  105.         return [
  106.             MediaEvents::MEDIA_WRITTEN_EVENT => 'persistMedia',
  107.         ];
  108.     }
  109.     /**
  110.      * @internal
  111.      */
  112.     public function persistMedia(EntityWrittenEvent $event): void
  113.     {
  114.         if (empty($this->cacheMediaFiles)) {
  115.             return;
  116.         }
  117.         $mediaFiles $this->cacheMediaFiles;
  118.         // prevent recursion
  119.         $this->cacheMediaFiles = [];
  120.         foreach ($event->getIds() as $id) {
  121.             if (!isset($mediaFiles[$id])) {
  122.                 continue;
  123.             }
  124.             $mediaFile $mediaFiles[$id];
  125.             $this->fileSaver->persistFileToMedia(
  126.                 $mediaFile['media'],
  127.                 $mediaFile['destination'],
  128.                 $id,
  129.                 $event->getContext()
  130.             );
  131.         }
  132.     }
  133.     private function getMediaFolderId(?string $id$entity): string
  134.     {
  135.         if ($id !== null) {
  136.             /** @var MediaFolderEntity|null $folder */
  137.             $folder $this->mediaFolderRepository->search(new Criteria([$id]), Context::createDefaultContext())->first();
  138.             if ($folder !== null) {
  139.                 return $folder->getId();
  140.             }
  141.         }
  142.         $criteria = new Criteria();
  143.         $criteria->addFilter(new EqualsFilter('media_folder.defaultFolder.entity'$entity));
  144.         $criteria->addAssociation('defaultFolder');
  145.         /** @var MediaFolderEntity|null $default */
  146.         $default $this->mediaFolderRepository->search($criteriaContext::createDefaultContext())->first();
  147.         if ($default !== null) {
  148.             return $default->getId();
  149.         }
  150.         $criteria = new Criteria();
  151.         $criteria->addFilter(new EqualsFilter('media_folder.defaultFolder.entity''import_export_profile'));
  152.         $criteria->addAssociation('defaultFolder');
  153.         /** @var MediaFolderEntity|null $fallback */
  154.         $fallback $this->mediaFolderRepository->search($criteriaContext::createDefaultContext())->first();
  155.         if ($fallback === null) {
  156.             throw new \RuntimeException('Failed to find default media folder for import_export_profile');
  157.         }
  158.         return $fallback->getId();
  159.     }
  160.     private function fetchFileFromURL(string $urlstring $extension): ?MediaFile
  161.     {
  162.         $request = new Request();
  163.         $request->query->set('url'$url);
  164.         $request->query->set('extension'$extension);
  165.         $request->request->set('url'$url);
  166.         $request->request->set('extension'$extension);
  167.         $request->headers->set('content-type''application/json');
  168.         try {
  169.             $file $this->mediaService->fetchFile($request);
  170.             if ($file !== null && $file->getFileSize() > 0) {
  171.                 return $file;
  172.             }
  173.         } catch (\Throwable $throwable) {
  174.         }
  175.         return null;
  176.     }
  177.     private function fetchExistingMediaByHash(array $deserializedstring $hash): array
  178.     {
  179.         $criteria = new Criteria();
  180.         $criteria->addFilter(new EqualsFilter('metaData.hash'$hash));
  181.         $media $this->mediaRepository->search($criteriaContext::createDefaultContext())->first();
  182.         if ($media) {
  183.             $deserialized['id'] = $media->getId();
  184.         }
  185.         return $deserialized;
  186.     }
  187. }