Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions guides/plugins/plugins/content/media/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ nav:

# Media

Shopware Media offers the ability to add media file extensions and prevent the deletion of media files within the e-commerce platform.
The media subsystem in Shopware can be extended in several ways with plugins.
This section contains guides for common media extension points.

With the Media plugin, users can specify and configure new media file extensions. This allows businesses to define different types of media files, such as images, videos, or documents, that can be uploaded and used within the Shopware platform.
## Guides

Furthermore, the plugin helps prevent the deletion of media files that are not used in your application.
* [Prevent Deletion of Media Files Referenced in your Plugins](./prevent-deletion-of-media-files-referenced-in-your-plugins.md): Prevent your plugin's media references from being deleted by `media:delete-unused` if they are not represented by foreign keys.
* [Add Custom Media File Extension](./add-custom-file-extension.md): Allow additional file extensions and map them to the correct media type.
* [Remote Thumbnail Generation](./remote-thumbnail-generation.md): Use externally generated thumbnails instead of local thumbnail generation.
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,20 @@ The ability to prevent Media entities from being deleted is available since Shop

## Overview

The Shopware CLI application provides a `media:delete-unused` command which deletes all media entities and their corresponding files which are not used in your application.
The Shopware CLI application provides a `media:delete-unused` command, which deletes all media entities and their corresponding files that are not used in your application.
Not used means that it is not referenced by any other entity. This works well in the simple case that all your entity definitions store references to Media entities with correct foreign keys.

However, this does not cover all the possible cases, even for many internal Shopware features. For example the CMS entities store their configuration as JSON blobs with references to Media IDs stored in a nested data structure.
However, this does not cover all the possible cases, even for many internal Shopware features. For example, the CMS entities store their configuration as JSON blobs with references to Media IDs stored in a nested data structure.

In order to fix the case of Media references that cannot be resolved without knowledge of the specific entity and its features, an extension point is provided via an event.
To address cases where Media references cannot be resolved without knowledge of the specific entity and its features, an extension point is provided via an event.

If you are developing an extension which references Media entities, and you cannot use foreign keys, this guide will detail how to prevent shopware deleting the Media entities your extension references.
If you are developing an extension that references Media entities, and you cannot use foreign keys, this guide explains how to prevent Shopware from deleting the Media entities your extension references.

## Prerequisites

As most of our plugin guides, this guide was also built upon our [Plugin base guide](../../plugin-base-guide).
Furthermore, you'll have to know about adding classes to the [Dependency injection](../../plugin-fundamentals/dependency-injection) container
and about using a subscriber in order to [Listen to events](../../plugin-fundamentals/listening-to-events).
and about using a subscriber to [Listen to events](../../plugin-fundamentals/listening-to-events).

## The deletion process

Expand All @@ -38,6 +38,9 @@ The remaining Media IDs will then be deleted by the `\Shopware\Core\Content\Medi

Please note that this process is completed in small batches to maintain stability, so the event may be dispatched multiple times when an installation has many unused Media entities.

Before running the command in production, consider using `media:delete-unused --dry-run` to inspect candidates first.
If you need a machine-readable export, you can use `media:delete-unused --report` instead.

## Adding a subscriber

In this section, we're going to register a subscriber for the `\Shopware\Core\Content\Media\Event\UnusedMediaSearchEvent` event.
Expand Down Expand Up @@ -79,15 +82,15 @@ class UnusedMediaSubscriber implements EventSubscriberInterface
}
```

You can use the method `getUnusedIds` of the `$event` variable to get the current an array of Media IDs scheduled for removal.
You can use the method `getUnusedIds` of the `$event` variable to get the current array of Media IDs scheduled for removal.

You can use these IDs to query whatever storage your plugin uses to store references to Media entities, to check if they are currently used.
You can use these IDs to query the storage your plugin uses to store references to Media entities and check whether they are currently in use.

If any of the IDs are used by your plugin, you can use the method `markAsUsed` of the `$event` variable to prevent the Media entities from being deleted. `markAsUsed` accepts an array of string IDs.
If your plugin uses any of the IDs, you can use the method `markAsUsed` of the `$event` variable to prevent the Media entities from being deleted. `markAsUsed` accepts an array of string IDs.

If your storage is a relational database such as MySQL you should, when possible, use direct database queries to check for references. This saves memory and CPU cycles by not loading unnecessary data.
If your storage is a relational database such as MySQL, you should, when possible, use direct database queries to check for references. This saves memory and CPU cycles by avoiding the loading of unnecessary data.

Imagine an extension which provides an image slider feature. An implementation of `getUsedMediaIds` might look something like the following:
Imagine an extension that provides an image slider feature. An implementation of `getUsedMediaIds` might look something like the following:

```php
// <plugin root>/src/Subscriber/UnusedMediaSubscriber.php
Expand All @@ -96,26 +99,28 @@ private function getUsedMediaIds(array $idsToBeDeleted): array
$sql = <<<SQL
SELECT JSON_EXTRACT(slider_config, "$.images") as mediaIds FROM my_slider_table
WHERE JSON_OVERLAPS(
JSON_EXTRACT(slider_config, "$.images"),
JSON_EXTRACT(slider_config, "$.images"),
JSON_ARRAY(?)
);
SQL;
);
SQL;

$usedMediaIds = $this->connection->fetchFirstColumn(
$sql,
[$event->getUnusedIds()],
[$idsToBeDeleted],
[ArrayParameterType::STRING]
);

return array_map(fn (string $ids) => json_decode($ids, true, \JSON_THROW_ON_ERROR), $usedMediaIds);
return array_merge(
...array_map(fn (string $ids) => json_decode($ids, true, \JSON_THROW_ON_ERROR), $usedMediaIds)
);
}
```

In the above example, `$this->connection` is an instance of `\Doctrine\DBAL\Connection` which can be injected in to your subscriber.
We use the MySQL JSON functions to query the table `my_slider_table`.
We check if there are any references to the Media IDs from the event, in the `slider_config` column which is a JSON blob. The `JSON_EXTRACT` function looks into the `images` key of the data. We use the where condition in combination with the `JSON_OVERLAPS` function to only query rows that have references to the Media IDs we are interested in.
In the above example, `$this->connection` is an instance of `\Doctrine\DBAL\Connection` which can be injected into your subscriber.
We use MySQL JSON functions to query the `my_slider_table` table.
We check whether there are any references to the Media IDs from the event in the `slider_config` column, which is a JSON blob. The `JSON_EXTRACT` function looks into the `images` key of the data. We use the WHERE condition in combination with the `JSON_OVERLAPS` function to query only rows that reference the Media IDs we are interested in.

Finally, we return all the IDs of Media which are used in the slider config so that they are not deleted.
Finally, we return all the IDs of Media used in the slider config so they are not deleted.

Make sure to register your event subscriber to the [Dependency injection container](../../plugin-fundamentals/dependency-injection)
by using the tag `kernel.event_subscriber`.
Expand Down