CDN Product Images: Installation
Installing the Sylius plugin CDN Product Images: Composer via Packeton, bundle, routes, setup command and the product-image template override.
CDN Product Images: Installation
This documentation always describes the latest published version.
Live Demo: Try this and other plugins live — no installation, no risk: demo.markus-michalski.net
Composer
Private repositories are managed via Packeton (
https://packeton.markus-michalski.net). Credentials are provided after license purchase/approval.
{ "repositories": [{ "type": "composer", "url": "https://packeton.markus-michalski.net" }] }
composer require mmd/sylius-cdn-product-images-plugin
After composer require:
-
Register the bundle in
config/bundles.php:Mmd\SyliusCdnProductImagesPlugin\MmdSyliusCdnProductImagesPlugin::class => ['all' => true], -
Import the Admin routes - this is what makes the route the "CDN Integration" sidebar entry links to actually exist:
# config/routes/mmd_sylius_cdn_product_images.yaml mmd_sylius_cdn_product_images: resource: '@MmdSyliusCdnProductImagesPlugin/config/routes/admin.yaml' -
Run the setup command:
bin/console mmd:cdn-product-images:setupThe command executes only this plugin's own still-open migrations - never those of the app or of other plugins - and is idempotent, so running it again after every
composer updatedoes nothing when there's no new migration. It also checks that the routes from step 2 are imported and prints the snippet above if not, without ever writing intoconfig/itself. If Sylius' own core migrations are still open, it stops and asks fordoctrine:migrations:migrateto run first. It asks for interactive confirmation; add--no-interactionin scripts. Plaindoctrine:migrations:migrateremains usable as an alternative. -
Install assets if not already done:
bin/console assets:install
No config/packages/*.yaml entry needed - see Configuration.
Product images: making the field visible
Taxon images work right after installation (the plugin ships its own Twig Hook for that). Product images need one extra step, because Sylius' product-image form template has no hook point and no form_rest() fallback - the cdnUrl field exists on the form object either way, but nothing renders it without this override:
{# templates/bundles/SyliusAdminBundle/product/form/sections/media/images.html.twig #}
{# Full copy of @SyliusAdmin/product/form/sections/media/images.html.twig, with one added
block for image_form.cdnUrl. Re-check against the vendor original on every Sylius minor -
Symfony's template override replaces the whole file, it doesn't merge. #}
{% set images = hookable_metadata.context.form.images %}
{% set product = hookable_metadata.context.product %}
<div class="row" {{ sylius_test_html_attribute('images') }} {{ sylius_test_html_attribute('image-subforms') }}>
{% for image_form in images %}
<div class="col-12 col-md-6 row mb-4"
{{ sylius_test_html_attribute('image-subform')}}
{{ sylius_test_html_attribute('type', image_form.type.vars.value) }}
{{ sylius_test_html_attribute('image-url', image_form.vars.value.path ? image_form.vars.value.path|imagine_filter('sylius_small') : null) }}
>
<div class="col-auto">
<div>
{% if image_form.vars.value.path is not null %}
<span class="avatar avatar-xl" style="background-image: url('{{ image_form.vars.value.path|imagine_filter('sylius_small') }}')"></span>
{% else %}
<span class="avatar avatar-xl"></span>
{% endif %}
</div>
<div class="mt-3 d-flex items-center">
{{ form_widget(image_form.vars.button_delete, { label: 'sylius.ui.delete'|trans, attr: { class: 'btn btn-outline-danger w-100','data-test-image-delete': '' }}) }}
</div>
</div>
<div class="col">
<div class="mb-3">
{{ form_row(image_form.type, sylius_test_form_attribute('type')) }}
</div>
<div class="mb-3">
{% if product.id is not null and 0 != product.variants|length and not product.simple %}
{{ form_row(image_form.productVariants, sylius_test_form_attribute('product-variant', image_form.productVariants.vars.value|join(','))) }}
{% endif %}
</div>
<div class="mb-3">
{{ form_row(image_form.file, sylius_test_form_attribute('file')) }}
</div>
{% if image_form.cdnUrl is defined %}
<div class="mb-3">
{{ form_row(image_form.cdnUrl) }}
</div>
{% endif %}
{% if image_form.position is defined %}
<div class="mb-3">
{{ form_row(image_form.position, sylius_test_form_attribute('position')) }}
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
Place the file in the consuming application at templates/bundles/SyliusAdminBundle/product/form/sections/media/images.html.twig (Symfony's classic bundle-template-override path) - a plugin cannot ship this override from inside its own package, only the app itself can override a vendor bundle's template.
A proper fix directly in the plugin (its own Twig Hook, the same way taxon images already have one) is in progress - once Sylius makes this template hookable, the manual override goes away.
Update
composer update mmd/sylius-cdn-product-images-plugin
bin/console mmd:cdn-product-images:setup
bin/console cache:clear
Older installations configured
cdn_domain/signing_api_url/signing_api_token/cdn_schemevia environment variables andconfig/packages/mmd_sylius_cdn_product_images.yaml. That file must be deleted after updating - any leftover key makes the container build fail loudly instead of silently doing nothing. Re-enter the four values once via Sylius Admin afterwards, see Configuration.
Uninstallation
- Remove the template override and Twig Hooks from your own app (see above)
- Roll back the migration while the bundle is still registered:
bin/console doctrine:migrations:execute --down "Mmd\SyliusCdnProductImagesPlugin\Migrations\Version20260911140000" - Remove the route import (
config/routes/mmd_sylius_cdn_product_images.yaml) and the bundle entry inconfig/bundles.php - Remove the package:
composer remove mmd/sylius-cdn-product-images-plugin