SyliusCon 2025
Early Bird Deal
LogoLogo
🛣️ Roadmap💻 Sylius Demo💬 Community Slack
  • Sylius Documentation
  • Sylius Plugins
  • Sylius Stack
  • 📖Sylius 2.0 Documentation
    • Organization
      • Release Cycle
      • Backwards Compatibility Promise
      • Sylius Team
      • Sylius Roadmap
  • Getting Started with Sylius
    • Installation
    • Basic Configuration
    • Shipping & Payment
    • First Product
    • Customizing the Shop
    • Customizing Business Logic
    • Using API
    • Installing Plugins
    • Deployment
    • Summary
  • The Book
    • Introduction to Sylius
    • Installation
      • System Requirements
      • Sylius CE Installation
        • Sylius CE Installation with Docker
      • ➕Sylius Plus Installation
      • Upgrading Sylius CE
      • Upgrading Sylius Plus
    • Architecture
      • Architecture Overview
      • Architectural Drivers
      • Resource Layer
      • State Machine
      • Translations
      • E-Mails
      • Contact
      • Fixtures
      • Events
    • Configuration
      • Channels
      • Locales
      • Currencies
    • Customers
      • Customer & ShopUser
      • ➕Customer Pools
      • AdminUser
      • Addresses
        • Countries
        • Zones
        • Addresses
        • Address Book
    • Products
      • Products
      • Product Reviews
      • Product Associations
      • Attributes
      • Pricing
      • Catalog Promotions
      • Taxons
      • Inventory
      • ➕Multi-Source Inventory
      • Search
    • Carts & Orders
      • Orders
      • Cart flow
      • Taxation
      • Adjustments
      • Cart Promotions
      • Coupons
      • Payments
      • 🧩Invoices
      • Shipments
    • Support
    • Contributing
      • Contributing Code
        • Submitting a Patch
        • ⚠️Security Issues
        • Coding Standards
        • Conventions
        • Sylius License and Trademark
      • Contributing Translations
      • Key Contributors
  • The Customization Guide
    • Customizing Models
      • How to add a custom model?
      • How to add a custom translatable model?
    • Customizing Forms
      • How to add a live form for a custom model?
    • Customizing Styles
    • Customizing Validation
    • Customizing Menus
    • Customizing Templates
    • Customizing Translations
    • Customizing Flashes
    • Customizing State Machines
    • Customizing Grids
    • Customizing Fixtures
    • Customizing API
    • Customizing Serialization of API
    • Customizing Payments
      • How to integrate a Payment Gateway as a Plugin?
  • 🧑‍🍳The Cookbook
  • How to resize images?
  • How to add one image to an entity?
  • How to add multiple images to an entity?
  • Sylius 1.X Documentation
    • 📓Sylius 1.x Documentation
Powered by GitBook
LogoLogo

Developer

  • Community
  • Online Course

About

  • Team

© 2025 Sylius. All Rights Reserved

On this page
  • Prerequisites
  • Step 1: Create the Image Entity
  • Step 2: Extend the Owner Entity
  • Step 3: Configure Resources
  • Step 4: Create the Image Form Type
  • Step 5: Extend the Form for Shipping Method
  • Step 6: Enable Image Upload via Listener
  • Step 7: (Optional) Add Validation Constraints
  • Step 8: Customize the Shipping Method twig hooks

Was this helpful?

Edit on GitHub

How to add multiple images to an entity?

This guide explains how to associate multiple images with a single entity in Sylius 2.x using a one-to-many relationship. We'll use the ShippingMethod entity as an example, but this applies to any entity.


Prerequisites

  • Sylius 2.x is installed

  • LiipImagineBundle is configured

  • Doctrine is configured with migrations

  • The media path (public/media/image/) is writable


Step 1: Create the Image Entity

<?php

// src/Entity/ShippingMethodImage.php

namespace App\Entity\Shipping;

use Sylius\Component\Core\Model\Image;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(name: 'app_shipping_method_image')]
class ShippingMethodImage extends Image
{
    #[ORM\ManyToOne(
        targetEntity: ShippingMethod::class,
        inversedBy: 'images'
    )]
    #[ORM\JoinColumn(
        name: 'owner_id',
        referencedColumnName: 'id',
        nullable: false,
        onDelete: 'CASCADE'
    )]
    protected $owner = null;
}

Step 2: Extend the Owner Entity

<?php

// src/Entity/ShippingMethod.php

namespace App\Entity\Shipping;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Sylius\Component\Core\Model\ImageInterface;
use Sylius\Component\Core\Model\ImagesAwareInterface;
use Sylius\Component\Core\Model\ShippingMethod as BaseShippingMethod;

#[ORM\Entity]
#[ORM\Table(name: 'sylius_shipping_method')]
class ShippingMethod extends BaseShippingMethod implements ImagesAwareInterface
{
    #[ORM\OneToMany(
        targetEntity: ShippingMethodImage::class,
        mappedBy: 'owner',
        orphanRemoval: true,
        cascade: ['persist', 'remove', 'merge', 'detach']
    )]
    private Collection $images;

    public function __construct()
    {
        parent::__construct();
        $this->images = new ArrayCollection();
    }

    public function getImages(): Collection
    {
        return $this->images;
    }

    public function getImagesByType(string $type): Collection
    {
        return $this->images->filter(fn(ImageInterface $image) => $image->getType() === $type);
    }

    public function hasImages(): bool
    {
        return !$this->images->isEmpty();
    }

    public function hasImage(ImageInterface $image): bool
    {
        return $this->images->contains($image);
    }

    public function addImage(ImageInterface $image): void
    {
        if (!$this->hasImage($image)) {
            $image->setOwner($this);
            $this->images->add($image);
        }
    }

    public function removeImage(ImageInterface $image): void
    {
        if ($this->hasImage($image)) {
            $image->setOwner(null);
            $this->images->removeElement($image);
        }
    }
}

Step 3: Configure Resources

# config/packages/_sylius.yaml
sylius_resource:
    resources:
        app.shipping_method_image:
            classes:
                model: App\Entity\ShippingMethodImage
                form: App\Form\Type\ShippingMethodImageType

Step 4: Create the Image Form Type

<?php

// src/Form/Type/ShippingMethodImageType.php

namespace App\Form\Type;

use App\Entity\Shipping\ShippingMethodImage;
use Sylius\Bundle\CoreBundle\Form\Type\ImageType;

final class ShippingMethodImageType extends ImageType
{
    public function __construct()
    {
        parent::__construct(ShippingMethodImage::class, ['sylius']);
    }

    public function getBlockPrefix(): string
    {
        return 'app_shipping_method_image';
    }
}

Register the form type if necessary:

# config/services.yaml
services:
    App\Form\Type\ShippingMethodImageType:
        tags:
            - { name: form.type }

Step 5: Extend the Form for Shipping Method

<?php

// src/Form/Extension/ShippingMethodTypeExtension.php

namespace App\Form\Extension;

use App\Form\Type\ShippingMethodImageType;
use Sylius\Bundle\ShippingBundle\Form\Type\ShippingMethodType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\UX\LiveComponent\Form\Type\LiveCollectionType;

final class ShippingMethodTypeExtension extends AbstractTypeExtension
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->add('images', LiveCollectionType::class, [
            'entry_type' => ShippingMethodImageType::class,
            'allow_add' => true,
            'allow_delete' => true,
            'by_reference' => false,
            'label' => 'sylius.form.shipping_method.images',
        ]);
    }

    public static function getExtendedTypes(): iterable
    {
        return [ShippingMethodType::class];
    }
}

Register the extension:

# config/services.yaml
services:
    App\Form\Extension\ShippingMethodTypeExtension:
        tags:
            - { name: form.type_extension }

Step 6: Enable Image Upload via Listener

# config/services.yaml
services:
    app.listener.images_upload:
        class: Sylius\Bundle\CoreBundle\EventListener\ImagesUploadListener
        parent: sylius.listener.images_upload
        autowire: true
        public: false
        tags:
            - { name: kernel.event_listener, event: sylius.shipping_method.pre_create, method: uploadImages }
            - { name: kernel.event_listener, event: sylius.shipping_method.pre_update, method: uploadImages }

Step 7: (Optional) Add Validation Constraints

// src/Entity/Shipping/ShippingMethodImage.php

use Symfony\Component\Validator\Constraints as Assert;
​
    #[Assert\Image(
        groups: ['sylius'],
        mimeTypes: ['image/png', 'image/jpeg', 'image/gif'],
        maxSize: '10M'
    )]
    protected $file;
// App\Entity\Shipping\ShippingMethod.php

#[Assert\Valid]
private Collection $images;

Step 8: Customize the Shipping Method twig hooks

Inspect the shipping method form, let's assume you want to add new new section Images that is between the general and configuration.

  1. Configure hooks for your new images section

# config/packages/_sylius.yaml
sylius_twig_hooks:
    hooks:
        'sylius_admin.shipping_method.update.content.form#left':
            images:
                template: '/admin/shipping_method/form/sections/images.html.twig'
                priority: 150 # to place it between general and configuration sections
                
        'sylius_admin.shipping_method.update.content.form.images':
            content:
                template: '/admin/shipping_method/form/sections/images/content.html.twig'
                priority: 100
            add_button:
                template: '/admin/shipping_method/form/sections/images/add_button.html.twig'
                priority: 0
                
        'sylius_admin.shipping_method.create.content.form#left':
            images:
                template: '/admin/shipping_method/form/sections/images.html.twig'
                priority: 150

        'sylius_admin.shipping_method.create.content.form.images':
            content:
                template: '/admin/shipping_method/form/sections/images/content.html.twig'
                priority: 100
            add_button:
                template: '/admin/shipping_method/form/sections/images/add_button.html.twig'
                priority: 0
  1. Create templates for your hooks

{# templates/admin/shipping_method/form/sections/images.html.twig #}

<div class="card mb-3">
    <div class="card-header">
        <div class="card-title">
            {{ 'sylius.ui.images'|trans }}
        </div>
    </div>
    <div class="card-body">
        {% hook 'images' %}
    </div>
</div>
{# templates/admin/shipping_method/form/sections/images/content.html.twig #}

{% set images = hookable_metadata.context.form.images %}

<div class="row">
    {% for image_form in images %}
        <div class="col-12 col-md-6 row mb-4">
            <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' }}) }}
                </div>
            </div>
            <div class="col">
                <div class="mb-3">
                    {{ form_row(image_form.file) }}
                </div>
            </div>
        </div>
    {% endfor %}
</div>
{# templates/admin/shipping_method/form/sections/images/add_button.html.twig #}

<div class="d-grid gap-2">
    {{ form_widget(hookable_metadata.context.form.images.vars.button_add) }}
</div>

Step 9: Result

PreviousHow to add one image to an entity?

Last updated 3 days ago

Was this helpful?

The Shipping method has now collection of images !

🎉