Phil
Phil

Reputation: 53

Media creation via php in Shopware 6

i'm struggling get a media import via PHP for Shopware 6 to work.

This is my service:

<?php declare(strict_types=1);

namespace My\Namespace\Service;

use Shopware\Core\Content\Media\File\MediaFile;
use Shopware\Core\Content\Media\MediaService;
use Shopware\Core\Framework\Context;

class ImageImport
{
    /**
     * @var MediaService
     */
    protected $mediaService;

    /**
     * ImageImport constructor.
     * @param MediaService $mediaService
     */
    public function __construct(MediaService $mediaService)
    {
        $this->mediaService = $mediaService;
    }


    public function addImageToProductMedia($imageUrl, Context $context)
    {
        $mediaId = NULL;
        $context->disableCache(function (Context $context) use ($imageUrl, &$mediaId): void {
            $filePathParts = explode('/', $imageUrl);
            $fileName = array_pop($filePathParts);
            $fileNameParts = explode('.', $fileName);

            $actualFileName = $fileNameParts[0];
            $fileExtension = $fileNameParts[1];


            if ($actualFileName && $fileExtension) {
                $tempFile = tempnam(sys_get_temp_dir(), 'image-import');
                file_put_contents($tempFile, file_get_contents($imageUrl));

                $fileSize = filesize($tempFile);
                $mimeType = mime_content_type($tempFile);

                $mediaFile = new MediaFile($tempFile, $mimeType, $fileExtension, $fileSize);

                $mediaId = $this->mediaService->saveMediaFile($mediaFile, $actualFileName, $context, 'product');
            }
        });
        return $mediaId;
    }
}

A entry in the table media with the correct media_folder_association is created. And as far as i can see there are no differences to other medias uploaded via backend (except private is 1 and user_id is NULL).

But in the backend the media entries are broken, seems like it can not load the actual image file (i've tried to set private to true to see it in the media section, same happens when adding the media to a product via php, but i guess the problem is before any assignment to products).

Image in backend media

Has anybody a suggestion whats wrong here?

Thanks Phil

===== SOLUTION ======

Here is the updated and working service:

<?php declare(strict_types=1);

namespace My\Namespace\Service;

use Shopware\Core\Content\Media\File\FileSaver;
use Shopware\Core\Content\Media\File\MediaFile;
use Shopware\Core\Content\Media\MediaService;
use Shopware\Core\Framework\Context;

class ImageImport
{
    /**
     * @var MediaService
     */
    protected $mediaService;

    /**
     * @var FileSaver
     */
    private $fileSaver;

    /**
     * ImageImport constructor.
     * @param MediaService $mediaService
     * @param FileSaver $fileSaver
     */
    public function __construct(MediaService $mediaService, FileSaver $fileSaver)
    {
        $this->mediaService = $mediaService;
        $this->fileSaver = $fileSaver;
    }


    public function addImageToProductMedia($imageUrl, Context $context)
    {
        $mediaId = NULL;
        $context->disableCache(function (Context $context) use ($imageUrl, &$mediaId): void {
            $filePathParts = explode('/', $imageUrl);
            $fileName = array_pop($filePathParts);
            $fileNameParts = explode('.', $fileName);

            $actualFileName = $fileNameParts[0];
            $fileExtension = $fileNameParts[1];


            if ($actualFileName && $fileExtension) {
                $tempFile = tempnam(sys_get_temp_dir(), 'image-import');
                file_put_contents($tempFile, file_get_contents($imageUrl));

                $fileSize = filesize($tempFile);
                $mimeType = mime_content_type($tempFile);

                $mediaFile = new MediaFile($tempFile, $mimeType, $fileExtension, $fileSize);
                $mediaId = $this->mediaService->createMediaInFolder('product', $context, false);

                $this->fileSaver->persistFileToMedia(
                    $mediaFile,
                    $actualFileName,
                    $mediaId,
                    $context
                );
            }
        });
        return $mediaId;
    }
}

Upvotes: 5

Views: 3523

Answers (1)

RappY
RappY

Reputation: 472

In order to import files to Shopware 6 theres two steps which are necessary:

  1. You have to create a media file object (MediaDefinition / media table). Take a look at the MediaConverter
  2. Create a new entry in the SwagMigrationMediaFileDefinition (swag_migration_media_file table).

Each entry in the swag_migration_media_file table of the associated migration run will get processed by an implementation of MediaFileProcessorInterface.

To add a file to the table you can do something like this in your Converter class (this example is from the MediaConverter):

abstract class MediaConverter extends ShopwareConverter
{

    public function convert(
        array $data,
        Context $context,
        MigrationContextInterface $migrationContext
    ): ConvertStruct {
        $this->generateChecksum($data);
        $this->context = $context;
        $this->locale = $data['_locale'];
        unset($data['_locale']);

        $connection = $migrationContext->getConnection();
        $this->connectionId = '';
        if ($connection !== null) {
            $this->connectionId = $connection->getId();
        }

        $converted = [];
        $this->mainMapping = $this->mappingService->getOrCreateMapping(
            $this->connectionId,
            DefaultEntities::MEDIA,
            $data['id'],
            $context,
            $this->checksum
        );
        $converted['id'] = $this->mainMapping['entityUuid'];

        if (!isset($data['name'])) {
            $data['name'] = $converted['id'];
        }

        $this->mediaFileService->saveMediaFile(
            [
                'runId' => $migrationContext->getRunUuid(),
                'entity' => MediaDataSet::getEntity(), // important to distinguish between private and public files
                'uri' => $data['uri'] ?? $data['path'],
                'fileName' => $data['name'], // uri or path to the file (because of the different implementations of the gateways)
                'fileSize' => (int) $data['file_size'],
                'mediaId' => $converted['id'], // uuid of the media object in Shopware 6
            ]
        );
        unset($data['uri'], $data['file_size']);

        $this->getMediaTranslation($converted, $data);
        $this->convertValue($converted, 'title', $data, 'name');
        $this->convertValue($converted, 'alt', $data, 'description');

        $albumMapping = $this->mappingService->getMapping(
            $this->connectionId,
            DefaultEntities::MEDIA_FOLDER,
            $data['albumID'],
            $this->context
        );

        if ($albumMapping !== null) {
            $converted['mediaFolderId'] = $albumMapping['entityUuid'];
            $this->mappingIds[] = $albumMapping['id'];
        }

        unset(
            $data['id'],
            $data['albumID'],

            // Legacy data which don't need a mapping or there is no equivalent field
            $data['path'],
            $data['type'],
            $data['extension'],
            $data['file_size'],
            $data['width'],
            $data['height'],
            $data['userID'],
            $data['created']
        );

        $returnData = $data;
        if (empty($returnData)) {
            $returnData = null;
        }
        $this->updateMainMapping($migrationContext, $context);

        // The MediaWriter will write this Shopware 6 media object
        return new ConvertStruct($converted, $returnData, $this->mainMapping['id']);
    }

}

swag_migration_media_files are processed by the right processor service. This service is different for documents and normal media, but it still is gateway dependent

=== DIFFERENT APPROACH (Shyim suggestion) ===

Take a look at this (taken from Shopwaredowntown's Github repository):

public function upload(UploadedFile $file, string $folder, string $type, Context $context): string
    {
        $this->checkValidFile($file);

        $this->validator->validate($file, $type);

        $mediaFile = new MediaFile($file->getPathname(), $file->getMimeType(), $file->getClientOriginalExtension(), $file->getSize());

        $mediaId = $this->mediaService->createMediaInFolder($folder, $context, false);

        try {
            $this->fileSaver->persistFileToMedia(
                $mediaFile,
                pathinfo($file->getFilename(), PATHINFO_FILENAME),
                $mediaId,
                $context
            );
        } catch (MediaNotFoundException $e) {
            throw new UploadException($e->getMessage());
        }

        return $mediaId;
    }

src/Portal/Hacks/StorefrontMediaUploader.php:49

public function upload(UploadedFile $file, string $folder, string $type, Context $context): string

Upvotes: 2

Related Questions