smartlabs / sonata-ecommerce
Ecommerce solution for Symfony and Sonata Admin (products, orders, payments, invoices)
Package info
github.com/smartlabsAT/sonata-project-ecommerce
Type:symfony-bundle
pkg:composer/smartlabs/sonata-ecommerce
Requires
- php: ^8.2
- ext-bcmath: *
- cocur/slugify: ^4.0
- doctrine/dbal: ^4.0
- doctrine/doctrine-bundle: ^2.18
- doctrine/orm: ^3.6
- knplabs/knp-menu-bundle: ^3.0
- knplabs/knp-paginator-bundle: ^6.0
- psr/log: ^3.0
- sonata-project/admin-bundle: ^4.42
- sonata-project/block-bundle: ^5.4
- sonata-project/classification-bundle: ^4.0
- sonata-project/doctrine-orm-admin-bundle: ^4.21
- sonata-project/exporter: ^3.0
- sonata-project/form-extensions: ^2.7
- sonata-project/formatter-bundle: ^5.0
- sonata-project/intl-bundle: ^3.0
- sonata-project/twig-extensions: ^2.6
- symfony/config: ^7.4
- symfony/console: ^7.4
- symfony/dependency-injection: ^7.4
- symfony/doctrine-bridge: ^7.4
- symfony/event-dispatcher: ^7.4
- symfony/form: ^7.4
- symfony/framework-bundle: ^7.4
- symfony/http-client: ^7.4
- symfony/http-foundation: ^7.4
- symfony/http-kernel: ^7.4
- symfony/intl: ^7.4
- symfony/options-resolver: ^7.4
- symfony/property-access: ^7.4
- symfony/routing: ^7.4
- symfony/security-core: ^7.4
- symfony/serializer: ^7.4
- symfony/translation: ^7.4
- symfony/twig-bundle: ^7.4
- symfony/validator: ^7.4
- twig/string-extra: ^3.0
- twig/twig: ^3.0
Requires (Dev)
- friendsofsymfony/rest-bundle: ^3.0
- matthiasnoback/symfony-config-test: ^5.2 || ^6.0
- matthiasnoback/symfony-dependency-injection-test: ^5.1 || ^6.0
- phpunit/phpunit: ^11.0
- sonata-project/media-bundle: ^4.0
- sonata-project/seo-bundle: ^3.0
- symfony/phpunit-bridge: ^7.4
- symfony/process: ^7.4
Suggests
- friendsofsymfony/rest-bundle: For REST API endpoints (product, basket, order, customer, invoice APIs)
- nelmio/api-doc-bundle: For API documentation generation
- sonata-project/media-bundle: For product images and galleries
- sonata-project/seo-bundle: For SEO meta tags on product and order pages
- stof/doctrine-extensions-bundle: For Gedmo Sluggable support on Product entities
- symfony/filesystem: For GenerateProductCommand file operations
- symfony/messenger: For async order processing and stock updates after payment
- symfony/process: For Scellius payment shell execution
Conflicts
README
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
- Thomas Rabaix and the Sonata Project contributors for the original ecommerce bundle
- Christopher Schwarz for the port to Symfony 7 / Sonata Admin 4
License
This project is licensed under the MIT License — see the LICENSE file for details.