
As you should already know, Sylius is constructed from fully decoupled bundles. They are all connected together inside the core, powering a standard webshop application.
Every aspect of our e-commerce platform comes from a standalone component and you can use these components in your own Symfony2 project.
For example, if you have a book catalogue application, you could integrate the cart bundle to introduce shopping feature for the users, based on your existing books collection.
Despite this separation of concerns, functionality like model persistence or CRUD actions is common for all bundles.
Initially, every model had its’ own controller, or even 2 controllers, one for backend and another for frontend.
Additionally, we had 1 manager and 1 manipulator class per entity, which was tremendous amount of duplicated code in every bundle.
I wanted to tackle several problems at once.
To achieve this, I created SyliusResourceBundle.
Use the following command to add the bundle to your composer.json and download the package.
$ composer require sylius/resource-bundle:*
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOSRestBundleFOSRestBundle(),
new JMSSerializerBundleJMSSerializerBundle($this),
new WhiteOctoberPagerfantaBundleWhiteOctoberPagerfantaBundle(),
new SyliusBundleResourceBundleSyliusResourceBundle(),
);
}
# app/config/config.yml
sylius_resource:
resources:
acme.user:
driver: doctrine/orm
templates: AcmeShopBundle:User
classes:
model: AcmeShopBundleEntityUser
With this configuration, the bundle registers several services, useful for managing the User entity.
acme.controller.user service is now available! It is an instance of the generic ResourceController class.
It works on top of the excellent FOSRestBundle.
It comes with a set of very basic, but extremely customizable, CRUD actions.
showAction for displaying a single user. By default it searches by the id, but it can be easily customized.indexAction for retrieving a collection of users. Supports flat and paginated lists, sorting and basic filtering.createAction for creating a new user.updateAction for editing an existing user.deleteAction for deleting an user.Some examples…
To get a single user instance and render AcmeShopBundle:User:show.html.twig template, define the following route.
# app/config/routing.yml
acme_user_show:
pattern: /users/{id}
methods: [GET]
defaults:
_controller: acme.controller.user:showAction
Pretty simple, but let’s display only enabled users, using their username and render a custom template.
# app/config/routing.yml
acme_profile_show:
pattern: /profile/{username}
methods: [GET]
defaults:
_controller: acme.controller.user:showAction
_sylius:
template: AcmeShopBundle:Profile:show.html.twig
criteria: { username: $username, enabled: true }
What if you need to customize the query, add some joins to optimize the performance? Let us use a custom repository action.
# app/config/routing.yml
acme_profile_show:
pattern: /profile/{username}
methods: [GET]
defaults:
_controller: acme.controller.user:showAction
_sylius:
template: AcmeShopBundle:Profile:show.html.twig
method: findOneForProfilePage
arguments: [$username]
Now, let’s assume we want to list all disabled accounts, as a paginated list.
# app/config/routing.yml
acme_user_disabled:
pattern: /users/disabled
methods: [GET]
defaults:
_controller: acme.controller.user:indexAction
_sylius:
template: AcmeShopBundle:User:disabled.html.twig
criteria: { enabled: false }
The bundle uses Pagerfanta library, and passes paginator instance as users variable in the template.
What if you want only five recently registered users, without pagination? indexAction supports flat list as well.
# app/config/routing.yml
acme_user_recently_registered:
pattern: /users/recently-registered
methods: [GET]
defaults:
_controller: acme.controller.user:indexAction
_sylius:
template: AcmeShopBundle:User:recentlyRegistered.html.twig
criteria: { enabled: true }
sorting: { createdAt: desc }
paginate: false
limit: 5
Thanks to the flexibility of Symfony, you can render routes as blocks and embed such list on another page. (example)
Just like for the showAction, you can use custom repository method and arguments.
createAction, updateAction and deleteAction represent similar level of flexibility, you can use custom form types per action and much more.
# app/config/routing.yml
acme_user_update_addresses:
pattern: /users/{id}/update-addresses
methods: [GET]
defaults:
_controller: acme.controller.user:updateAction
_sylius:
template: AcmeShopBundle:User:updateAddresses.html.twig
form: acme_user_addresses # 'acme_user' type is default.
There is a lot of other possibilities, you can find out more by checking out the documentation.
The bundle dispatches a set of very useful events for every action and is format agnostic, thanks to the FOSRestBundle it can also serve json and xml responses.
You can customize the redirection after creating/updating/deleting resources or even add your own actions.
Except the controller, acme.manager.user is registered, but it is only an alias to the real ObjectManager service. (EntityManager for Doctrine ORM, or DocumentManager for MongoDB ODM)
You can safely use it, but it’s not a requirement.
Additionally, the entity repository class is changed to a slightly customized EntityRepository (or DocumentRepository). It is available as acme.repository.user service.
It contains two extra methods, the createNew() and createPaginator() functions.
First returns a new instance of the entity. The second method creates a Pagerfanta instance for specific criteria and sorting.
The controller and repository classes are configurable, you can define them next to the model setting.
# app/config/config.yml
sylius_resource:
resources:
acme.user:
driver: doctrine/orm
templates: AcmeShopBundle:User
classes:
model: AcmeShopBundleEntityUser
repository: AcmeShopBundleEntityUserRepository
controller: AcmeShopBundleControllerUserController
With such changes, the app.repository.user and app.controller.user services will use your own classes.
When implementing this bundle, I was a bit worried that I’m giving too much power to the routing … but I got convinced when I realized how easy it is to manage the models, add new actions and features.
All bundles are using this technique – everything mentioned above is possible in Sylius, which makes the customization process very simple.
The bundle supports Doctrine ORM and Doctrine MongoDB ODM, thanks to user contributions.
Please leave your thoughts in the comments section below. Thank you!