-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
Description
We need to use the new routing system using new metadata with operations instead of the legacy ResourceController.
The new "MainController" has 60 lines of code (580 for the ResourceController). It's based on the same technical architecture as API-Platform provider/processor system.
This new approach brings the routing logic closer to the resource definitions (similar to API Platform), eliminates boilerplate, and supports both RAD and advanced DDD usage patterns. It also removes the coupled legacy ResourceController pattern which has been hard to extend and understand for many contributors.
Before
The legacy ResourceController
After
final class MainController
{
public function __construct(
private HttpOperationInitiatorInterface $operationInitiator,
private RequestContextInitiatorInterface $requestContextInitiator,
private ProviderInterface $provider,
private ProcessorInterface $processor,
) {
}
public function __invoke(Request $request): Response
{
$operation = $this->operationInitiator->initializeOperation($request);
if (null === $operation) {
throw new RuntimeException('Operation should not be null.');
}
$context = $this->requestContextInitiator->initializeContext($request);
if (null === $operation->canWrite()) {
$operation = $operation->withWrite(!$request->isMethodSafe());
}
$data = $this->provider->provide($operation, $context);
$valid = $request->attributes->getBoolean('is_valid', true);
if (!$valid) {
$operation = $operation->withWrite(false);
}
return $this->processor->process($data, $operation, $context);
}
}With this change, routing definitions become fully driven by metadata and operations — removing the need for Symfony routing files and the legacy ResourceController. This reduces duplication, standardizes route behavior, and opens the path to more flexible, decoupled resources across both Admin and Shop interfaces.
| Before | After |
|---|---|
| Legacy routing via ResourceController (~580 LOC) | Metadata-driven routing via operations (~60 LOC) |
| Duplication and generated controllers | Unified system like API Platform |
| Coupled to Doctrine entities | Works with any PHP classes |
Why this change matters
The legacy ResourceController adds hundreds of lines of duplicate routing logic and diverges from the resource-metadata driven model that SyliusResourceBundle now provides. Moving to a unified routing system reduces complexity, improves maintainability, and allows us to leverage the full expressiveness of operation metadata (like API-Platform).
Bc-layer
We try to provide the best bc-layer as possible that will allow you to migrate slowly and prepare the moment when the ResourceController will be removed in the future (Sylius 3.x probably).
bc-layer example
sylius_core:
routing:
bc_layer:
enabled: true
routes:
admin_admin_user: false
admin_catalog_promotion: false
admin_channel: false
admin_product: false
shop_product_review: falseThe bc_layer allows selectively disabling legacy routes, letting developers migrate incrementally. Setting a route to false disables its legacy variant while keeping the new metadata-driven routes active.
Routing converter
We are trying to provide a best-effort tool to convert your existing custom routes and Sylius built-in routes' customization.
Convert this legacy routing with YAML configuration
sylius_admin_product:
resource: |
alias: sylius.product
section: admin
templates: "@SyliusAdmin\\shared\\crud"
redirect: update
grid: sylius_admin_product
form:
type: Sylius\Bundle\AdminBundle\Form\Type\ProductType
permission: true
type: sylius.resourceWith the brand new routing configuration
declare(strict_types=1);
use Sylius\Bundle\AdminBundle\Form\Type\ProductType;
use Sylius\Resource\Metadata\BulkDelete;
use Sylius\Resource\Metadata\Create;
use Sylius\Resource\Metadata\Delete;
use Sylius\Resource\Metadata\Index;
use Sylius\Resource\Metadata\Operations;
use Sylius\Resource\Metadata\ResourceMetadata;
use Sylius\Resource\Metadata\Show;
use Sylius\Resource\Metadata\Update;
return (new ResourceMetadata())
->withClass('%sylius.model.product.class%')
->withRoutePrefix('/admin')
->withSection('admin')
->withTemplatesDir('@SyliusAdmin/shared/crud')
->withFormType(ProductType::class)
->withOperations(new Operations([
new Create(
redirectToRoute: 'sylius_admin_product_update',
),
new Update(
redirectToRoute: 'sylius_admin_product_update',
),
new Delete(),
new BulkDelete(),
new Index(
grid: 'sylius_admin_product',
),
new Show(),
]))
;DDD support
The new routing system is more powerful and not coupled to Doctrine entities.
You can use it with any php classes.
<?php
declare(strict_types=1);
namespace App\BookStore\Infrastructure\Sylius\Resource;
use ApiPlatform\Metadata\ApiProperty;
use App\BookStore\Domain\Model\Book;
use App\BookStore\Infrastructure\Sylius\Grid\BookGrid;
use App\BookStore\Infrastructure\Sylius\State\Processor\CreateBookProcessor;
use App\BookStore\Infrastructure\Sylius\State\Processor\DeleteBookProcessor;
use App\BookStore\Infrastructure\Sylius\State\Processor\UpdateBookProcessor;
use App\BookStore\Infrastructure\Sylius\State\Provider\BookBulkItemsProvider;
use App\BookStore\Infrastructure\Sylius\State\Provider\BookItemProvider;
use App\BookStore\Infrastructure\Symfony\Form\BookResourceType;
use Sylius\Resource\Metadata\AsResource;
use Sylius\Resource\Metadata\BulkDelete;
use Sylius\Resource\Metadata\Create;
use Sylius\Resource\Metadata\Delete;
use Sylius\Resource\Metadata\Index;
use Sylius\Resource\Metadata\Update;
use Sylius\Resource\Model\ResourceInterface;
use Symfony\Component\Uid\AbstractUid;
use Symfony\Component\Validator\Constraints as Assert;
#[AsResource(
section: 'admin',
formType: BookResourceType::class,
templatesDir: '@SyliusAdminUi/crud',
routePrefix: '/admin',
driver: false,
operations: [
new Create(
processor: CreateBookProcessor::class,
),
new Update(
provider: BookItemProvider::class,
processor: UpdateBookProcessor::class,
),
new Delete(
provider: BookItemProvider::class,
processor: DeleteBookProcessor::classRelat,
),
new BulkDelete(
provider: BookBulkItemsProvider::class,
processor: DeleteBookProcessor::class,
),
new Index(
grid: BookGrid::class,
),
],
)]
final class BookResource implements ResourceInterface
{
public function __construct(
#[ApiProperty(identifier: true, readable: false, writable: false)]
public ?AbstractUid $id = null,
#[Assert\NotNull(groups: ['create'])]
#[Assert\Length(min: 1, max: 255, groups: ['create', 'Default'])]
public ?string $name = null,
#[Assert\NotNull(groups: ['create'])]
#[Assert\Length(min: 1, max: 1023, groups: ['create', 'Default'])]
public ?string $description = null,
#[Assert\NotNull(groups: ['create'])]
#[Assert\Length(min: 1, max: 255, groups: ['create', 'Default'])]
public ?string $author = null,
#[Assert\NotNull(groups: ['create'])]
#[Assert\Length(min: 1, max: 65535, groups: ['create', 'Default'])]
public ?string $content = null,
#[Assert\NotNull(groups: ['create'])]
#[Assert\PositiveOrZero(groups: ['create', 'Default'])]
public ?int $price = null,
) {
}
public function getId(): ?AbstractUid
{
return $this->id;
}
public static function fromModel(Book $book): self
{
return new self(
$book->id()->value,
$book->name()->value,
$book->description()->value,
$book->author()->value,
$book->content()->value,
$book->price()->amount,
);
}
}This example shows defining the resource entirely via attributes and operations — without relying on Doctrine — enabling DDD-friendly APIs and custom workflows out of the box.
Documentation
See also the Resource Bundle documentation for more details on metadata and operations: https://docs.sylius.com/sylius-stack/resource/index
Main PR
Related PRs on Resource package
- feat(metadata) Customize Resource & operations SyliusResourceBundle#1055
- feat(metadata): Add route priorities SyliusResourceBundle#1147
- fix(provider) Fix parsing nested repository arguments SyliusResourceBundle#1149
Related PRs
- [New routing] Bump resource package #18801
- [New routing] Adjust bulk delete route paths #18835
- [New routing] Uniformize shop resources #18863
ADMIN
- Add bc layer for legacy routing system #18214
- [New routing] Admin product crud #18215
- [New routing] Create simple product and generate variants #18230
- [New routing] Admin administrators crud #18231
- [New routing] Admin catalog promotions crud #18232
- [New routing] Admin channels crud #18234
- [New routing] Admin countries crud #18235
- [New routing] Fix condition in product routes #18243
- [New routing] Admin currencies crud #18236
- [New routing] Admin customers crud #18239
- [New routing] Improve routing bc-layer DX #18246
- [New routing] Admin customer groups crud #18240
- [New routing] Admin exchange rates crud #18241
- [New routing] Admin inventory #18242
- [New routing] Admin locales crud #18245
- [New routing] Admin orders crud #18247
- [New routing] Admin order payments crud #18249
- [New routing] Admin order shipments crud #18250
- [New routing] Admin payments crud #18252
- [New routing] Admin payment methods crud #18253
- [New routing] Admin payment requests crud #18254
- [New routing] Admin product association types crud #18255
- [New routing] Admin product attributes crud #18256
- [New routing] Admin product options crud #18258
- [New routing] Admin product reviews crud #18259
- [New routing] Admin product taxa crud #18261
- [New routing] Admin product variants crud #18264
- [New routing] Admin promotions crud #18265
- [New routing] Admin promotion coupons crud #18267
- [New routing] Admin shipments crud #18271
- [New routing] Admin shipping categories crud #18275
- [New routing] Admin shipping methods crud #18277
- [New routing] Admin shop users crud #18278
- [New routing] Admin tax categories crud #18280
- [New routing] Admin tax rates crud #18281
- [New routing] Admin taxa crud #18283
- [New routing] Admin zones crud #18284
- [New routing][Admin] Configure imports path #18520
- [New routing] Prefix expressions #18719
Missing route conversions:
- sylius_admin_ajax_product_taxons_update_position Refactoring update product taxon positions #18864
- sylius_admin_ajax_product_variants_update_position [New routing] Refactoring update product variant positions #18882
- sylius_admin_catalog_promotion_product_variant_index [New routing] Convert sylius_admin_catalog_promotion_product_variant_… #18746
- sylius_admin_customer_order_index [New routing] Convert sylius_admin_customer_order_index route #18744
- sylius_admin_channel_pricing_log_entry_index [New Routing] Migrate channel pricing log entry routing #18743
- sylius_admin_product_taxon_bulk_delete_products [New routing] Delete multiple products from a taxon #18757
- sylius_admin_promotion_coupon_generate [New routing] Generate promotion coupons #18893
SHOP
- [New routing] Shop product review crud #18292
- Refactoring order thank you controller #18782
- Refactoring cart summary controller #18783
- [New routing] Shop account order crud #18790
- [New routing] Shop checkout #18791
- [New routing] Shop product #18792
- [New routing] Shop account address book #18793
- [New routing] Shop account #18823
- [New routing] Shop order #18830
- [New routing] Shop register #18852
- [New routing] Refactoring cart checkout #18865
- [New routing] Reset password #18869
- [New routing] Request reset password token #18874
- [New routing] Refactoring account change password #18877
- [New routing] Refactoring verify shop user #18879
- [New routing] Refactoring shop user verification token #18886
Shop routes:
- sylius_shop_homepage
- sylius_shop_live_component
- sylius_shop_login
- sylius_shop_login_check
- sylius_shop_json_login_check
- sylius_shop_logout
- sylius_shop_register [New routing] Shop register #18852
- sylius_shop_register_after_checkout [New routing] Shop register #18852
- sylius_shop_register_thank_you
- sylius_shop_request_password_reset_token [New routing] Request reset password token #18874
- sylius_shop_password_reset [New routing] Reset password #18869
- sylius_shop_user_request_verification_token [New routing] Refactoring shop user verification token #18886
- sylius_shop_user_verification [New routing] Refactoring verify shop user #18879
- sylius_shop_product_show [New routing] Shop product #18792
- sylius_shop_product_index [New routing] Shop product #18792
- sylius_shop_product_review_index [New routing] Shop product review crud #18292
- sylius_shop_product_review_create [New routing] Shop product review crud #18292
- sylius_shop_cart_summary Refactoring cart summary controller #18783
- sylius_shop_cart_checkout [New routing] Refactoring cart checkout #18865
- sylius_shop_checkout_start
- sylius_shop_checkout_address [New routing] Shop checkout #18791
- sylius_shop_checkout_select_shipping [New routing] Shop checkout #18791
- sylius_shop_checkout_select_payment [New routing] Shop checkout #18791
- sylius_shop_checkout_complete [New routing] Shop checkout #18791
- sylius_shop_contact_request
- sylius_shop_order_thank_you Refactoring order thank you controller #18782
- sylius_shop_order_pay
⚠️ need to refactor the interface to remove RequestConfiguration - sylius_shop_order_after_pay
⚠️ need to refactor the interface to remove RequestConfiguration - sylius_shop_order_show [New routing] Shop order #18830
- sylius_shop_payment_request_pay
⚠️ need to refactor the interface to remove RequestConfiguration - sylius_shop_account_live_component
- sylius_shop_account_order_index [New routing] Shop account order crud #18790
- sylius_shop_account_order_show [New routing] Shop account order crud #18790
- sylius_shop_account_address_book_index [New routing] Shop account address book #18793
- sylius_shop_account_address_book_create [New routing] Shop account address book #18793
- sylius_shop_account_address_book_update [New routing] Shop account address book #18793
- sylius_shop_account_address_book_delete [New routing] Shop account address book #18793
- sylius_shop_account_address_book_set_as_default [New routing] Shop account address book #18793
- sylius_shop_account_root
- sylius_shop_account_dashboard [New routing] Shop account #18823
- sylius_shop_account_profile_update [New routing] Shop account #18823
- sylius_shop_account_change_password [New routing] Refactoring account change password #18877
- sylius_shop_switch_currency
- sylius_shop_switch_locale
- sylius_shop_default_locale
- sylius_shop_request_password_reset_token_redirect
BC_LAYER
The PoC was here
#17695