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!