Sylius: How to add position to product images
January 5th, 2022
I've recently been working on migrating a Magento 1 store to Sylius, an open source e-commerce platform based on the Symfony framework.
Whilst Sylius contains a lot of features, it doesn't come with functionality to change the ordering of images in the admin area. This is something I had to add manually.
Here's how to add a position to the product images tab.
Migration
First step is to add the position
column to the product_images
database table.
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20220105112323 extends AbstractMigration
{
public function getDescription() : string
{
return 'Adds a position field to product_images table';
}
public function up(Schema $schema) : void
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE sylius_product_image ADD COLUMN position INT UNSIGNED DEFAULT 1');
}
public function down(Schema $schema) : void
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE sylius_product_image DROP COLUMN position');
}
}
Extend the form
Next we should extend the form type used in the admin area. To find the form type run the following in your terminal:
> php bin/console debug:container | grep product.image | grep form
sylius.form.type.product_image Sylius\Bundle\CoreBundle\Form\Type\Product\ProductImageType
To find out which class you need to extend, use the console:
> php bin/console debug:container sylius.form.type.product_image
Information for Service "sylius.form.type.product_image"
========================================================
---------------- -------------------------------------------------------------
Option Value
---------------- -------------------------------------------------------------
Service ID sylius.form.type.product_image
Class Sylius\Bundle\CoreBundle\Form\Type\Product\ProductImageType
Tags form.type
Public yes
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired no
Autoconfigured no
---------------- -------------------------------------------------------------
Great, now we know that we need to extend the Sylius\Bundle\CoreBundle\Form\Type\Product\ProductImageType
class. Let's do that:
Extend the class
<?php
declare(strict_types=1);
namespace App\Form\Extension;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Sylius\Bundle\CoreBundle\Form\Type\Product\ProductImageType;
final class ProductImageFormExtension extends AbstractTypeExtension
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('position', NumberType::class, [
'required' => false,
'label' => 'Position',
]);
}
public static function getExtendedTypes(): iterable
{
return [ProductImageType::class];
}
}
Add the extended form to Sylius
Now that we have overridden the form class we need to let Sylius know about it. Open your config/services.yaml
file and add the following in the services
node:
app.form.extension.type.product_image:
class: App\Form\Extension\ProductImageFormExtension
tags:
- { name: form.type_extension, extended_type: Sylius\Bundle\CoreBundle\Form\Type\Product\ProductImageType }
Looking good - go to a product in the admin area, and click on the 'Media' tab. What? No position field? Thats because we haven't told the template to show the position field yet.
Add position field to the template
Copy the image template from the vendor dir to your local theme folder:
cp vendor/sylius/sylius/src/Sylius/Bundle/AdminBundle/Resources/views/Form/imagesTheme.html.twig templates/bundles/SyliusAdminBundle/Form/
Then edit the file and add the position:
# templates/bundles/SyliusAdminBundle/Form/imagesTheme.html.twig
{% extends '@SyliusUi/Form/imagesTheme.html.twig' %}
{% block sylius_product_image_widget %}
{{ block('sylius_image_widget') }}
{# ADD THE POSITION FIELD HERE ↓ #}
{{ form_row(form.position) }}
{% apply spaceless %}
{% if product.id is not null and 0 != product.variants|length and not product.simple %}
<br/>
{{ form_row(form.productVariants, {'remote_url': path('sylius_admin_ajax_product_variants_by_phrase', {'productCode': product.code}), 'remote_criteria_type': 'contains', 'remote_criteria_name': 'phrase', 'load_edit_url': path('sylius_admin_ajax_product_variants_by_codes', {'productCode': product.code})}) }}
{% endif %}
{% endapply %}
{% endblock %}
Test it works in the admin
Clear your caches and reload the admin product page. You should see the position field underneath the image 'type' field.
Change the value in a couple of fields and hit the 'Save Changes' button. If all went well it should have added your new values to the database. Reload the product_images
table and order by the position
field (desc).
Add sorting to Product Entity
To get the product images sorting by position you can add the following method to your Product Entity class:
<?php
namespace namespace App\Entity\Product;
class Product extends BaseProduct implements ProductInterface {
// ...
public function getImages(): Collection
{
/** @var PersistentCollection $collection */
$collection = $this->images;
$values = $collection->getValues();
uasort($values, function($a, $b){
return $a->getPosition() < $b->getPosition() ? -1 : 1;
});
$collection->clear();
foreach ($values as $key => $value) {
$collection->set($key, $value);
}
return $collection;
}
// ...
}
Note: It has been suggested that adding a Doctrine OrderBy
annotation to the Product Entity images
property would handle the sorting (see below). I tried this and it didn't work.
If anyone knows a better way to do this please let me know in the comments section.
<?php
namespace namespace App\Entity\Product;
class Product extends BaseProduct implements ProductInterface {
/**
* @ORM\OrderBy({"position" = "ASC"})
*/
protected $images;
// ...
}
Previous →
Vue Not Updating When Changing Array