# Sylius

> This skill should be used when the user asks to "create a Sylius plugin", "build a Sylius extension", "Sylius resource model", "Sylius API Platform", "Sylius admin customization", "Sylius payment plugin", "Sylius Twig templates", "Sylius service container", or needs guidance on Sylius plugin development with PHP/Symfony.

- Skill: `biggora/sylius` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add biggora/sylius`
- Raw SKILL.md: https://api.skillmd.com/api/skills/biggora/sylius/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: biggora (https://skillmd.com/u/biggora)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/biggora/sylius

---


# Sylius Plugin Development

## Overview

Sylius is an open-source e-commerce framework built on top of Symfony and Doctrine ORM. Unlike monolithic e-commerce platforms where plugins hook into predefined extension points, Sylius leverages the full power of the Symfony ecosystem -- dependency injection, service decoration, event dispatching, and Twig templating -- to provide a highly customizable commerce engine. Sylius follows a resource-oriented architecture where every domain concept (product, order, customer, payment) is modeled as a "resource" managed by the ResourceBundle, giving plugins a consistent CRUD interface, API exposure, and admin panel integration out of the box.

The plugin system is built around standard Symfony bundles. A Sylius plugin is a Composer package containing a bundle class, service definitions, entity mappings, templates, and optional migrations. The official plugin skeleton (`sylius/plugin-skeleton`) provides a development environment with a fully functional Sylius application for testing. Plugins can extend or replace any part of the system -- add new entities, decorate existing services, override templates, inject form fields, customize the admin grid, and expose REST or GraphQL endpoints via API Platform.

Sylius 1.x targets Symfony 5.4/6.x, while Sylius 2.0 (in development) moves to Symfony 7.x with API Platform 3.x as the primary storefront interface. Modern plugin development should target both Sylius 1.13+ and prepare for Sylius 2.0 compatibility by avoiding deprecated patterns.

## Plugin Structure

Every Sylius plugin is a Symfony bundle distributed as a Composer package. The bundle class registers the plugin with the Symfony kernel, and the `composer.json` declares the package type as `sylius-plugin` for automatic discovery.

### Minimal Directory Layout

```
my-sylius-plugin/
  src/
    MyPlugin.php                  # Bundle class (extends AbstractBundle or Bundle)
    DependencyInjection/
      Configuration.php           # Bundle configuration tree
      MyPluginExtension.php       # Loads services.xml/yaml
    Entity/                       # Doctrine entities
    Repository/                   # Custom repository classes
    Form/
      Extension/                  # Form type extensions
      Type/                       # Custom form types
    Resources/
      config/
        services.xml              # Service definitions
        doctrine/                 # Doctrine mapping files (XML)
        grids/                    # Sylius Grid configuration
        routing.yml               # Route definitions
      templates/                  # Twig templates
      translations/               # Translation files
  tests/
    Behat/                        # Behat feature tests
    PHPUnit/                      # Unit/integration tests
  composer.json
```

### Bundle Class

The bundle class extends `Symfony\Component\HttpKernel\Bundle\AbstractBundle` (Symfony 6.1+) or `Symfony\Component\HttpKernel\Bundle\Bundle`. For plugins that register Doctrine entity mappings, extend `Sylius\Bundle\ResourceBundle\AbstractResourceBundle` and override `getModelNamespace()` and `getModelInterfaces()` to enable entity resolution.

### Composer Package

The `composer.json` must declare `"type": "sylius-plugin"` and require `sylius/sylius` or individual Sylius component packages. Register the bundle class in the `extra.symfony.bundles` section for Symfony Flex auto-configuration.

For complete plugin skeleton setup, entity registration, trait-based customization, and recipe-based installation, see [references/plugin-architecture.md](references/plugin-architecture.md).

## Resource Model

Sylius ResourceBundle provides a uniform pattern for managing domain entities. Every resource (product, order, channel, payment method) is registered with the ResourceBundle, which auto-generates a controller, repository, factory, form type, and routing for CRUD operations.

### Defining a Resource

Register resources in the bundle configuration under the `sylius_resource` key:

```yaml
sylius_resource:
    resources:
        app.bonus_points:
            classes:
                model: App\Entity\BonusPoints
                interface: App\Entity\BonusPointsInterface
                repository: App\Repository\BonusPointsRepository
                factory: App\Factory\BonusPointsFactory
                form:
                    default: App\Form\Type\BonusPointsType
```

This single declaration gives the resource:

- A CRUD controller (`sylius.controller.bonus_points`)
- A repository service (`app.repository.bonus_points`)
- A factory service (`app.factory.bonus_points`)
- Automatic routing for admin CRUD pages (when combined with grid configuration)

### Entity Interfaces and Resolution

Sylius uses Doctrine's ResolveTargetEntityListener to decouple entities from concrete classes. Every entity implements an interface, and the ResourceBundle maps the interface to the concrete class. This allows plugins to swap entity implementations without modifying dependent code. When extending a core entity (e.g., adding fields to `Product`), create a trait with the additional properties and apply it to the project-level entity class.

### Factories

Use Sylius factories (not `new Entity()`) to instantiate resources. Factories ensure that required defaults are set and that the correct class is instantiated (respecting entity resolution). Decorate the factory service to customize default values for new resources.

## Extension Mechanisms

Sylius provides multiple extension points rooted in Symfony's architecture.

### Service Decoration

Override any Sylius service by decorating it in the DI container. Decoration wraps the original service, allowing the plugin to add behavior before or after the original method call:

```yaml
services:
    app.calculator.decorated_shipping:
        class: App\Calculator\DecoratedShippingCalculator
        decorates: sylius.shipping_calculator.flat_rate
        arguments:
            - '@.inner'
```

### Form Extensions

Extend existing form types (product, order, customer) using Symfony form type extensions. This adds fields to admin forms without overriding the entire form class:

```php
class ProductTypeExtension extends AbstractTypeExtension
{
    public static function getExtendedTypes(): iterable
    {
        return [ProductType::class];
    }

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->add('customField', TextType::class);
    }
}
```

### Template Events (Twig Blocks)

Sylius defines template events (also called Sylius template blocks) at strategic points in admin and shop templates. Inject content into these locations by registering blocks in configuration:

```yaml
sylius_ui:
    events:
        sylius.shop.product.show.content:
            blocks:
                bonus_points:
                    template: '@MyPlugin/Shop/Product/_bonusPoints.html.twig'
                    priority: 10
```

### Event Listeners

Listen to Symfony events dispatched by Sylius for lifecycle hooks (pre-create, post-update, pre-delete). ResourceBundle dispatches events following the pattern `sylius.<resource>.<action>` (e.g., `sylius.product.pre_create`).

For complete coverage of Symfony DI patterns, compiler passes, tagged services, grid configuration, and menu builders, see [references/service-container.md](references/service-container.md).

For admin panel customization, Sonata blocks, Twig block overrides, JavaScript widgets, and form themes, see [references/admin-and-twig.md](references/admin-and-twig.md).

## API Platform Integration

Sylius 1.13+ and Sylius 2.0 use API Platform as the primary API layer, replacing the legacy FOSRestBundle-based API. API Platform provides automatic REST and GraphQL endpoints for Sylius resources with serialization, validation, pagination, and filtering.

### Resource Declaration

Expose a Sylius entity through API Platform by adding the `#[ApiResource]` attribute (or XML/YAML configuration):

```php
#[ApiResource(
    operations: [
        new GetCollection(),
        new Get(),
        new Post(),
    ],
    normalizationContext: ['groups' => ['bonus_points:read']],
    denormalizationContext: ['groups' => ['bonus_points:write']],
)]
class BonusPoints implements BonusPointsInterface
{
    #[Groups(['bonus_points:read'])]
    private int $points;
}
```

### Serialization Groups

Control which fields appear in API responses using serialization groups. Sylius defines standard groups (`shop:read`, `admin:read`, `admin:write`) that plugins should follow for consistency.

### Custom Operations and Filters

Add custom API operations for business logic that does not map to standard CRUD. Use API Platform data providers and data persisters to customize how resources are fetched and saved.

For complete API Platform integration patterns including custom resources, filters, data providers, and endpoint customization, see [references/api-platform.md](references/api-platform.md).

## Development Setup

### Plugin Skeleton

The official `sylius/plugin-skeleton` provides a ready-to-use development environment:

```bash
composer create-project sylius/plugin-skeleton my-sylius-plugin
cd my-sylius-plugin
composer install
(cd tests/Application && bin/console sylius:install --no-interaction)
(cd tests/Application && bin/console server:start)
```

The skeleton includes a test Sylius application in `tests/Application/` that loads the plugin bundle. This allows running the full Sylius application with the plugin active for manual testing and Behat acceptance tests.

### Sylius Standard Project

For end-to-end testing against a real Sylius store, install Sylius Standard:

```bash
composer create-project sylius/sylius-standard my-shop
cd my-shop
bin/console sylius:install
symfony serve
```

Require the plugin as a local Composer path repository during development:

```json
{
    "repositories": [
        { "type": "path", "url": "../my-sylius-plugin" }
    ],
    "require": {
        "acme/sylius-bonus-points-plugin": "dev-main"
    }
}
```

### Database and Fixtures

Set up the test database with Sylius fixtures for a realistic development environment:

```bash
bin/console doctrine:database:create --env=dev
bin/console doctrine:schema:create --env=dev
bin/console sylius:fixtures:load default --no-interaction --env=dev
```

The `default` fixture suite creates sample channels, currencies, locales, products, customers, and orders. Create plugin-specific fixture suites for data that the plugin needs during development.

### Debug Tools

Enable Symfony Profiler and Web Debug Toolbar for development. Key debug panels for Sylius plugin development:

- **Doctrine** -- Inspect SQL queries generated by resource operations. Watch for N+1 queries when loading related entities.
- **Events** -- View all dispatched events to verify that plugin listeners trigger correctly.
- **Security** -- Debug authentication and authorization issues for admin and shop contexts.
- **API Platform** -- Inspect API request/response serialization, filters, and pagination.

### Testing Stack

Sylius uses a three-tier testing approach:

1. **Behat + Mink** -- Acceptance tests that drive the browser (or HTTP client) through complete user flows. Sylius provides extensive Behat contexts and page objects for admin and shop interactions.
2. **PHPSpec** -- Unit-level specification tests for individual classes. Sylius core uses PHPSpec for all domain logic.
3. **PHPUnit** -- Integration tests for database queries, service wiring, and API endpoints.

For detailed testing setup, fixtures, and Sylius test helpers, see [references/testing-with-behat.md](references/testing-with-behat.md).

## Anti-Patterns

Avoid these common mistakes in Sylius plugin development:

### Overriding Controllers Directly

Never replace Sylius controllers with custom implementations. Use service decoration or event listeners to modify behavior. Direct controller replacement breaks when Sylius updates the controller signature.

### Bypassing the Resource Layer

Never use `$entityManager->persist()` directly on Sylius resources. Use the resource controller flow or the repository service (`$repository->add($resource)`). Bypassing the resource layer skips events, validation, and authorization checks.

### Hardcoding Entity Classes

Never reference concrete entity classes like `Sylius\Component\Core\Model\Product` directly in service definitions or DQL queries. Use the entity interface (`ProductInterface`) and let the resource bundle resolve the concrete class. Hardcoded classes break when the shop overrides the entity.

### Copying Templates Instead of Extending

Never copy entire Sylius templates into the plugin to make small modifications. Use template events (Twig blocks) to inject content at specific points, or override only the specific block within a template. Copying templates creates maintenance burden when Sylius updates its templates.

### Ignoring Channel Awareness

Sylius is multi-channel by design. Every resource query in shop context must be scoped to the current channel. Forgetting channel filtering exposes data from other channels and breaks multi-store setups.

### Modifying Core Migrations

Never alter Sylius core migration files. Create separate plugin migrations for schema changes. Use Doctrine migration diffing against the plugin's entity mappings.

### Tight Coupling to Sylius Internals

Avoid depending on Sylius internal classes (marked `@internal` or in `Internal` namespaces). These classes change between minor versions without notice. Depend only on public interfaces and services documented in the Sylius API.

### Missing Translation Keys

Always provide translation files for every UI string. Hardcoded English strings in templates break multi-language stores. Use the `trans` Twig filter and Symfony translation component for all user-facing text. Provide at least English translations; document translation keys so shop owners can add their own translations.

### Ignoring State Machine Transitions

Sylius uses the Winzou State Machine (Sylius 1.x) or Symfony Workflow (Sylius 2.0) for order, payment, and shipment lifecycle management. Never change entity statuses directly (e.g., `$order->setState('completed')`). Always apply state machine transitions through the state machine service so that guards, callbacks, and event listeners execute correctly.

## Reference Files

- [Plugin Architecture](references/plugin-architecture.md) -- Plugin skeleton, bundle structure, configuration, resource registration, entity extension, trait-based customization, recipe-based installation
- [Service Container](references/service-container.md) -- Symfony DI in Sylius, service decoration, compiler passes, tagged services, form extensions, grid configuration, menu builders
- [API Platform](references/api-platform.md) -- API Platform integration, custom resources, serialization groups, operations, filters, data providers, custom endpoints
- [Admin and Twig](references/admin-and-twig.md) -- Admin panel customization, Sonata blocks, template events, Twig block overrides, JavaScript widgets, grid customization, form themes
- [Testing with Behat](references/testing-with-behat.md) -- Behat/Mink for acceptance testing, PHPSpec for unit testing, PHPUnit integration, test fixtures, Sylius test helpers

