Розробка інтернет-магазину на Sylius
Sylius — PHP e-commerce фреймворк на базі Symfony. Це не CMS з прикладеною кошиком: архітектура спроектована для складних B2C та B2B сценаріїв. Компонентна структура дозволяє використовувати окремі частини (Inventory, Pricing, Promotions) в існуючих Symfony-додатках без встановлення повного стеку.
Позиціонування відносно конкурентів
Sylius виграє коли:
- Команда працює на PHP/Symfony
- Потрібна гнучкість без SaaS-обмежень (commercetools, Shopify)
- Потрібна мультиканальність та мультивалютність з коробки
- Важливий режим headless через API Platform (JSON-LD / HAL / JSON:API)
- Потрібні кастомні робочі процеси через Symfony State Machine
Архітектура: Resource Layer
Ключова особливість Sylius — Resource System: всі сутності (Product, Order, Customer) управляються через єдиний механізм конфігурації ресурсів. Це спрощує переопределення моделі та додавання кастомних полів.
# config/packages/sylius_product.yaml
sylius_product:
resources:
product:
classes:
model: App\Entity\Product\Product # ваш entity, розширює Sylius\Product
translation:
classes:
model: App\Entity\Product\ProductTranslation
Sylius автоматично оновлює репозиторії, фабрики та форми для кастомного класу.
Канали: мультисайтовість
Канал у Sylius — це точка продажу з окремим каталогом, цінами, валютою та доменом:
// src/DataFixtures/ChannelFixture.php
$channel = $this->channelFactory->createNew();
$channel->setCode('WEB_UA');
$channel->setName('Інтернет-магазин Україна');
$channel->setHostname('myshop.ua');
$channel->setDefaultLocale($this->localeRepository->findOneBy(['code' => 'uk_UA']));
$channel->addLocale($this->localeRepository->findOneBy(['code' => 'en_US']));
$channel->setBaseCurrency($this->currencyRepository->findOneBy(['code' => 'UAH']));
$channel->setTaxCalculationStrategy('order_items_based');
$channel->setContactEmail('[email protected]');
$channel->setSkippingShippingStepAllowed(false);
$channel->setSkippingPaymentStepAllowed(false);
$this->channelRepository->add($channel);
Один екземпляр Sylius обслуговує кілька каналів. Кожен канал бачить тільки призначені йому товари та має власний прайс-лист.
Розширення моделі Product
// 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; }
// геттери/сеттери для інших полів
}
bin/console doctrine:migrations:diff
bin/console doctrine:migrations:migrate
Ціноутворення через Price Group
Sylius підтримує Catalog Promotions (постійні скидки на групи товарів) та Cart Promotions (промокоди, правила):
// Catalog Promotion: скидка 20% на бренд Nike
$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 у Sylius — State Machine з явними переходами. Стандартні стани: 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();
// Перевірка наявності товарів перед завершенням
foreach ($order->getItems() as $item) {
$variant = $item->getVariant();
if (!$this->inventoryChecker->isReserved($variant, $item->getQuantity())) {
throw new \RuntimeException(
sprintf('Товар "%s" закінчився', $variant->getName())
);
}
}
}
}
# config/services.yaml
App\StateMachine\CustomOrderCheckoutListener:
tags:
- { name: kernel.event_listener, event: sylius.order.pre_complete, method: preComplete }
API Platform: Режим Headless
Sylius 2.0 інтегрований з API Platform. Кожен ресурс доступен через REST та 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
Аутентифікація через 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: отримати JWT та використовувати
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();
// Наступні запити
const products = await fetch('/api/v2/shop/products?channel=WEB_UA&locale=uk_UA', {
headers: { Authorization: `Bearer ${token}` },
});
Розгортання
# 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]
Етапи розробки
| Етап | Строк |
|---|---|
| Встановлення, Docker, конфігурація | 2–3 дні |
| Налаштування каналів, валют, локалей | 1–2 дні |
| Кастомні Entity + міграції | 2–4 дні |
| Імпорт каталогу | 4–8 днів |
| Бізнес-логіка (промоції, доставка, податки) | 5–10 днів |
| Headless фронтенд (Next.js + API Platform) | 12–20 днів |
| Платіжні інтеграції | 3–5 днів |
| Разом | 29–52 дні |







