E-Commerce Store Development on Sylius

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Showing 1 of 1 servicesAll 2065 services
E-Commerce Store Development on Sylius
Complex
from 2 weeks to 3 months
FAQ
Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Developing an Online Store on Sylius

Sylius is a PHP e-commerce framework based on Symfony. It's not a CMS with a shopping cart bolted on: the architecture is designed for complex B2C and B2B scenarios. The component structure allows using separate parts (Inventory, Pricing, Promotions) in existing Symfony applications without installing the full stack.

Positioning Against Competitors

Sylius wins when:

  • Team works with PHP/Symfony
  • Flexibility is needed without SaaS limitations (commercetools, Shopify)
  • Multi-channel and multi-currency out of the box are required
  • Headless mode via API Platform (JSON-LD / HAL / JSON:API) is important
  • Custom workflows through Symfony State Machine are needed

Architecture: Resource Layer

Key Sylius feature — Resource System: all entities (Product, Order, Customer) are managed through a single resource configuration mechanism. This simplifies overriding models and adding custom fields.

# config/packages/sylius_product.yaml
sylius_product:
  resources:
    product:
      classes:
        model: App\Entity\Product\Product    # your entity, extends Sylius\Product
      translation:
        classes:
          model: App\Entity\Product\ProductTranslation

Sylius automatically updates repositories, factories, and forms for the custom class.

Channels: Multi-Site Support

A channel in Sylius is a sales point with a separate catalog, prices, currency, and domain:

// src/DataFixtures/ChannelFixture.php
$channel = $this->channelFactory->createNew();
$channel->setCode('WEB_EN');
$channel->setName('Online Store English');
$channel->setHostname('myshop.com');
$channel->setDefaultLocale($this->localeRepository->findOneBy(['code' => 'en_US']));
$channel->addLocale($this->localeRepository->findOneBy(['code' => 'fr_FR']));
$channel->setBaseCurrency($this->currencyRepository->findOneBy(['code' => 'USD']));
$channel->setTaxCalculationStrategy('order_items_based');
$channel->setContactEmail('[email protected]');
$channel->setSkippingShippingStepAllowed(false);
$channel->setSkippingPaymentStepAllowed(false);

$this->channelRepository->add($channel);

One Sylius instance serves multiple channels. Each channel sees only assigned products and has its own price list.

Extending Product Model

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

use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Core\Model\Product as BaseProduct;

#[ORM\Entity]
#[ORM\Table(name: 'sylius_product')]
class Product extends BaseProduct
{
    #[ORM\Column(type: 'string', nullable: true)]
    private ?string $sku = null;

    #[ORM\Column(type: 'integer', nullable: true)]
    private ?int $weight = null;

    #[ORM\Column(type: 'boolean', options: ['default' => false])]
    private bool $isBulkAvailable = false;

    #[ORM\Column(type: 'decimal', precision: 10, scale: 2, nullable: true)]
    private ?string $bulkMinOrderAmount = null;

    public function getSku(): ?string { return $this->sku; }
    public function setSku(?string $sku): void { $this->sku = $sku; }

    // getters/setters for other fields
}
bin/console doctrine:migrations:diff
bin/console doctrine:migrations:migrate

Pricing via Price Group

Sylius supports Catalog Promotions (permanent discounts on product groups) and Cart Promotions (promo codes, rules):

// Catalog Promotion: 20% off Nike brand
$catalogPromotion = $this->factory->createNew();
$catalogPromotion->setCode('NIKE_20_OFF');
$catalogPromotion->setName('Nike -20%');
$catalogPromotion->addChannel($niceChannel);

$scope = $this->catalogPromotionScopeFactory->createNew();
$scope->setType(InForProductScopeVariantChecker::TYPE);
$scope->setConfiguration([
    'products' => $nikeProductCodes,
]);
$catalogPromotion->addScope($scope);

$action = $this->catalogPromotionActionFactory->createNew();
$action->setType(PercentageDiscountPriceCalculator::TYPE);
$action->setConfiguration(['amount' => 0.20]);
$catalogPromotion->addAction($action);

Checkout: State Machine

Checkout in Sylius is State Machine with explicit transitions. Standard states: cart → addressed → shipping_selected → payment_selected → completed.

// src/StateMachine/CustomOrderCheckoutListener.php
class CustomOrderCheckoutListener
{
    public function preComplete(GenericEvent $event): void
    {
        /** @var OrderInterface $order */
        $order = $event->getSubject();

        // Check product availability before completion
        foreach ($order->getItems() as $item) {
            $variant = $item->getVariant();
            if (!$this->inventoryChecker->isReserved($variant, $item->getQuantity())) {
                throw new \RuntimeException(
                    sprintf('Product "%s" is out of stock', $variant->getName())
                );
            }
        }
    }
}
# config/services.yaml
App\StateMachine\CustomOrderCheckoutListener:
    tags:
        - { name: kernel.event_listener, event: sylius.order.pre_complete, method: preComplete }

API Platform: Headless Mode

Sylius 2.0 is integrated with API Platform. Each resource is available via REST and GraphQL:

# GET /api/v2/shop/products
# GET /api/v2/shop/products/my-product-slug
# POST /api/v2/shop/orders
# PATCH /api/v2/shop/orders/TOKEN/items

Authentication via JWT:

# config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
    secret_key: '%kernel.project_dir%/config/jwt/private.pem'
    public_key: '%kernel.project_dir%/config/jwt/public.pem'
    pass_phrase: '%env(JWT_PASSPHRASE)%'
    token_ttl: 3600
// Frontend: get JWT and use
const auth = await fetch('/api/v2/shop/customers/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email, password }),
});
const { token } = await auth.json();

// Subsequent requests
const products = await fetch('/api/v2/shop/products?channel=WEB_EN&locale=en_US', {
  headers: { Authorization: `Bearer ${token}` },
});

Deployment

# docker-compose.yml (prod)
services:
  app:
    image: myshop:latest
    environment:
      APP_ENV: prod
      DATABASE_URL: "postgresql://sylius:pass@postgres/sylius"
      MAILER_DSN: "smtp://user:[email protected]:587"
    depends_on: [postgres, redis]

  worker:
    image: myshop:latest
    command: php bin/console messenger:consume async --limit=100
    depends_on: [postgres, redis]

Development Stages

Stage Timeline
Installation, Docker, configuration 2–3 days
Setting up channels, currencies, locales 1–2 days
Custom Entity + migrations 2–4 days
Catalog import 4–8 days
Business logic (promotions, shipping, taxes) 5–10 days
Headless frontend (Next.js + API Platform) 12–20 days
Payment integrations 3–5 days
Total 29–52 days