How to add images to an entity?¶
Danger
We’re sorry but this documentation section is outdated. Please have that in mind when trying to use it. You can help us making documentation up to date via Sylius Github. Thank you!
Extending entities with an images
field is quite a popular use case.
In this cookbook we will present how to add image to the Shipping Method entity.
Instructions:¶
1. Extend the ShippingMethod class with the ImagesAwareInterface¶
In order to override the ShippingMethod
that lives inside of the SyliusCoreBundle,
you have to create your own ShippingMethod class that will extend it:
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Sylius\Component\Core\Model\ImagesAwareInterface;
use Sylius\Component\Core\Model\ImageInterface;
use Sylius\Component\Core\Model\ShippingMethod as BaseShippingMethod;
class ShippingMethod extends BaseShippingMethod implements ImagesAwareInterface
{
/**
* @var Collection|ImageInterface[]
*/
protected $images;
public function __construct()
{
parent::__construct();
$this->images = new ArrayCollection();
}
/**
* {@inheritdoc}
*/
public function getImages(): Collection
{
return $this->images;
}
/**
* {@inheritdoc}
*/
public function getImagesByType(string $type): Collection
{
return $this->images->filter(function (ImageInterface $image) use ($type) {
return $type === $image->getType();
});
}
/**
* {@inheritdoc}
*/
public function hasImages(): bool
{
return !$this->images->isEmpty();
}
/**
* {@inheritdoc}
*/
public function hasImage(ImageInterface $image): bool
{
return $this->images->contains($image);
}
/**
* {@inheritdoc}
*/
public function addImage(ImageInterface $image): void
{
$image->setOwner($this);
$this->images->add($image);
}
/**
* {@inheritdoc}
*/
public function removeImage(ImageInterface $image): void
{
if ($this->hasImage($image)) {
$image->setOwner(null);
$this->images->removeElement($image);
}
}
}
Tip
Read more about customizing models in the docs here.
2. Register your extended ShippingMethod as a resource’s model class¶
With such a configuration you will register your ShippingMethod
class in order to override the default one:
# config/packages/sylius_shipping.yaml
sylius_shipping:
resources:
shipping_method:
classes:
model: App\Entity\ShippingMethod
3. Create the ShippingMethodImage class¶
In the App\Entity
namespace place the ShippingMethodImage
class which should look like this:
<?php
declare(strict_types=1);
namespace App\Entity;
use Sylius\Component\Core\Model\Image;
class ShippingMethodImage extends Image
{
}
4. Add the mapping file for the ShippingMethodImage¶
Your new entity will be saved in the database, therefore it needs a mapping file, where you will set the ShippingMethod
as the owner
of the ShippingMethodImage
.
# App/Resources/config/doctrine/ShippingMethodImage.orm.yml
App\Entity\ShippingMethodImage:
type: entity
table: app_shipping_method_image
manyToOne:
owner:
targetEntity: App\Entity\ShippingMethod
inversedBy: images
joinColumn:
name: owner_id
referencedColumnName: id
nullable: false
onDelete: CASCADE
5. Modify the ShippingMethod’s mapping file¶
The newly added images
field has to be added to the mapping, with a relation to the ShippingMethodImage
:
# App/Resources/config/doctrine/ShippingMethod.orm.yml
App\Entity\ShippingMethod:
type: entity
table: sylius_shipping_method
oneToMany:
images:
targetEntity: App\Entity\ShippingMethodImage
mappedBy: owner
orphanRemoval: true
cascade:
- all
6. Register the ShippingMethodImage as a resource¶
The ShippingMethodImage
class needs to be registered as a Sylius resource:
# app/config/config.yml
sylius_resource:
resources:
app.shipping_method_image:
classes:
model: App\Entity\ShippingMethodImage
7. Create the ShippingMethodImageType class¶
This is how the class for ShippingMethodImageType
should look like. Place it in the App\Form\Type\
directory.
<?php
declare(strict_types=1);
namespace App\Form\Type;
use Sylius\Bundle\CoreBundle\Form\Type\ImageType;
final class ShippingMethodImageType extends ImageType
{
/**
* {@inheritdoc}
*/
public function getBlockPrefix(): string
{
return 'app_shipping_method_image';
}
}
8. Register the ShippingMethodImageType as a service¶
After creating the form type class, you need to register it as a form.type
service like below:
# services.yml
services:
app.form.type.shipping_method_image:
class: App\Form\Type\ShippingMethodImageType
tags:
- { name: form.type }
arguments: ['%app.model.shipping_method_image.class%']
9. Add the ShippingMethodImageType to the resource form configuration¶
What is more the new form type needs to be configured as the resource form of the ShippingMethodImage
:
# app/config/config.yml
sylius_resource:
resources:
app.shipping_method_image:
classes:
form: App\Form\Type\ShippingMethodImageType
10. Extend the ShippingMethodType with the images field¶
Tip
Read more about customizing forms via extensions in the dedicated guide.
Create the form extension class for the Sylius\Bundle\ShippingBundle\Form\Type\ShippingMethodType
:
It needs to have the images field as a CollectionType.
<?php
declare(strict_types=1);
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\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
final class ShippingMethodTypeExtension extends AbstractTypeExtension
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('images', CollectionType::class, [
'entry_type' => ShippingMethodImageType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'label' => 'sylius.form.shipping_method.images',
]);
}
/**
* {@inheritdoc}
*/
public function getExtendedType(): string
{
return ShippingMethodType::class;
}
}
Tip
In case you need only a single image upload, this can be done in 2 very easy steps.
First, in the code for the form provided above set allow_add
and allow_delete
to false
Second, in the __construct
method of the ShippingMethod
entity you defined earlier add the following:
public function __construct()
{
parent::__construct();
$this->images = new ArrayCollection();
$this->addImage(new ShippingMethodImage());
}
Register the form extension as a service:
# services.yml
services:
app.form.extension.type.shipping_method:
class: App\Form\Extension\ShippingMethodTypeExtension
tags:
- { name: form.type_extension, extended_type: Sylius\Bundle\ShippingBundle\Form\Type\ShippingMethodType }
11. Declare the ImagesUploadListener service¶
In order to handle the image upload you need to attach the ImagesUploadListener
to the ShippingMethod
entity events:
# services.yml
services:
app.listener.images_upload:
class: Sylius\Bundle\CoreBundle\EventListener\ImagesUploadListener
parent: sylius.listener.images_upload
autowire: true
autoconfigure: false
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 }
12. Render the images field in the form view¶
In order to achieve that you will need to customize the form view from the SyliusAdminBundle/views/ShippingMethod/_form.html.twig
file.
Copy and paste its contents into your own app/Resources/SyliusAdminBundle/views/ShippingMethod/_form.html.twig
file,
and render the {{ form_row(form.images) }}
field.
{# app/Resources/SyliusAdminBundle/views/ShippingMethod/_form.html.twig #}
{% from '@SyliusAdmin/Macro/translationForm.html.twig' import translationForm %}
<div class="ui two column stackable grid">
<div class="column">
<div class="ui segment">
{{ form_errors(form) }}
<div class="three fields">
{{ form_row(form.code) }}
{{ form_row(form.zone) }}
{{ form_row(form.position) }}
</div>
{{ form_row(form.enabled) }}
<h4 class="ui dividing header">{{ 'sylius.ui.availability'|trans }}</h4>
{{ form_row(form.channels) }}
<h4 class="ui dividing header">{{ 'sylius.ui.category_requirements'|trans }}</h4>
{{ form_row(form.category) }}
{% for categoryRequirementChoiceForm in form.categoryRequirement %}
{{ form_row(categoryRequirementChoiceForm) }}
{% endfor %}
<h4 class="ui dividing header">{{ 'sylius.ui.taxes'|trans }}</h4>
{{ form_row(form.taxCategory) }}
<h4 class="ui dividing header">{{ 'sylius.ui.shipping_charges'|trans }}</h4>
{{ form_row(form.calculator) }}
{% for name, calculatorConfigurationPrototype in form.vars.prototypes %}
<div id="{{ form.calculator.vars.id }}_{{ name }}" data-container=".configuration"
data-prototype="{{ form_widget(calculatorConfigurationPrototype)|e }}">
</div>
{% endfor %}
{# Here you go! #}
{{ form_row(form.images) }}
<div class="ui segment configuration">
{% if form.configuration is defined %}
{% for field in form.configuration %}
{{ form_row(field) }}
{% endfor %}
{% endif %}
</div>
</div>
</div>
<div class="column">
{{ translationForm(form.translations) }}
</div>
</div>
Tip
Learn more about customizing templates here.
13. Validation¶
Your form so far is working fine, but don’t forget about validation.
The easiest way is using validation config files under the App/Resources/config/validation
folder.
This could look like this e.g.:
# src\Resources\config\validation\ShippingMethodImage.yml
App\Entity\ShippingMethodImage:
properties:
file:
- Image:
groups: [sylius]
maxHeight: 1000
maxSize: 10240000
maxWidth: 1000
mimeTypes:
- "image/png"
- "image/jpg"
- "image/jpeg"
- "image/gif"
mimeTypesMessage: 'This file format is not allowed. Please use PNG, JPG or GIF files.'
minHeight: 200
minWidth: 200
This defines the validation constraints for each image entity.
Now connecting the validation of the ShippingMethod
to the validation of each single Image Entity
is left:
# src\Resources\config\validation\ShippingMethod.yml
App\Entity\ShippingMethod:
properties:
...
images:
- Valid: ~