smartlabs/sonata-ecommerce

Ecommerce solution for Symfony and Sonata Admin (products, orders, payments, invoices)

Maintainers

Package info

github.com/smartlabsAT/sonata-project-ecommerce

Homepage

Type:symfony-bundle

pkg:composer/smartlabs/sonata-ecommerce

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

4.0.1 2026-03-04 08:18 UTC

This package is auto-updated.

Last update: 2026-03-04 08:19:19 UTC


README

Latest Stable Version License

eCommerce solution for Symfony, built on Sonata Admin. Products, categories, cart, checkout, orders, invoices, payments, and delivery — all integrated with the Sonata ecosystem.

Background

This bundle is a maintained port of the archived sonata-project/ecommerce (v3.5.2, July 2022).

We have been using sonata-project/ecommerce in production for years. When it was archived and left behind by the Symfony ecosystem, we decided to port it to current versions and share it with the community. The functionality and architecture are preserved 1:1 — only changes required for compatibility with current dependency versions have been made.

Requirements

  • PHP ^8.2
  • Symfony 7.4.*
  • Sonata Admin ^4.42
  • Doctrine ORM ^3.6 / DBAL ^4.0

Installation

composer require smartlabs/sonata-ecommerce

Register the bundles in config/bundles.php:

return [
    // ... other bundles
    Sonata\ProductBundle\SonataProductBundle::class => ['all' => true],
    Sonata\BasketBundle\SonataBasketBundle::class => ['all' => true],
    Sonata\OrderBundle\SonataOrderBundle::class => ['all' => true],
    Sonata\InvoiceBundle\SonataInvoiceBundle::class => ['all' => true],
    Sonata\CustomerBundle\SonataCustomerBundle::class => ['all' => true],
    Sonata\PaymentBundle\SonataPaymentBundle::class => ['all' => true],
    Sonata\DeliveryBundle\SonataDeliveryBundle::class => ['all' => true],
    Sonata\PriceBundle\SonataPriceBundle::class => ['all' => true],
];

Quick Start

1. Create your entities

The bundle provides abstract Base* entities. Create concrete entities in your application:

// src/Entity/Product.php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Sonata\ProductBundle\Entity\BaseProduct;

#[ORM\Entity]
#[ORM\Table(name: 'commerce_product')]
class Product extends BaseProduct
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    protected ?int $id = null;
}

Repeat for Basket, BasketElement, Order, OrderElement, Invoice, InvoiceElement, Customer, Address, Transaction, Delivery, Package, ProductCategory, ProductCollection.

2. Configure the bundles

Create configuration files under config/packages/ for each sub-bundle (sonata_product.yaml, sonata_basket.yaml, etc.) to map your entity classes.

3. Create the database schema

php bin/console doctrine:schema:update --force

Architecture

Sub-Bundles

Bundle Namespace Purpose
SonataProductBundle Sonata\ProductBundle Products, variants, categories, collections
SonataBasketBundle Sonata\BasketBundle Shopping cart
SonataOrderBundle Sonata\OrderBundle Orders, checkout
SonataInvoiceBundle Sonata\InvoiceBundle Invoices
SonataCustomerBundle Sonata\CustomerBundle Customers, addresses
SonataPaymentBundle Sonata\PaymentBundle Payments, transactions
SonataDeliveryBundle Sonata\DeliveryBundle Delivery methods
SonataPriceBundle Sonata\PriceBundle Price calculation

Shared Component

Sonata\Component contains shared code used across all bundles: interfaces, events, currency handling, payment implementations (PayPal, Ogone, Scellius, Check, Pass, Debug), transformers, and the base entity manager.

Differences from the Original

This bundle is a 1:1 port of the original sonata-project/ecommerce. Only changes required for compatibility with current dependency versions have been made.

Serializer: Symfony Serializer instead of JMS Serializer

The original bundle used JMS Serializer handlers (via Sonata\Form\Serializer\BaseSerializerHandler) to serialize entities to/from their IDs. This base class was deprecated in sonata-project/form-extensions 1.13 and removed in 2.0.

Since sonata-project/form-extensions 2.x no longer provides BaseSerializerHandler and JMS Serializer is not a required dependency, the serializer handlers have been reimplemented as Symfony Serializer normalizers:

Original (JMS Serializer) Ported (Symfony Serializer)
Sonata\Form\Serializer\BaseSerializerHandler Component\Serializer\BaseSerializerNormalizer
BasketBundle\Serializer\BasketSerializerHandler BasketBundle\Serializer\BasketSerializerNormalizer
CustomerBundle\Serializer\CustomerSerializerHandler CustomerBundle\Serializer\CustomerSerializerNormalizer
OrderBundle\Serializer\OrderSerializerHandler OrderBundle\Serializer\OrderSerializerNormalizer
OrderBundle\Serializer\OrderElementSerializerHandler OrderBundle\Serializer\OrderElementSerializerNormalizer
InvoiceBundle\Serializer\InvoiceSerializerHandler InvoiceBundle\Serializer\InvoiceSerializerNormalizer
ProductBundle\Serializer\ProductSerializerHandler ProductBundle\Serializer\ProductSerializerNormalizer

The functionality is identical: entities are serialized to their ID and deserialized back to entity objects via the corresponding manager service.

Service tag: serializer.normalizer (replaces jms_serializer.subscribing_handler)

Doctrine DBAL: Direct Connection Access via EntityManager

The original bundle's manager classes (extending Sonata\Doctrine\Entity\BaseEntityManager) used $this->getConnection() to access the DBAL connection for raw SQL queries.

Since the port uses its own AbstractEntityManager (which does not expose a getConnection() method), affected classes use $this->em->getConnection() instead. This accesses the Doctrine DBAL connection through the injected EntityManagerInterface.

Affected class: ProductBundle\Manager\ProductCategoryManager::getProductCount()

The functionality is identical — only the access path to the DBAL connection differs. The raw SQL query itself has been updated for DBAL 4.x compatibility:

  • $stmt->execute() / $stmt->fetchAll()$connection->executeQuery() / ->fetchAllAssociative()
  • $metadata->table['name']$metadata->getTableName()

Message Queue: Symfony Messenger instead of SonataNotificationBundle

The original bundle used sonata-project/notification-bundle for async order processing after payment (stock updates). SonataNotificationBundle is archived and not compatible with Symfony 7.x.

The consumers have been reimplemented as Symfony Messenger handlers:

Original (SonataNotificationBundle) Ported (Symfony Messenger)
PaymentBundle\Consumer\PaymentProcessOrderConsumer PaymentBundle\MessageHandler\ProcessOrderHandler
PaymentBundle\Consumer\PaymentProcessOrderElementConsumer PaymentBundle\MessageHandler\ProcessOrderElementHandler
sonata_payment_order_process notification type PaymentBundle\Message\ProcessOrderMessage
sonata_payment_order_element_process notification type PaymentBundle\Message\ProcessOrderElementMessage
BackendInterface::createAndPublish() in PaymentHandler MessageBusInterface::dispatch() in PaymentHandler

The business logic is identical: after payment processing, a ProcessOrderMessage is dispatched. The handler loads the order and transaction, then dispatches a ProcessOrderElementMessage for each order element. The element handler decrements product stock when both transaction and order status are VALIDATED.

Optional dependency: symfony/messenger is listed as a suggest dependency. When not installed, the bundle works normally — only async stock updates after payment are disabled. Install it with:

composer require symfony/messenger

To process messages asynchronously, configure a transport in config/packages/messenger.yaml:

framework:
    messenger:
        transports:
            async: '%env(MESSENGER_TRANSPORT_DSN)%'
        routing:
            'Sonata\PaymentBundle\Message\ProcessOrderMessage': async
            'Sonata\PaymentBundle\Message\ProcessOrderElementMessage': async

Credits

License

This project is licensed under the MIT License — see the LICENSE file for details.