Anda di halaman 1dari 361

Sylius

Release

November 18, 2015

Contents

The Book
1.1 The Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3
3

The API Guide


2.1 The API Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

33
33

The User Guide


3.1 The User Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

83
83

Cookbook
4.1 Cookbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

85
85

Integrations
5.1 Integrations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89
89

Migration Guide
6.1 The Migration Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

91
91

Bundles
7.1 Symfony2 Ecommerce Bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

93
93

Components
239
8.1 PHP E-Commerce Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239

Contributing
331
9.1 Contributing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331

ii

Sylius, Release

Sylius is a modern e-commerce solution for PHP, based on Symfony2 Framework.


Note: This documentation assumes you have a working knowledge of the Symfony2 Framework. If you havent,
please start by reading the Quick Tour from the Symfony documentation.

Contents

Sylius, Release

Contents

CHAPTER 1

The Book

The Developers guide to leveraging the flexibility of Sylius.

1.1 The Book


1.1.1 Introduction to Sylius
Sylius is a game-changing e-commerce solution for PHP, based on the Symfony2 framework.
Philosophy
Sylius is completely open source (MIT license) and free, maintained by diverse and creative community of developers
and companies.
What are our core values and what makes us different from other solutions?
Components based approach
Unlimited flexibility and simple customization
Developer-friendly, using latest technologies
Developed using best practices and BDD approach
Highest quality of code
And much more, but we will let you discover it yourself.
The Three Natures of Sylius
Sylius is constructed from fully decoupled and flexible e-commerce components for PHP. It is also a set of Symfony2
bundles, which integrate the components into the full-stack framework. On top of that, Sylius is also a complete
e-commerce platform crafted from all these building blocks.
It is your choice how to use Sylius, you can benefit from the components with any framework, integrate selected
bundles into existing or new Symfony2 app or built your application on top of Sylius platform.

Sylius, Release

Sylius Platform
This book is about our full-stack e-commerce platform, which is a standard Symfony application providing the most
common webshop and a foundation for custom systems.
Leveraging Symfony2 Bundles
If you prefer to build your very custom system step by step and from scratch, you can integrate the standalone Symfony2 bundles. For the installation instructions, please refer to the appropriate bundle documentation.
E-Commerce Components for PHP
If you use a different framework than Symfony, you are welcome to use Sylius components, which will make it much
easier to implement a webshop with any PHP application and project. They provide you with default models, services
and logic for all aspects of e-commerce, completely separated and ready to use.
Final Thoughts
Depending on how you want to use Sylius, continue reading The Book, which covers the usage of the full stack
solution, browse the Bundles Reference or learn about The Components.

1.1.2 Understanding Environments


Every Sylius application is the combination of code and a set of configuration that dictates how that code should
function. The configuration may define the database being used, whether or not something should be cached, or how
verbose logging should be. In Symfony, the idea of environments is the idea that the same codebase can be run using
multiple different configurations. For example, the dev environment should use configuration that makes development
easy and friendly, while the prod environment should use a set of configuration optimized for speed.
Development
Development environment or dev, as the name suggests, should be used for development purposes. It is much slower
than production, because it uses much less aggressive caching and does a lot of processing on every request. However,
it allows you to add new features or fix bugs quickly, without worrying about clearing the cache after every change.
Sylius console runs in dev environment by default. You can access the website in dev mode via the /app_dev.php
file in the web/ directory. (under your website root)
Production
Production environment or prod is your live website environment. It uses proper caching and is much faster than
other environments. It uses live APIs and sends out all e-mails.
To run Sylius console in prod environment, add the following parameters to every command call:
$ app/console --env=prod --no-debug cache:clear

You can access the website in production mode via the /app.php file in your website root (web/) or just / path.
(on Apache)

Chapter 1. The Book

Sylius, Release

Staging
Staging environment or staging should be your before-production environment. It is very similar to the production
env, except that it redirects e-mails to your configured address and uses test APIs (payment etc.) wherever possible.
To run Sylius console in staging environment, add the following parameters to every command call:
$ app/console --env=staging --no-debug cache:clear

You can access the website in staging mode via the /app_staging.php file in the web/ directory. (under your
website root)
Test
Test environment or test is used for automated testing. Most of the time you will not access it directly.
To run Sylius console in test environment, add the following parameters to every command call:
$ app/console --env=test cache:clear

Final Thoughts
You can read more about Symfony environments in this cookbook article.

1.1.3 Installation
The Sylius main application can serve as end-user app, as well as a foundation for your custom e-commerce application.
This article assumes youre familiar with Composer, a dependency manager for PHP. It also assumes you have Composer installed globally.
Note: If you downloaded the Composer phar archive, you should use php composer.phar where this guide uses
composer.
It can be installed using two different approaches, depending on your use case.
Install to Contribute
To install Sylius main application from our main repository and contribute, run the following command:
$ composer create-project -s dev sylius/sylius

This will create a new sylius project in sylius. When all the dependencies are installed, youll be asked to fill the
parameters.yml file via interactive script. Please follow the steps. After everything is in place, run the following
commands:
# Move to the newly created directory
$ cd sylius
$ php app/console sylius:install

The sylius:install command actually runs several other commands, which will ask you some questions and
check if everything is setup to run Sylius properly.

1.1. The Book

Sylius, Release

This package contains our main Sylius development repository, with all the components and bundles in the src/
folder.
For the contributing process questions, please refer to the Contributing Guide.
Bootstrap A New Sylius Project
To create a new project using Sylius Standard Edition, run this command:
$ composer create-project -s dev sylius/sylius-standard acme

This will create a new Symfony project in acme directory. When all the dependencies are installed, youll be asked
to fill the parameters.yml file via interactive script. Please follow the steps. After everything is in place, run the
following commands:
# Move to the newly created directory
$ cd acme
$ php app/console sylius:install

This package has the whole sylius/sylius package in vendors, so you can easily updated it and focus on your
custom development.
Accessing the Shop
In order to see the shop, access the web/app_dev.php file via your web browser.
Tip:
If you use PHP 5.4 or higher, you can also use the build-in webserver for Symfony.
app/console server:run command and then access http://localhost:8000.

Run the php

1.1.4 Architecture Overview


Before we dive separately into every Sylius concept, you need to have an overview of how our main application is
structured. You already know that Sylius is built from components and Symfony2 bundles, which are integration layers
with the framework.
All bundles share the conventions for naming things and the same way of data persistence. Sylius, by default, uses
Doctrine ORM for managing all entities.
For deeper understanding of how Doctrine works, please refer to the excellent documentation on their official website.
Resource Layer
We created an abstraction on top of Doctrine, in order to have a consistent and flexible way to manage all the resources.
By resource we understand every model in the application. Simplest examples of Sylius resources are product,
order, tax_category, promotion, user, shipping_method and so on...
Sylius resource management system lives in the SyliusResourceBundle and can be used in any Symfony2 project.
Services

For every resource you have three very important services available:
Manager

Chapter 1. The Book

Sylius, Release

Repository
Controller
Let us take the product resource as an example.
By default, It is represented
Sylius\Component\Core\Model\Product class and implement proper ProductInterface.

by

Manager The manager service is just an alias to appropriate Doctrines ObjectManager and can be accessed via the
sylius.manager.product id. API is exactly the same and you are probably already familiar with it:
<?php
public function myAction()
{
$manager = $this->container->get('sylius.manager.product');
$manager->persist($product1);
$manager->remove($product2);
$manager->flush(); // Save changes in database.
}

Repository Repository is defined as a service for every resource and shares the API with standard Doctrine ObjectRepository. It contains two additional methods for creating a new object instance and a paginator provider.
The repository service is available via the sylius.repository.product id and can be used like all the repositories you
have seen before.
<?php
public function myAction()
{
$repository = $this->container->get('sylius.repository.product');

$product = $repository->find(4); // Get product with id 4, returns null if not found.


$product = $repository->findOneBy(array('slug' => 'my-super-product')); // Get one product by def
$products = $repository->findAll(); // Load all the products!
$products = $repository->findBy(array('special' => true)); // Find products matching some custom
}

Every Sylius repository supports paginating resources. To create a Pagerfanta instance use the createPaginator
method.
<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.product');
$products = $repository->createPaginator();
$products->setMaxPerPage(3);
$products->setCurrentPage($request->query->get('page', 1));

// Now you can return products to template and iterate over it to get products from current page.
}

Paginator can be created for a specific criteria and with desired sorting.

1.1. The Book

Sylius, Release

<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.product');
$products = $repository->createPaginator(array('foo' => true), array('createdAt' => 'desc'));
$products->setMaxPerPage(3);
$products->setCurrentPage($request->query->get('page', 1));
}

To create a new object instance, you can simply call the createNew() method on the repository.
Now lets try something else than product, well create a new TaxRate model.
<?php
public function myAction()
{
$repository = $this->container->get('sylius.repository.tax_rate');
$taxRate = $repository->createNew();
}

Note: Creating resources via this factory method makes the code more testable, and allows you to change the model
class easily.

Controller This service is the most important for every resource and provides a format agnostic CRUD controller
with the following actions:
[GET] showAction() for getting a single resource
[GET] indexAction() for retrieving a collection of resources
[GET/POST] createAction() for creating new resource
[GET/PUT] updateAction() for updating an existing resource
[DELETE] deleteAction() for removing an existing resource
As you can see, these actions match the common operations in any REST API and yes, they are format agnostic. That
means, all Sylius controllers can serve HTML, JSON or XML, depending on what do you request.
Additionally, all these actions are very flexible and allow you to use different templates, forms, repository methods
per route. The bundle is very powerful and allows you to register your own resources as well. To give you some idea
of what is possible, here are some examples!
Displaying a resource with custom template and repository methods:
# routing.yml
app_product_show:
path: /products/{slug}
methods: [GET]
defaults:
_controller: sylius.controller.product:showAction
_sylius:
template: AppStoreBundle:Product:show.html.twig # Use a custom template.
repository:
method: findForStore # Use custom repository method.
arguments: [$slug] # Pass the slug from the url to the repository.

Chapter 1. The Book

Sylius, Release

Creating a product using custom form and redirection method:


# routing.yml
app_product_create:
path: /my-stores/{store}/products/new
methods: [GET, POST]
defaults:
_controller: sylius.controller.product:createAction
_sylius:
form: app_user_product # Use this form type!
template: AppStoreBundle:Product:create.html.twig # Use a custom template.
factory:
method: createForStore # Use custom factory method to create a product.
arguments: [$store] # Pass the store name from the url.
redirect:
route: app_product_index # Redirect the user to his products.
parameters: [$store]

All other methods have the same level of flexibility and are documented in the [SyliusResourceBundle guide].
Core and Web Interface
Main application is constructed from two main bundles:
SyliusCoreBundle, which is the glue for all other bundles. It is the integration layer of Core component - the heart
of Sylius, providing the whole e-commerce framework. SyliusWebBundle, which contains the default web interface,
assets, templates and menu builders.
Third Party Libraries
Sylius uses a lot of libraries for various tasks:
[SymfonyCMF] for content management
[Gaufrette] for filesystem abstraction (store images locally, Amazon S3 or external server)
[Imagine] for images processing, generating thumbnails and cropping
[Snappy] for generating PDF files
[HWIOAuthBundle] for facebook/amazon/google logins
[Pagerfanta] for pagination
Final Thoughts
...
Learn more
...

1.1.5 State Machine


...
1.1. The Book

Sylius, Release

States
...
Transitions
...
Callbacks
...
Configuration
...
Final Thoughts
...
Learn more
...

1.1.6 Channels
In the modern world of e-commerce your website is no longer the only point of sale for your goods. Sylius supports
multiple-channels and in this guide you will understand them from a technical point of view.
Channel model represents a single sales channel, which can be one of the following things:
Webstore
Mobile application
Cashier in your physical store
Or pretty much any other channel type you can imagine.
The default model has the following basic properties:
code An unique code identifying this channel
name The human readable name of the channel
description Short description
color: Color representation
url: The url pattern used to identify the channel
enabled: Is the channel currently enabled?
createdAt Date of creation
updateAt Timestamp of the most recent update

10

Chapter 1. The Book

Sylius, Release

Channel configuration also allows you to configure several important aspects:


locales You can select one or more locales available in this particular store
currencies Every channel operates only on selected currencies
paymentMethods You can define which payment methods are available in this channel
shippingMethods Channel must have shipping methods configured
Final Thoughts
...
Learn more
...

1.1.7 Products
Product model represents unique products in your Sylius store. Every product can have different variations or attributes and has following values:
name - The full name of the product
slug - Urlized version of the name
description - Description of the product
shortDescription - Simple description of the product for lists and banners
metaDescription - Description for search engines
metaKeywords - SEO keywords
createdAt - Date of creation
updateAt - Timestamp of the most recent update
Options
In many cases, you will have product with different variations. The simplest example would be a T-Shirt available in
different sizes and colors. In order to automatically generate appropriate variants, you need to define options.
Every option type is represented by ProductOption and references multiple ProductOptionValue entities.
Size
S
M
L
XL
XXL
Color
Red

1.1. The Book

11

Sylius, Release

Green
Blue
Variants
ProductVariant represents a unique combination of product options and can have its own pricing configuration,
inventory tracking etc.
You are also able to use product variations system without the options at all.
Master Variant

Each product has a master variant, which tracks the same information as any other variant. It exists to simplify the
internal Sylius logic. Whenever a product is created, a master variant for that product will be created too.
Attributes
Attributes allow you to define product specific values.
Prototypes
...
Images
...
Final Thoughts
...
Learn more
...

1.1.8 Addresses
Every address in Sylius is represented by Address model. Default structure has the following fields:
firstname
lastname
street
city
postcode
reference to Country
reference to Province (optional)
12

Chapter 1. The Book

Sylius, Release

createdAt
updatedAt
Countries
Every country to which you will be shipping your goods lives as Country entity. Country consists of name and
isoName.
Provinces
Province is a smaller area inside of a Country. You can use it to manage provinces or states and assign it to an address
as well.
Attribute
id
name
country
createdAt
updatedAt

Description
Unique id of the province
Reference to a country
Date when province was created
Date of last update

Zones
This library allows you to define Zones, which represent a specific geographical area.
Zone Model

Every Zone is represented by the following model:


Attribute
id
name
type
members
createdAt
updatedAt

Description
Unique id of the zone
String type of zone
Zone members
Date when zone was created
Date of last update

Three different types of zones are supported out-of-the-box.


country zone, which consists of many countries.
province zone, which is constructed from multiple provinces.
zone, which is a group of other zones.
Each zone type has different ZoneMember model, but they all expose the same API:
There are following models and each of them represents a different zone member:
ZoneMemberCountry
ZoneMemberProvince
ZoneMemberZone

1.1. The Book

13

Sylius, Release

Matching a Zone
Zones are not very useful by themselves, but they can be part of a complex taxation/shipping or any other system. A
service implementing the ZoneMatcherInterface is responsible for matching the Address to a specific Zone.
<?php
$zoneMatcher = $this->get('sylius.zone_matcher');
$zone = $zoneMatcher->match($user->getAddress());

Zone matcher can also return all matching zones. (not only the best one)
<?php
$zoneMatcher = $this->get('sylius.zone_matcher');
$zones = $zoneMatcher->matchAll($user->getAddress());

Internally, Sylius uses this service to define the shipping and billing zones of an Order, but you can use it for many
different things and it is totally up to you.
Final Thoughts
...
Learn more
...

1.1.9 Inventory
Sylius leverages a very simple approach to inventory management. The current stock of an item is stored on the
ProductVariant entity.
It is always accessible via simple API:
<?php
echo $productVariant->getOnHand(); // Prints current inventory.

Every variant also has an unique SKU and can be available on demand, if you do not want to have strict inventory
tracking.
<?php
$variant = $product->getMasterVariant();
$variant->setAvailableOnDemand(false);
if ($variant->isAvailableOnDemand()) {
// Order any amount you want!
}

InventoryUnit
Every item sold in the store is represented by InventoryUnit, which has many different states:

14

Chapter 1. The Book

Sylius, Release

checkout - When item is in the cart.


onhold - When checkout is completed, but we are waiting for the payment.
sold - When item has been sold and is no longer in the warehouse.
backordered - Item has been sold, but is not in stock and waiting for supply.
returned - Item has been sold, but returned and is in stock.
For example, if someone puts a product Book with quantity 4 in the cart, 4 inventory units are created. This allows
us for very precise tracking of all sold/backordered/returned items.
InventoryUnitFactory
Normally, inventory units are created automatically by Sylius and you do not need to bother. If you want to create
some inventory units yourself, you should use the sylius.inventory_unit_factory service.
<?php
use Sylius\Component\Inventory\Model\InventoryUnitInterface;

$variant = // Get variant from product.


$inventoryUnits = $this->get('sylius.inventory_unit_factory')->create($variant, 6, InventoryUnitInter

$inventoryUnits is now ArrayCollection with 6 instances of InventoryUnit, referencing the ProductVariant and
with state backordered.
InventoryOperator
Inventory operator is the service responsible for managing the stock amounts of every ProductVariant with following
methods:
increase(variant, quantity)
hold(variant, quantity)
release(variant, quantity)
decrease(InventoryUnit[])
Backorders
...
Inventory On Hold
Final Thoughts
...
Learn more
...

1.1. The Book

15

Sylius, Release

1.1.10 Orders
Order model is one of the most important in Sylius, it represents the order placed via your store! It has a very consistent
and clear API, which allows you to easily manipulate and process orders.
Customer Reference
Order holds a reference to specific User, which is available through getUser() method:
echo $order->getUser()->getEmail(); // john@example.com

When creating order programatically, you can define the user yourself:
$order = $this->get('sylius.repository.order')->createNew();
$john = $this->get('sylius.repository.user')->find(3);
$order->setUser($john);

Order may not have reference to User in case when Order was created by guest.
Billing and Shipping Address
By default, every order has its own billing and shipping address, which are heavily used through whole process. Both
of them are represented by Address model.
$shippingAddress = $order->getShippingAddress();
echo 'Shipped to: '.$shippingAddress->getCountry();

Order Contents
Order holds a collection of OrderItem instances.
OrderItem model has the attributes listed below:
Attribute
id
order
variant
product
unitPrice
quantity
adjustments
adjustmentsTotal
total
createdAt
updatedAt

Description
Unique id of the item
Reference to an Order
Reference to Variant
Product loaded via Variant
The price of a single unit
Quantity of sold item
Collection of Adjustments
Total value of adjustments
Order grand total
Date when order was created
Date of last change

Taxes and Shipping Fees as Adjustments


...

16

Chapter 1. The Book

Sylius, Release

Shipments
...
Shipping State

...
Payments
...
Payment State

...
The Order State Machine
Order has also its general state, which can have the following values:
cart
pending
released
confirmed
shipped
abandoned
cancelled
returned
Final Thoughts
...
Learn more
...

1.1.11 Shipments
...
The Shipment State Machine
...

1.1. The Book

17

Sylius, Release

Shipping Methods
...
Shipping Cost Calculators
...
Default Calculators
...
Shipping Zones
...
Examples
...
Product and ProductVariant Configuration
...
Final Thoughts
...
Learn more
...

1.1.12 Payments
Sylius contains a very flexible payments management system with support for many gateways. (payment providers) We
are using very powerful payment abstraction library, called [Payum], which handles all sorts of capturing, refunding
and recurring payments logic.
On Sylius side, we integrate it into our checkout and manage all the payment data.
Payment
Every payment in Sylius, successful or failed, is represented by Payment model, which contains basic information and
reference to appropriate order.
Payment has following attributes:
id

18

Chapter 1. The Book

Sylius, Release

currency (code)
amount
reference to PaymentMethod
reference to Order
state
reference to [PaymentSource] (optional)
createdAt
updatedAt
All these properties are easily accessible through simple API:
<?php
echo $payment->getAmount() . $payment->getCurrency();
$order = $payment->getOrder(); // Get the order.
echo $payment->getMethod()->getName(); // Get the name of payment method used.

1.1.13 Payment State and Workflow


We are using [StateMachine] library to manage all payment states, here is the full list of defaults:
new (initial)
unknown
pending
processing
completed
failed
cancelled
void
refunded
Of course, you can define your own states and transitions to create a workflow, that perfectly matches your business.
Full configuration can be seen here.
Changes to payment happen mostly through applying appropriate transitions.
Payment Methods
A [PaymentMethod] represents a way that your customer pays during the checkout process. It holds a reference to
specific gateway with custom configuration. You can have different payment methods using the same gateway, like
PayPal or Stripe. Default model of payment method contains the following fields:
name
description
enabled

1.1. The Book

19

Sylius, Release

gateway
configuration
environment
feeCalculator
feeCalculatorConfiguration
createdAt
updatedAt
Gateway and Configurations
...
Payment Processing
...
Supported Gateways
...
Final Thoughts
...
Learn more
...

1.1.14 Troubleshoooting
Sylius stores payment output inside the details column of the sylius_payment table. It can provide valuable info when
debugging the payment process.
PayPal Error Code 10409
Also known as Checkout token was issued for a merchant account other than yours. You most likely changed the
PayPal credentials from config.yml during the checkout process. Clear the cache and try again:
app/console cache:clear

1.1.15 Taxation
Sylius has a very flexible taxation system, which allows you to apply appropriate taxes for different items, billing
zones and use custom calculators.

20

Chapter 1. The Book

Sylius, Release

Tax Categories
In order to process taxes in your store, you need to configure at least one TaxCategory, which represents a specific
type of merchandise. If all your items are taxed with the same rate, you can have a simple Taxable Goods category
assigned to all items.
If you sell various products and some of them have different taxes applicable, you could create multiple categories.
For example, Clothing, Books and Food.
Tax Zones
Additionally to tax categories, you can have different tax zones, in order to apply correct taxes for customers coming
from any country in the world. To understand how zones work, please refer to the Zones chapter of this book.
Tax Rates
A tax rate is essentially a percentage amount charged based on the sales price. Tax rates also contain other important
information:
Whether product prices are inclusive of this tax
The zone in which the order address must fall within
The tax category that a product must belong to in order to be considered taxable
Calculator to use for computing the tax
Default Tax Zone
...
Examples
...
Calculators
...
Final Thoughts
...
Learn more
...

1.1. The Book

21

Sylius, Release

1.1.16 Pricing
Pricing is the part of Sylius responsible for calculating the product prices for the cart. This functionality comes from
the SyliusPricingBundle.
ProductVariant implements the PriceableInterface and has following attributes available:
pricingCalculator
pricingConfiguration
Note: All prices in Sylius are represented by integers, so 14.99$ is stored as 1499 in the database.
First parameter holds the name of calculator type and second contains the configuration array for this particular calculator.
For example, if you have a product without any variations, its master variant can have a simple setup with:
price = 2099
pricingCalculator = group_based
pricingConfiguration = [23 => 2499, 85 => 2999]
Calculators
A calculator is a very simple service implement PriceCalculatorInterface and has a very important calculate(priceable, configuration, context) method.
Every time product is added, removed to cart and generally on every cart modification event, this method is used to
calculate the unit price. It takes the appropriate calculator configured on the product, together with the configuration
and context. Context is taken from the product as well, but context is coming from the cart.
Currently the context is really simple and contains the following details:
quantity of the cart item
user
groups
You can easily [implement your own pricing calculator] and allow store managers to define complex pricing rules for
any merchandise.
Final Thoughts
...
Learn more
...

1.1.17 Promotions
...

22

Chapter 1. The Book

Sylius, Release

Promotion Rules
...
Promotion Actions
...
Coupons
...
Examples
...
Exclusive Promotions
Final Thoughts
...
Learn more
...

1.1.18 Users and Groups


Retrieving users and groups from the database should always happen via repository, which implements
Sylius\Bundle\ResourceBundle\Model\RepositoryInterface. If you are using Doctrine, youre
already familiar with this concept, as it extends the native Doctrine ObjectRepository interface.
Your user repository is always accessible via sylius.repository.user service.
Of course,
sylius.repository.group is also available for use. Sometimes you will not need it because you can
obtain a group directly from the User.
User Management
Creating users is quite straight forward. You simply call the createNew() method on the repository. Next you are
able to set all the information.
<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.user');
$user = $repository->createNew();
$user->setEmail('john.doe@example.com');
$user->setFirstName('John');
$user->setLastName('Doe');

1.1. The Book

23

Sylius, Release

$manager = $this->container->get('sylius.manager.user');
$manager->persist($user);
$manager->flush(); // Save changes in database.
}

Setting billing and shipping addresses is easy. First you need an Address object. This is created by calling the
createNew() method on the repository.
Afterwards you can set all the data and assign the Address object to the user by using either
setShippingAddress() or setBillingAddress().
<?php
public function myAction(Request $request)
{
$userRepository = $this->container->get('sylius.repository.user');
$user = $userRepository->find(1);
$addressRepository = $this->container->get('sylius.repository.address');
$address = $addressRepository->createNew();
$address->setFirstName('John');
$address->setLastName('Doe');
$address->setCity('New York');
$address->setPostcode(2000);
$address->setStreet('Times Sq. 137');
$user->setBillingAddress($address);
$manager = $this->container->get('sylius.manager.user');
$manager->persist($user);
$manager->flush(); // Save changes in database.
}

Groups
To create a new group instance, you can simply call the createNew() method on the repository.
Do not forget setting a name for the group, it is a required field as it is not nullable. The name for the group must also
be unique.
<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.group');
$group = $repository->createNew();
$group->setName('Premium customers');
}

You can now start adding users to your newly made group.
<?php
public function myAction(Request $request)
{
$groupRepository = $this->container->get('sylius.repository.group');
$group = $groupRepository->findOneBy(array('name' => 'Premium customers');

24

Chapter 1. The Book

Sylius, Release

$userRepository = $this->container->get('sylius.repository.user');
$user = $userRepository->find(1);
$user->addGroup($group);
$manager = $this->container->get('sylius.manager.group');
$manager->persist($user);
$manager->flush(); // Save changes in database.
}

Final Thoughts
...
Learn more
...

1.1.19 Currencies
Sylius supports multiple currencies per store and makes it very easy to manage them.
There are several approaches to processing several currencies, but we decided to use the simplest solution we store all
money values in the default currency and convert them to different currency with current rates or specific rates.
Every currency is represented by Currency entity and holds basic information:
id
code
exchangeRate
enabled
createdAt
updatedAt
The default currency has exchange rate of 1.000.
Currency Context
By default, user can switch his current currency in the frontend of the store.
To manage the currently used currency, we use CurrencyContext.
sylius.context.currency id.

You can always access it through

<?php
public function fooAction()
{
$currency = $this->get('sylius.context.currency')->getCurrency();
}

To change the currently used currency, you can simply use the setCurrency() method of context service.

1.1. The Book

25

Sylius, Release

<?php
public function fooAction()
{
$this->get('sylius.context.currency')->setCurrency('PLN');
}

The currency context can be injected into your custom service and give you access to currently used currency.
Product Prices
...
Available Currencies Provider
The default menu for selecting currency is using a special service called sylius.currency_provider, which
returns all enabled currencies. This is your entry point if you would like override this logic and return different
currencies for various scenarios.
<?php
public function fooAction()
{
$currencies = $this->get('sylius.currency_provider')->getAvailableCurrencies();
foreach ($currencies as $currency) {
echo $currency->getCode();
}
}

Final Thoughts
...
Learn more
...

1.1.20 Locales
To support multiple site languages, we use Locale model with the following set of fields:
id
code
enabled
createdAt
updatedAt

26

Chapter 1. The Book

Sylius, Release

Locale Context
With the default configuration, customers are able to change the store language in the frontend.
To manage the currently used language, we use LocaleContext.
sylius.context.locale id.

You can always access it through

<?php
public function fooAction()
{
$locale = $this->get('sylius.context.locale')->getLocale();
echo $locale; // pl_PL
}

To change the locale, you can simply use the setLocale() method of the context service.
<?php
public function fooAction()
{
$this->get('sylius.context.locale')->setLocale('de_DE'); // Store will be displayed in German.
}

The locale context can be injected into your custom service and give you access to currently used locale.
Available Locales Provider
Service sylius.locale_provider is responsible for returning all languages available to the current user. By
default, it filters out all disabled locales. You can easily modify this logic by overriding this component.
<?php
public function fooAction()
{
$locales = $this->get('sylius.locale_provider')->getAvailableLocales();
foreach ($locales as $locale) {
echo $locale->getCode();
}
}

To get all languages configured in the store, including the disabled ones, you can simply use the repository.
<?php
$locales = $this->get('sylius.repository.locale')->findAll();

Final Thoughts
...
Learn more
...

1.1. The Book

27

Sylius, Release

1.1.21 Content
...
SymfonyCMF and PHPCR
...
Static Content
...
Pages
...
Blocks
...
Final Thoughts
...
Learn more
...

1.1.22 E-Mails
Sylius is sending various e-mails and this chapter is a reference about all of them. Continue reading to learn what emails are sent, when and how to customize the templates. To understand how e-mail sending works internally, please
refer to SyliusMailerBundle documentation.
User Confirmation E-Mail
Every time new customer registers via registration form or checkout, user_confirmation e-mail is sent to him.
The default template is
SyliusWebBundle:Email:userConfirmation.html.twig

You also have the following parameters available:


user Instance of the user model

28

Chapter 1. The Book

Sylius, Release

Order Confirmation
This e-mail is sent when order is paid. Unique code is order_confirmation. Template name is:
SyliusWebBundle:Email:orderConfirmation.html.twig

You also have the following parameters available:


order Instance of the user order
order.user Customer
order.shippingAddress Shipping address
order.billingAddress Billing address
order.items Collection of order items
Order Comment
In the backend, you can comment orders and optionally notify the customer via e-mail with code order_comment,
this template is used:
SyliusWebBundle:Email:orderComment.html.twig

You also have the following parameters available:


comment: Comment instance
order Instance of the user order
order.user Customer
order.shippingAddress Shipping address
order.billingAddress Billing address
order.items Collection of order items
Final Thoughts
...
Learn more
...

1.1.23 Settings
...
General Settings
...

1.1. The Book

29

Sylius, Release

Taxation Settings
...
Final Thoughts
...
Learn more
...
Introduction to Sylius
Installation
Architecture Overview
State Machine
Channels
Products
Addresses
Inventory
Orders
Shipments
Payments
Taxation
Pricing
Promotions
Users and Groups
Currencies
Locales
Content
E-Mails
Settings
Introduction to Sylius
Installation
Architecture Overview
State Machine
Channels
Products
Addresses

30

Chapter 1. The Book

Sylius, Release

Inventory
Orders
Shipments
Payments
Taxation
Pricing
Promotions
Users and Groups
Currencies
Locales
Content
E-Mails
Settings

1.1. The Book

31

Sylius, Release

32

Chapter 1. The Book

CHAPTER 2

The API Guide

This chapter covers the REST API of Sylius platform.

2.1 The API Guide


2.1.1 Introduction to Sylius REST API
This part of documentation is about RESTful JSON/XML API for Sylius platform.
Note: This documentation assumes you have at least basic experience with REST APIs.

2.1.2 Authorization
This part of documentation is about authorization to Sylius platform through API.
OAuth2
Sylius has configured OAuth2 authorization. The authorization process is standard procedure. Authorize as admin and
enjoy the API!
Note: User has to have ROLE_API role in order to access /api resources

Create OAuth client

Use sylius command:

php app/console sylius:oauth-server:create-client --grant-type="password" --grant-type="refresh_token

You will receive client public id and client secret


Example
public id: 4fugw85qtim8c48s88g00s0gc040s4s4k8wk4kswowkksg0skw
secret: s8tg7026wo0k0koco8o4wc0kwocowskgwkk0wowsgc8o4408c

33

Sylius, Release

Tip: If you use Guzzle check out OAuth2 plugin and use Password Credentials.

Obtain access token

Send the request with the following parameters


client_id Public client id
client_secret Client secret
grant_type We will use password to authorize as user
username User name
password User password
Example

GET /oauth/v2/token?client_id=4fugw85qtim8c48s88g00s0gc040s4s4k8wk4kswowkksg0skw&client_secret=s8tg70

Example response
{

access_token: "Y2Y2NmNlNGExNzc1YmRiNzY3MDFlNmU0NjVjZjAxZjMwOTQ0MDZlODVhMTJlYTc4MDU3ZDFjMmExZjU3YT
expires_in: 3600
token_type: "bearer"
scope: null
refresh_token: "YzYzNDYyZmFiN2QyYTk3OTM4ZTFjODA2ZWJkMDFiZmIwZjE2Yzc4MTBkZWFlYzM3ZDU4YTE5ODcwMTc3M
}

Request for resource

Put access token in the request header


Authorization: Bearer {access_token}

You can now access any resource you want under /api prefix
Example

GET /api/users/
Authorization: Bearer Y2Y2NmNlNGExNzc1YmRiNzY3MDFlNmU0NjVjZjAxZjMwOTQ0MDZlODVhMTJlYTc4MDU3ZDFjMmExZjU

Note: You have to refresh your token after it expires

Refresh token

Send request with the following parameters


client_id Public client id
client_secret Client secret
grant_type refresh_token
34

Chapter 2. The API Guide

Sylius, Release

refresh_token Refresh token


Example

GET /oauth/v2/token?client_id=4fugw85qtim8c48s88g00s0gc040s4s4k8wk4kswowkksg0skw&client_secret=s8tg70

Example response You can now use new token to send requests
{

access_token: "MmE1YmJkMmVjNWI4YTUyZWU2OTM2NzljM2Y2N2FkMTVkMTQ2Y2ViYmZhNTQ4OTYzODVmN2UzMjEwNjU3NW
expires_in: 3600
token_type: "bearer"
scope: null
refresh_token: "OGQyMWZhYzkzYTZlNWY2YjA5MzRjMTk2MTNkNjM2Y2Y5ODg3ZjRlZmVlY2IyMmY1OGZkNGMxMjAwZjRmZ
}

2.1.3 Channels API


Sylius channels API endpoint is /api/channels.
Index of all channels
To browse all channels available in the Sylius e-commerce platform you can call the following GET request:
GET /api/channels/

Parameters

page Number of the page, by default = 1


limit Number of items to display per page
Response

Response will contain a paginated list of channels.


STATUS: 200 OK
{
"page":1,
"limit":10,
"pages":1,
"total":3,
"_links":{
"self":{
"href":"\/api\/channels\/?page=1"
},
"first":{
"href":"\/api\/channels\/?page=1"
},
"last":{
"href":"\/api\/channels\/?page=12"
},

2.1. The API Guide

35

Sylius, Release

"next":{
"href":"\/api\/channels\/?page=2"
}
},
"_embedded":{
"items":[
{
"code": "WEB-UK",
"color": "Red",
"created_at": "2014-11-26T23:00:15+0000",
"currencies": [
],
"enabled": true,
"id": 91,
"locales": [
],
"name": "UK Webstore",
"payment_methods": [
],
"shipping_methods": [
],
"type": "web",
"updated_at": "2014-11-26T23:00:15+0000"
}
]
}
}

Getting a single channel


You can view a single channel by executing the following request:
GET /api/channels/91

Response
STATUS: 200 OK
{
"code": "WEB-UK",
"color": "Red",
"created_at": "2014-11-26T23:00:15+0000",
"currencies": [
],
"enabled": true,
"id": 91,
"locales": [
],
"name": "UK Webstore",
"payment_methods": [
],
"shipping_methods": [
],
"type": "web",
"updated_at": "2014-11-26T23:00:15+0000"
}

36

Chapter 2. The API Guide

Sylius, Release

Create an channel
To create a new channel, you can execute the following request:
POST /api/channels/

Parameters

code Unique code


color Color used in the backend
enabled (optional) Is enabled? (boolean)
locales (optional) Array of Locale id
currencies (optional) Array of Currency id
paymentMethods (optional) Array of PaymentMethod id
shippingMethods (optional) Array of ShippingMethod id
Response
STATUS: 201 CREATED
{
"code": "WEB-US",
"color": "Blue",
"created_at": "2014-11-26T23:00:15+0000",
"currencies": [
],
"enabled": true,
"id": 92,
"locales": [
],
"name": "US Webstore",
"payment_methods": [
],
"shipping_methods": [
],
"type": "web",
"updated_at": "2014-11-26T23:00:15+0000"
}

Updating a channel
You can update an existing channel using PUT or PATCH method:
PUT /api/channels/92
PATCH /api/channels/92

2.1. The API Guide

37

Sylius, Release

Parameters

code Unique code


color Color used in the backend
enabled (optional) Is enabled? (boolean)
locales (optional) Array of Locale id
currencies (optional) Array of Currency id
paymentMethods (optional) Array of PaymentMethod id
shippingMethods (optional) Array of ShippingMethod id
Response
STATUS: 204 NO CONTENT

Deleting a channel
You can delete (soft) a channel from the system by making the following DELETE call:
DELETE /api/channels/92

Response
STATUS: 204 NO CONTENT

2.1.4 Orders API


Sylius orders API endpoint is /api/orders.
Index of all orders
You can retrieve the full list order by making the following request:
GET /api/orders/

Parameters

page Number of the page, by default = 1


limit Number of items to display per page
Response

The response will contain the newly created order information.

38

Chapter 2. The API Guide

Sylius, Release

STATUS: 200 OK
{
"page":1,
"limit":10,
"pages":12,
"total":120,
"_links":{
"self":{
"href":"\/api\/orders\/?page=1"
},
"first":{
"href":"\/api\/orders\/?page=1"
},
"last":{
"href":"\/api\/orders\/?page=12"
},
"next":{
"href":"\/api\/orders\/?page=2"
}
},
"_embedded":{
"items":[
{
"id":301,
"completed_at":"2014-11-26T23:00:33+0000",
"number":"000000048",
"items":[
{
"id":1353,
"quantity":3,
"unit_price":9054,
"adjustments":[
],
"adjustments_total":0,
"total":27162,
"immutable":false,
"variant":{
"id":13099,
"master":false,
"object":{
"id":2107,
"name":"T-Shirt \"voluptas\"",
"description":"Non molestias voluptas quae nemo omnis totam. Impedit
"created_at":"2014-11-26T23:00:17+0000",
"updated_at":"2014-11-26T23:00:17+0000",
"masterVariant":{
"id":13085,
"master":true,
"options":[
],
"created_at":"2014-11-26T23:00:17+0000",
"updated_at":"2014-11-26T23:00:17+0000",
"available_on":"2014-08-27T08:51:04+0000",
"sku":"43596"
},

2.1. The API Guide

39

Sylius, Release

"short_description":"Quos in dignissimos in fugit culpa vitae."


},
"created_at":"2014-11-26T23:00:17+0000",
"updated_at":"2014-11-26T23:00:34+0000",
"available_on":"2013-12-10T09:16:56+0000",
"sku":"8808"
},
"inventory_units":[
],
"_links":{
"product":{
"href":"\/api\/products\/2107"
},
"variant":{
"href":"\/api\/products\/2107\/variants\/13099"
}
}
}
],
"items_total":97783,
"adjustments":[
],
"comments":[
],
"adjustments_total":24240,
"total":122023,
"confirmed":true,
"created_at":"2014-04-30T10:41:14+0000",
"updated_at":"2014-11-26T23:00:34+0000",
"state":"pending",
"email":"ygrant@example.com",
"expires_at":"2014-11-27T02:00:33+0000",
"user":{
"id":476,
"username":"ygrant@example.com",
"username_canonical":"ygrant@example.com",
"email":"ygrant@example.com",
"email_canonical":"ygrant@example.com",
"enabled":false,
"groups":[
],
"locked":false,
"expired":false,
"roles":[
],
"credentials_expired":false
},
"channel":{
"id":91,
"code":"WEB-UK",
"name":"UK Webstore",
"type":"web",
"color":"Red",
"enabled":true,
"created_at":"2014-11-26T23:00:15+0000",

40

Chapter 2. The API Guide

Sylius, Release

"updated_at":"2014-11-26T23:00:15+0000",
},
"shipping_address":{
},
"billing_address":{
},
"payments":[
],
"shipments":[
],
"currency":"GBP",
"checkout_state":"cart"
}
]
}
}

Getting a single order


You can view a single order by executing the following request:
GET /api/orders/24/

Response
STATUS: 200 OK
{
"id":301,
"completed_at":"2014-11-26T23:00:33+0000",
"number":"000000048",
"items":[
{
"id":1353,
"quantity":3,
"unit_price":9054,
"adjustments":[

],
"adjustments_total":0,
"total":27162,
"immutable":false,
"variant":{
"id":13099,
"master":false,
"object":{
"id":2107,
"name":"T-Shirt \"voluptas\"",
"description":"Non molestias voluptas quae nemo omnis totam. Impedit ad perferend
"created_at":"2014-11-26T23:00:17+0000",
"updated_at":"2014-11-26T23:00:17+0000",
"masterVariant":{
"id":13085,
"master":true,
"options":[

2.1. The API Guide

41

Sylius, Release

],
"created_at":"2014-11-26T23:00:17+0000",
"updated_at":"2014-11-26T23:00:17+0000",
"available_on":"2014-08-27T08:51:04+0000",
"sku":"43596"
},
"short_description":"Quos in dignissimos in fugit culpa vitae."
},
"created_at":"2014-11-26T23:00:17+0000",
"updated_at":"2014-11-26T23:00:34+0000",
"available_on":"2013-12-10T09:16:56+0000",
"sku":"8808"
},
"inventory_units":[
{
"id":4061,
"inventory_state":"onhold",
"created_at":"2014-11-26T23:00:34+0000",
"updated_at":"2014-11-26T23:00:34+0000",
"_links":{
"order":{
"href":"\/app_dev.php\/api\/orders\/301"
}
}
},
{
"id":4062,
"inventory_state":"onhold",
"created_at":"2014-11-26T23:00:34+0000",
"updated_at":"2014-11-26T23:00:34+0000",
"_links":{
"order":{
"href":"\/app_dev.php\/api\/orders\/301"
}
}
},
{
"id":4063,
"inventory_state":"onhold",
"created_at":"2014-11-26T23:00:34+0000",
"updated_at":"2014-11-26T23:00:34+0000",
"_links":{
"order":{
"href":"\/app_dev.php\/api\/orders\/301"
}
}
}
],
"_links":{
"product":{
"href":"\/app_dev.php\/api\/products\/2107"
},
"variant":{
"href":"\/app_dev.php\/api\/products\/2107\/variants\/13099"
}
}
}
],

42

Chapter 2. The API Guide

Sylius, Release

"items_total":97783,
"adjustments":[
{
"id":1011,
"type":"tax",
"description":"EU VAT (23%)",
"amount":22490,
"neutral":false,
"locked":false,
"created_at":"2014-11-26T23:00:33+0000",
"updated_at":"2014-11-26T23:00:34+0000"
},
{
"id":1012,
"type":"shipping",
"description":"UPS Ground",
"amount":2500,
"neutral":false,
"locked":false,
"created_at":"2014-11-26T23:00:33+0000",
"updated_at":"2014-11-26T23:00:34+0000"
},
{
"id":1013,
"type":"promotion",
"description":"New Year Sale for 3 and more items.",
"amount":-500,
"neutral":false,
"locked":false,
"created_at":"2014-11-26T23:00:33+0000",
"updated_at":"2014-11-26T23:00:34+0000"
},
{
"id":1014,
"type":"promotion",
"description":"Christmas Sale for orders over 100 EUR.",
"amount":-250,
"neutral":false,
"locked":false,
"created_at":"2014-11-26T23:00:33+0000",
"updated_at":"2014-11-26T23:00:34+0000"
}
],
"comments":[
],
"adjustments_total":24240,
"total":122023,
"confirmed":true,
"created_at":"2014-04-30T10:41:14+0000",
"updated_at":"2014-11-26T23:00:34+0000",
"state":"pending",
"email":"ygrant@example.com",
"expires_at":"2014-11-27T02:00:33+0000",
"user":{
"id":476,
"username":"ygrant@example.com",
"username_canonical":"ygrant@example.com",

2.1. The API Guide

43

Sylius, Release

"email":"ygrant@example.com",
"email_canonical":"ygrant@example.com",
"enabled":false,
"groups":[
],
"locked":false,
"expired":false,
"roles":[
],
"credentials_expired":false
},
"channel":{
"id":91,
"code":"WEB-UK",
"name":"UK Webstore",
"type":"web",
"color":"Red",
"enabled":true,
"created_at":"2014-11-26T23:00:15+0000",
"updated_at":"2014-11-26T23:00:15+0000",
},
"shipping_address":{
},
"billing_address":{
},
"payments":[
],
"shipments":[
],
"currency":"GBP",
"checkout_state":"cart"
}

Create an order
To create a new order (cart), you need to execute the following request:
POST /api/orders/

Parameters

channel The id of channel


user The id of customer
currency Currency code
Response
STATUS: 201 CREATED

44

Chapter 2. The API Guide

Sylius, Release

{
"id":304,
"items":[
],
"items_total":0,
"adjustments":[
],
"comments":[
],
"adjustments_total":0,
"total":0,
"confirmed":true,
"created_at":"2014-11-29T12:29:07+0000",
"updated_at":"2014-11-29T12:29:08+0000",
"state":"cart",
"email":"chelsie.witting@example.com",
"expires_at":"2014-11-29T15:29:07+0000",
"user":{
"id":481,
"username":"chelsie.witting@example.com",
"username_canonical":"chelsie.witting@example.com",
"email":"chelsie.witting@example.com",
"email_canonical":"chelsie.witting@example.com",
"enabled":true,
"groups":[
],
"locked":false,
"expired":false,
"roles":[
],
"credentials_expired":false
},
"channel":{
"id":91,
"code":"WEB-UK",
"name":"UK Webstore",
"type":"web",
"color":"Red",
"enabled":true,
"created_at":"2014-11-26T23:00:15+0000",
"updated_at":"2014-11-26T23:00:15+0000",
},
"payments":[
],
"shipments":[
],
"currency":"USD",
"checkout_state":"cart"
}

Deleting a single order


You can delete (soft) an order from the system by making the following DELETE call:

2.1. The API Guide

45

Sylius, Release

DELETE /api/orders/24

Response
STATUS: 204 NO CONTENT

Add an item to order


To add an item to order, you simply need to do a POST request:
POST /api/orders/305/items/

Parameters

variant The id of product variant


unitPrice Unit price of the item
quantity Desired quantity
Response

Response will contain a representation of the newly created item.


STATUS: 201 CREATED
{
"_links": {
"product": {
"href": "/app_dev.php/api/products/101"
},
"variant": {
"href": "/app_dev.php/api/products/101/variants/779"
}
},
"adjustments": [],
"adjustments_total": 0,
"id": 277,
"immutable": false,
"inventory_units": [
{
"_links": {
"order": {
"href": "/app_dev.php/api/orders/52"
}
},
"created_at": "2014-12-15T13:18:48+0000",
"id": 828,
"inventory_state": "checkout",
"updated_at": "2014-12-15T13:18:48+0000"
},
{
"_links": {

46

Chapter 2. The API Guide

Sylius, Release

"order": {
"href": "/app_dev.php/api/orders/52"
}
},
"created_at": "2014-12-15T13:18:48+0000",
"id": 829,
"inventory_state": "checkout",
"updated_at": "2014-12-15T13:18:48+0000"
},
{
"_links": {
"order": {
"href": "/app_dev.php/api/orders/52"
}
},
"created_at": "2014-12-15T13:18:48+0000",
"id": 830,
"inventory_state": "checkout",
"updated_at": "2014-12-15T13:18:48+0000"

}
],
"quantity": 3,
"total": 0,
"unit_price": 500000,
"variant": {
"available_on": "2014-04-01T06:43:02+0000",
"created_at": "2014-12-03T09:54:35+0000",
"id": 779,
"master": true,
"object": {
"attributes": [
{
"id": 238,
"name": "Book author",
"presentation": "Author",
"value": "Marlen Yost"
},
{
"id": 239,
"name": "Book ISBN",
"presentation": "ISBN",
"value": "326ccbc7-92d1-3aec-b3af-df8afdc5651d"
},
{
"id": 240,
"name": "Book pages",
"presentation": "Number of pages",
"value": "149"
}
],
"created_at": "2014-12-03T09:54:35+0000",
"description": "Et eveniet voluptas ut magni vero temporibus nihil. Omnis possimus accusa
"id": 101,
"name": "Book \"Quidem\" by \"Marlen Yost\"",
"options": [],
"short_description": "Distinctio quos est eaque fugit totam repellendus.",
"updated_at": "2014-12-03T09:54:35+0000"
},

2.1. The API Guide

47

Sylius, Release

"options": [],
"sku": "326ccbc7-92d1-3aec-b3af-df8afdc5651d",
"updated_at": "2014-12-03T09:54:35+0000"
}
}

Removing an item from order


To remove an item from order, you can simply call a DELETE on its url.
DELETE /api/orders/49/items/245

Response
STATUS: 204 NO CONTENT

2.1.5 Checkouts API


After you create a cart (an empty order) and add some items to it, you can start the checkout via API. This basically
means updating the order with concrete information, step by step, in a correct order.
Default Sylius checkout via API is constructed from the following steps:
addressing You enter customer shipping and billing address
shipping Shipments are proposed and you can select methods
payment Payments are calculated and methods proposed
finalize Final order is built and you can confirm it, cart will become an order
purchase You provide Sylius with payment information and order is paid
Sylius API endpoint is /api/orders.
Addressing step
After you added some items to the cart, to start the checkout you simply need to provide a shipping address. You can
also specify a different billing address if needed.
You need to pass order id in the following url and make a PUT call:
PUT /api/checkouts/44

Parameters

shippingAddress[firstName] Firstname for shipping address


shippingAddress[lastName] Lastname for shipping address
shippingAddress[city] City name
shippingAddress[postcode] Postcode
shippingAddress[street] Address line 1

48

Chapter 2. The API Guide

Sylius, Release

shippingAddress[country] Id of the country


shippingAddress[province] (optional) Id of the province
If you do not specify the billing address block, shipping address will be used for that purpose.
billingAddress[firstName] Firstname for billing address
billingAddress[lastName] Lastname for billing address
billingAddress[city] City name
billingAddress[postcode] Postcode
billingAddress[street] Address line 1
billingAddress[country] Id of the country
billingAddress[province] (optional) Id of the province
Response

The response will contain the updated order information.


STATUS: 200 OK
{
"adjustments": ,
"adjustments_total": -250,
"shipping_address": {
"_links": {
"country": {
"href": "/app_dev.php/api/countries/9"
}
},
"city": "New York",
"created_at": "2014-12-15T13:37:28+0000",
"first_name": "John",
"id": 105,
"last_name": "Doe",
"postcode": "12435",
"street": "Test",
"updated_at": "2014-12-15T13:37:29+0000"
},
"billing_address": {
"_links": {
"country": {
"href": "/app_dev.php/api/countries/9"
}
},
"city": "New York",
"created_at": "2014-12-15T13:37:28+0000",
"first_name": "John",
"id": 106,
"last_name": "Doe",
"postcode": "12435",
"street": "Test",
"updated_at": "2014-12-15T13:37:29+0000"
},
"channel": {
"_links": {

2.1. The API Guide

49

Sylius, Release

"self": {
"href": "/app_dev.php/api/channels/3"
}
},
"code": "WEB-US",
"color": "Pink",
"created_at": "2014-12-03T09:54:28+0000",
"enabled": true,
"id": 3,
"name": "United States Webstore",
"type": "web",
"updated_at": "2014-12-03T09:58:29+0000"
},
"checkout_state": "addressing",
"comments": [],
"confirmed": true,
"created_at": "2014-12-15T13:15:22+0000",
"currency": "USD",
"email": "xschaefer@example.com",
"expires_at": "2014-12-15T16:15:22+0000",
"id": 52,
"items": [],
"items_total": 1500000,
"payments": [],
"shipments": [],
"state": "cart",
"total": 1499750,
"updated_at": "2014-12-15T13:37:29+0000",
"user": {
"credentials_expired": false,
"email": "xschaefer@example.com",
"email_canonical": "xschaefer@example.com",
"enabled": true,
"expired": false,
"groups": [],
"id": 5,
"locked": false,
"roles": [],
"username": "xschaefer@example.com",
"username_canonical": "xschaefer@example.com"
}
}

Shipping step
When order contains the address information, we are able to determine the stock locations and available shipping
methods. You can get these informations by first calling a GET request on the checkout unique URL.
GET /api/checkouts/44
STATUS: 200 OK
[
{
"methods": [
{
"_links": {

50

Chapter 2. The API Guide

Sylius, Release

"self": {
"href": "/app_dev.php/api/shipping-methods/4"
},
"zone": {
"href": "/app_dev.php/api/zones/4"
}
},
"calculator": "flexible_rate",
"category_requirement": 1,
"configuration": {
"additional_item_cost": 500,
"additional_item_limit": 10,
"first_item_cost": 4000
},
"created_at": "2014-12-03T09:54:28+0000",
"enabled": true,
"id": 4,
"name": "FedEx World Shipping",
"updated_at": "2014-12-03T09:54:28+0000"
}
],
"shipment": {
"_links": {
"order": {
"href": "/app_dev.php/api/orders/52"
}
},
"created_at": "2014-12-15T14:11:32+0000",
"state": "checkout"
}
}
]

Response contains the proposed shipments and for each, it also has a list of shipping methods available.
Next step is updating the order with the types of shipping method that we have selected. To do so, you need to call
another PUT request, but this time with different set of parameters.
You need to pass an id of shipping method for every id, you should obtain them in the previous request.
PUT /api/checkouts/44

Parameters

shipments[X][method] The id of the shipping method, where X is the shipment number


Response

Response will contain an updated order information.


STATUS: 200 OK
{
"adjustments": {
},
"adjustments_total": 4750,

2.1. The API Guide

51

Sylius, Release

"billing_address": {
},
"channel": {
},
"checkout_state": "shipping",
"comments": [],
"confirmed": true,
"created_at": "2014-12-15T13:15:22+0000",
"currency": "USD",
"email": "xschaefer@example.com",
"expires_at": "2014-12-15T16:15:22+0000",
"id": 52,
"items": [
],
"items_total": 1500000,
"payments": [],
"shipments": [
{
"_links": {
"method": {
"href": "/app_dev.php/api/shipping-methods/4"
},
"order": {
"href": "/app_dev.php/api/orders/52"
},
"self": {
"href": "/app_dev.php/api/shipments/51"
}
},
"created_at": "2014-12-15T14:30:40+0000",
"id": 51,
"method": {
"_links": {
"self": {
"href": "/app_dev.php/api/shipping-methods/4"
},
"zone": {
"href": "/app_dev.php/api/zones/4"
}
},
"calculator": "flexible_rate",
"category_requirement": 1,
"configuration": {
"additional_item_cost": 500,
"additional_item_limit": 10,
"first_item_cost": 4000
},
"created_at": "2014-12-03T09:54:28+0000",
"enabled": true,
"id": 4,
"name": "FedEx World Shipping",
"updated_at": "2014-12-03T09:54:28+0000"
},
"state": "checkout",
"updated_at": "2014-12-15T14:30:41+0000"
}
],
"shipping_address": {

52

Chapter 2. The API Guide

Sylius, Release

},
"state": "cart",
"total": 1504750,
"updated_at": "2014-12-15T14:30:41+0000",
"user": {
}
}

Payment step
When we are done with shipping choices and we know the final price of an order, we can select a payment method.
To obtain a list of available payment methods for this order, simply call a GET request again:
GET /api/checkouts/44
STATUS: 200 OK
{
"methods": {
"1": {
"_links": {
"self": {
"href": "/app_dev.php/api/payment-methods/1"
}
},
"created_at": "2014-12-03T09:54:28+0000",
"id": 1,
"name": "Dummy",
"updated_at": "2014-12-03T09:54:28+0000"
},
"2": {
"_links": {
"self": {
"href": "/app_dev.php/api/payment-methods/2"
}
},
"created_at": "2014-12-03T09:54:28+0000",
"id": 2,
"name": "Paypal Express Checkout",
"updated_at": "2014-12-03T09:54:28+0000"
},
"3": {
"_links": {
"self": {
"href": "/app_dev.php/api/payment-methods/3"
}
},
"created_at": "2014-12-03T09:54:28+0000",
"id": 3,
"name": "Stripe",
"updated_at": "2014-12-03T09:54:28+0000"
},
"4": {
"_links": {
"self": {
"href": "/app_dev.php/api/payment-methods/4"

2.1. The API Guide

53

Sylius, Release

}
},
"created_at": "2014-12-03T09:54:28+0000",
"id": 4,
"name": "Be2bill",
"updated_at": "2014-12-03T09:54:28+0000"
},
"5": {
"_links": {
"self": {
"href": "/app_dev.php/api/payment-methods/5"
}
},
"created_at": "2014-12-03T09:54:28+0000",
"id": 5,
"name": "Stripe Checkout",
"updated_at": "2014-12-03T09:54:28+0000"
}
},
"payment": {
"_links": {
"order": {
"href": "/app_dev.php/api/orders/52"
}
},
"amount": 1504750,
"created_at": "2014-12-15T14:57:28+0000",
"currency": "USD",
"state": "new"
}
}

With that information, another PUT request with the id of payment method is enough to proceed:
PUT /api/checkouts/44

Parameters

paymentMethod The id of the payment method you prefer


Response

Response will contain the updated order information.


STATUS: 200 OK
{
"adjustments": [
],
"adjustments_total": 4750,
"billing_address": {
},
"channel": {
},
"checkout_state": "payment",
"comments": [],

54

Chapter 2. The API Guide

Sylius, Release

"confirmed": true,
"created_at": "2014-12-15T13:15:22+0000",
"currency": "USD",
"email": "xschaefer@example.com",
"expires_at": "2014-12-15T16:15:22+0000",
"id": 52,
"items": [
],
"items_total": 1500000,
"payments": [
{
"_links": {
"order": {
"href": "/app_dev.php/api/orders/52"
},
"payment-method": {
"href": "/app_dev.php/api/payment-methods/1"
},
"self": {
"href": "/app_dev.php/api/payments/51"
}
},
"amount": 1504750,
"created_at": "2014-12-15T15:02:54+0000",
"currency": "USD",
"id": 51,
"method": {
"_links": {
"self": {
"href": "/app_dev.php/api/payment-methods/1"
}
},
"created_at": "2014-12-03T09:54:28+0000",
"id": 1,
"name": "Dummy",
"updated_at": "2014-12-03T09:54:28+0000"
},
"state": "new",
"updated_at": "2014-12-15T15:02:55+0000"
}
],
"shipments": [
],
"shipping_address": {
},
"state": "cart",
"total": 1504750,
"updated_at": "2014-12-15T15:02:55+0000",
"user": {
}
}

Finalize step
Now your order is fully constructed, you can get its latest snapshot by calling your last GET request:

2.1. The API Guide

55

Sylius, Release

GET /api/checkouts/44
STATUS: 200 OK
{
"adjustments": [
{
"amount": 0,
"created_at": "2014-12-15T13:37:29+0000",
"description": "No tax (0%)",
"id": 205,
"type": "tax",
"locked": false,
"neutral": false,
"updated_at": "2014-12-15T13:37:29+0000"
},
{
"amount": 5000,
"created_at": "2014-12-15T14:30:41+0000",
"description": "FedEx World Shipping",
"id": 207,
"type": "shipping",
"locked": false,
"neutral": false,
"updated_at": "2014-12-15T14:30:41+0000"
},
{
"amount": -250,
"created_at": "2014-12-15T14:30:41+0000",
"description": "Christmas Sale for orders over 100 EUR.",
"id": 208,
"type": "promotion",
"locked": false,
"neutral": false,
"updated_at": "2014-12-15T14:30:41+0000"
}
],
"adjustments_total": 4750,
"billing_address": {
"_links": {
"country": {
"href": "/app_dev.php/api/countries/9"
}
},
"city": "New York",
"created_at": "2014-12-15T13:37:28+0000",
"first_name": "John",
"id": 106,
"last_name": "Doe",
"postcode": "12435",
"street": "Test",
"updated_at": "2014-12-15T13:37:29+0000"
},
"channel": {
"_links": {
"self": {
"href": "/app_dev.php/api/channels/3"
}

56

Chapter 2. The API Guide

Sylius, Release

},
"code": "WEB-US",
"color": "Pink",
"created_at": "2014-12-03T09:54:28+0000",
"enabled": true,
"id": 3,
"name": "United States Webstore",
"type": "web",
"updated_at": "2014-12-03T09:58:29+0000"
},
"checkout_state": "payment",
"comments": [],
"confirmed": true,
"created_at": "2014-12-15T13:15:22+0000",
"currency": "USD",
"email": "xschaefer@example.com",
"expires_at": "2014-12-15T16:15:22+0000",
"id": 52,
"items": [
{
"_links": {
"product": {
"href": "/app_dev.php/api/products/101"
},
"variant": {
"href": "/app_dev.php/api/products/101/variants/779"
}
},
"adjustments": [],
"adjustments_total": 0,
"id": 277,
"immutable": false,
"inventory_units": [
{
"_links": {
"order": {
"href": "/app_dev.php/api/orders/52"
}
},
"created_at": "2014-12-15T13:18:48+0000",
"id": 828,
"inventory_state": "checkout",
"updated_at": "2014-12-15T14:30:41+0000"
},
{
"_links": {
"order": {
"href": "/app_dev.php/api/orders/52"
}
},
"created_at": "2014-12-15T13:18:48+0000",
"id": 829,
"inventory_state": "checkout",
"updated_at": "2014-12-15T14:30:41+0000"
},
{
"_links": {
"order": {

2.1. The API Guide

57

Sylius, Release

"href": "/app_dev.php/api/orders/52"
}
},
"created_at": "2014-12-15T13:18:48+0000",
"id": 830,
"inventory_state": "checkout",
"updated_at": "2014-12-15T14:30:41+0000"

}
],
"quantity": 3,
"total": 1500000,
"unit_price": 500000,
"variant": {
"available_on": "2014-04-01T06:43:02+0000",
"created_at": "2014-12-03T09:54:35+0000",
"id": 779,
"master": true,
"object": {
"attributes": [
{
"id": 238,
"name": "Book author",
"presentation": "Author",
"value": "Marlen Yost"
},
{
"id": 239,
"name": "Book ISBN",
"presentation": "ISBN",
"value": "326ccbc7-92d1-3aec-b3af-df8afdc5651d"
},
{
"id": 240,
"name": "Book pages",
"presentation": "Number of pages",
"value": "149"
}
],
"created_at": "2014-12-03T09:54:35+0000",
"description": "Et eveniet voluptas ut magni vero temporibus nihil. Omnis possimu
"id": 101,
"name": "Book \"Quidem\" by \"Marlen Yost\"",
"options": [],
"short_description": "Distinctio quos est eaque fugit totam repellendus.",
"updated_at": "2014-12-03T09:54:35+0000"
},
"options": [],
"sku": "326ccbc7-92d1-3aec-b3af-df8afdc5651d",
"updated_at": "2014-12-03T09:54:35+0000"
}
}
],
"items_total": 1500000,
"payments": [
{
"_links": {
"order": {
"href": "/app_dev.php/api/orders/52"

58

Chapter 2. The API Guide

Sylius, Release

},
"payment-method": {
"href": "/app_dev.php/api/payment-methods/1"
},
"self": {
"href": "/app_dev.php/api/payments/51"
}
},
"amount": 1504750,
"created_at": "2014-12-15T15:02:54+0000",
"currency": "USD",
"id": 51,
"method": {
"_links": {
"self": {
"href": "/app_dev.php/api/payment-methods/1"
}
},
"created_at": "2014-12-03T09:54:28+0000",
"id": 1,
"name": "Dummy",
"updated_at": "2014-12-03T09:54:28+0000"
},
"state": "new",
"updated_at": "2014-12-15T15:02:55+0000"
}
],
"shipments": [
{
"_links": {
"method": {
"href": "/app_dev.php/api/shipping-methods/4"
},
"order": {
"href": "/app_dev.php/api/orders/52"
},
"self": {
"href": "/app_dev.php/api/shipments/51"
}
},
"created_at": "2014-12-15T14:30:40+0000",
"id": 51,
"method": {
"_links": {
"self": {
"href": "/app_dev.php/api/shipping-methods/4"
},
"zone": {
"href": "/app_dev.php/api/zones/4"
}
},
"calculator": "flexible_rate",
"category_requirement": 1,
"configuration": {
"additional_item_cost": 500,
"additional_item_limit": 10,
"first_item_cost": 4000
},

2.1. The API Guide

59

Sylius, Release

"created_at": "2014-12-03T09:54:28+0000",
"enabled": true,
"id": 4,
"name": "FedEx World Shipping",
"updated_at": "2014-12-03T09:54:28+0000"
},
"state": "checkout",
"updated_at": "2014-12-15T14:30:41+0000"
}
],
"shipping_address": {
"_links": {
"country": {
"href": "/app_dev.php/api/countries/9"
}
},
"city": "New York",
"created_at": "2014-12-15T13:37:28+0000",
"first_name": "John",
"id": 105,
"last_name": "Doe",
"postcode": "12435",
"street": "Test",
"updated_at": "2014-12-15T13:37:29+0000"
},
"state": "cart",
"total": 1504750,
"updated_at": "2014-12-15T15:02:55+0000",
"user": {
"credentials_expired": false,
"email": "xschaefer@example.com",
"email_canonical": "xschaefer@example.com",
"enabled": true,
"expired": false,
"groups": [],
"id": 5,
"locked": false,
"roles": [],
"username": "xschaefer@example.com",
"username_canonical": "xschaefer@example.com"
}
}

This is how your final order looks, if you are happy with that response, simply call another PUT to confirm the
checkout, which will became a real order and appear in the backend.
PUT /api/checkouts/44

Response

Final response contains the full order information, now you can call the purchase action to actually pay for the order.
STATUS: 200 OK
{"to": "do"}

60

Chapter 2. The API Guide

Sylius, Release

Purchase step
TODO.
PUT /api/checkouts/44

Parameters

type Card type


cardholderName Card holder name
number Card number
securityCode Card security code
expiryMonth Month expire number
expiryYear Year of card expiration
Response

You can check the payment status in the payment lists on order response.
STATUS: 200 OK
{"to": "do"}

2.1.6 Products API


Sylius products catalogue API endpoint is /api/products and it allows for browsing, creating & editing product information.
Index of all products
To browse all products available in the store you should call the following GET request:
GET /api/products/

Parameters

page Number of the page, by default = 1


limit Number of items to display per page
criteria[channel] Id of the channel (optional)
criteria[name] Name of the product (optional)

2.1. The API Guide

61

Sylius, Release

Response

Response will contain a paginated list of products.


STATUS: 200 OK
{

"page":1,
"limit":10,
"pages":12,
"total":120,
"_links":{
"self":{
"href":"\/api\/products\/?page=1"
},
"first":{
"href":"\/api\/products\/?page=1"
},
"last":{
"href":"\/api\/products\/?page=12"
},
"next":{
"href":"\/api\/products\/?page=2"
}
},
"_embedded":{
"items":[
{
"created_at": "2014-11-26T23:00:20+0000",
"description": "Facere ipsum id eveniet rem omnis et. Totam vero eos eveniet nihil si
"id": 2173,
"masterVariant": {
"available_on": "2014-03-29T01:30:04+0000",
"created_at": "2014-11-26T23:00:20+0000",
"id": 13403,
"master": true,
"options": [],
"sku": "68051",
"updated_at": "2014-11-26T23:00:20+0000"
},
"name": "T-Shirt \"ipsam\"",
"short_description": "Aut rerum quasi neque.",
"updated_at": "2014-11-26T23:00:20+0000"
}
]
}
}

Getting a single product


You can view a single product by executing the following request:
GET /api/products/2173

62

Chapter 2. The API Guide

Sylius, Release

Response
STATUS: 200 OK
{

"created_at": "2014-11-26T23:00:20+0000",
"description": "Facere ipsum id eveniet rem omnis et. Totam vero eos eveniet nihil sint. Labore o
"id": 2173,
"masterVariant": {
"available_on": "2014-03-29T01:30:04+0000",
"created_at": "2014-11-26T23:00:20+0000",
"id": 13403,
"master": true,
"options": [],
"sku": "68051",
"updated_at": "2014-11-26T23:00:20+0000"
},
"name": "T-Shirt \"ipsam\"",
"short_description": "Aut rerum quasi neque.",
"updated_at": "2014-11-26T23:00:20+0000"
}

Create an product
To create a new product, you can execute the following request:
POST /api/products/

Parameters

name Name of the product


description Description of the product
price Price of the product
shortDescription (optional) Short description of the product (for lists)
Response
STATUS: 201 CREATED
{
"created_at": "2014-11-29T14:23:57+0000",
"description": "Bar",
"id": 2181,
"masterVariant": {
"available_on": "2014-11-29T14:23:57+0000",
"created_at": "2014-11-29T14:23:57+0000",
"id": 13468,
"master": true,
"options": [],
"updated_at": "2014-11-29T14:23:58+0000"
},
"name": "Foo",

2.1. The API Guide

63

Sylius, Release

"updated_at": "2014-11-29T14:23:58+0000"
}

Updating a product
You can update an existing product using PUT or PATCH method:
PUT /api/products/2181
PATCH /api/products/2181

Parameters

name Name of the product


description Description of the product
price Price of the product
shortDescription (optional) Short description of the product (for lists)
Response
STATUS: 204 NO CONTENT

Deleting a product
You can delete (soft) a product from the catalog by making the following DELETE call:
DELETE /api/products/24

Response
STATUS: 204 NO CONTENT

2.1.7 Users API


Sylius users API endpoint is /api/users and it allows for browsing, creating & editing user data.
Index of all users
To browse all users available in the store you should call the following GET request:
GET /api/users/

64

Chapter 2. The API Guide

Sylius, Release

Parameters

page Number of the page, by default = 1


limit Number of items to display per page
criteria[query] Username, email or first & last names
Response

Response will contain a paginated list of users.


STATUS: 200 OK
{

"page":1,
"limit":10,
"pages":10,
"total":100,
"_links":{
"self":{
"href":"\/api\/users\/?page=1"
},
"first":{
"href":"\/api\/users\/?page=1"
},
"last":{
"href":"\/api\/users\/?page=12"
},
"next":{
"href":"\/api\/users\/?page=2"
}
},
"_embedded":{
"items":[
{
"credentials_expired": false,
"email": "chelsie.witting@example.com",
"email_canonical": "chelsie.witting@example.com",
"enabled": true,
"expired": false,
"groups": [],
"id": 481,
"locked": false,
"password": "EbOLtGHYxJKotA+bdb9BElhXPd8qZsnlo8CjDdCk+qFR22EEZJoOTntBX/M5GUXw2vnEqOKI
"roles": [],
"salt": "h9ltmmawvdsk08oocogkws4sg040k04",
"username": "chelsie.witting@example.com",
"username_canonical": "chelsie.witting@example.com"
}
]
}
}

Getting a single user


You can view a single user by executing the following request:
2.1. The API Guide

65

Sylius, Release

GET /api/users/481

Response
STATUS: 200 OK
{

"credentials_expired": false,
"email": "chelsie.witting@example.com",
"email_canonical": "chelsie.witting@example.com",
"enabled": true,
"expired": false,
"groups": [],
"id": 481,
"locked": false,
"password": "EbOLtGHYxJKotA+bdb9BElhXPd8qZsnlo8CjDdCk+qFR22EEZJoOTntBX/M5GUXw2vnEqOKIEVPaJr66yxXq
"roles": [],
"salt": "h9ltmmawvdsk08oocogkws4sg040k04",
"username": "chelsie.witting@example.com",
"username_canonical": "chelsie.witting@example.com"
}

Create an user
To create a new user, you can execute the following request:
POST /api/users/

Parameters

firstName Firstname of the customer


lastName Lastname of the customer
email User e-mail
plainPassword Password string
enabled (optional) User account status (boolean)
Response
STATUS: 201 CREATED
{

"credentials_expired": false,
"email": "chelsie.witting@example.com",
"email_canonical": "chelsie.witting@example.com",
"enabled": true,
"expired": false,
"groups": [],
"id": 481,
"locked": false,
"password": "EbOLtGHYxJKotA+bdb9BElhXPd8qZsnlo8CjDdCk+qFR22EEZJoOTntBX/M5GUXw2vnEqOKIEVPaJr66yxXq

66

Chapter 2. The API Guide

Sylius, Release

"roles": [],
"salt": "h9ltmmawvdsk08oocogkws4sg040k04",
"username": "chelsie.witting@example.com",
"username_canonical": "chelsie.witting@example.com"
}

Updating a user
You can update an existing user using PUT or PATCH method:
PUT /api/users/481
PATCH /api/users/481

Parameters

firstName Firstname of the customer


lastName Lastname of the customer
email User e-mail
plainPassword Password string
enabled (optional) User account status (boolean)
Response
STATUS: 204 NO CONTENT

Deleting a user
You can delete (soft) a user from the system by making the following DELETE call:
DELETE /api/users/24

Response
STATUS: 204 NO CONTENT

Request password resetting


You can create a new password resetting request by calling the following API endpoint:
POST /api/password-resetting-requests/

Parameters

username Username or e-mail

2.1. The API Guide

67

Sylius, Release

Response

The successful response will contain the user object with a confirmation token and date of password request.
STATUS: 200 OK
{
"confirmation_token": "dzOeNrmdnn20IVHBW2Uaq-yAYsO2sY2hCXhfKdYl_xM",
"credentials_expired": false,
"email": "sylius@example.com",
"email_canonical": "sylius@example.com",
"enabled": true,
"expired": false,
"groups": [],
"id": 1,
"last_login": "2014-12-08T13:08:02+0000",
"locked": false,
"password_requested_at": "2014-12-08T14:19:26+0000",
"roles": [
"ROLE_SYLIUS_ADMIN"
],
"username": "sylius@example.com",
"username_canonical": "sylius@example.com"
}

Index of all user orders


To browse all orders for specific user, you can do the following call:
GET /api/users/14/orders/

Parameters

page Number of the page, by default = 1


limit Number of items to display per page

2.1.8 Shipments API


Sylius shipments API endpoint is /api/shipments.
Index of all shipments
You can retrieve the full list shipment by making the following request:
GET /api/shipments/

Parameters

page Number of the page, by default = 1


limit Number of items to display per page

68

Chapter 2. The API Guide

Sylius, Release

criteria[channel] (optional) The channel id


criteria[stockLocation] (optional) The id of stock location
criteria[number] (optional) The order number
criteria[shippingAddress] (optional) First or last name of the customer ship to address
criteria[createdAtFrom] (optional) Starting date
criteria[createdAtTo] (optional) End date
Response
STATUS: 200 OK
{
"page":1,
"limit":10,
"pages":8,
"total":80,
"_links":{
"self":{
"href":"\/api\/shipments\/?page=1"
},
"first":{
"href":"\/api\/shipments\/?page=1"
},
"last":{
"href":"\/api\/shipments\/?page=12"
},
"next":{
"href":"\/api\/shipments\/?page=2"
}
},
"_embedded":{
"items":[
{
"_links": {
"method": {
"href": "/api/shipping-methods/120"
},
"order": {
"href": "/api/orders/302"
},
"self": {
"href": "/api/shipments/251"
}
},
"created_at": "2014-11-26T23:00:34+0000",
"id": 251,
"method": {
"_links": {
"self": {
"href": "/api/shipping-methods/120"
},
"zone": {
"href": "/api/zones/120"
}

2.1. The API Guide

69

Sylius, Release

},
"calculator": "flexible_rate",
"category_requirement": 1,
"configuration": {
"additional_item_cost": 500,
"additional_item_limit": 10,
"first_item_cost": 4000
},
"created_at": "2014-11-26T23:00:15+0000",
"enabled": true,
"id": 120,
"name": "FedEx World Shipping",
"updated_at": "2014-11-26T23:00:15+0000"
},
"state": "backordered",
"updated_at": "2014-11-26T23:00:34+0000"
}
]
}
}

Getting a single shipment


You can view a single shipment by executing the following request:
GET /api/shipments/251

Response
STATUS: 200 OK
{
"_links": {
"method": {
"href": "/api/shipping-methods/120"
},
"order": {
"href": "/api/orders/302"
},
"self": {
"href": "/api/shipments/251"
}
},
"created_at": "2014-11-26T23:00:34+0000",
"id": 251,
"method": {
"_links": {
"self": {
"href": "/api/shipping-methods/120"
},
"zone": {
"href": "/api/zones/120"
}
},
"calculator": "flexible_rate",

70

Chapter 2. The API Guide

Sylius, Release

"category_requirement": 1,
"configuration": {
"additional_item_cost": 500,
"additional_item_limit": 10,
"first_item_cost": 4000
},
"created_at": "2014-11-26T23:00:15+0000",
"enabled": true,
"id": 120,
"name": "FedEx World Shipping",
"updated_at": "2014-11-26T23:00:15+0000"
},
"state": "backordered",
"updated_at": "2014-11-26T23:00:34+0000"
}

Deleting a shipment
You can delete a shipment from the system by making the following DELETE call:
DELETE /api/shipments/24

Response
STATUS: 204 NO CONTENT

2.1.9 Payments API


Sylius payment API endpoint is /api/payments.
Index of all payments
You can retrieve the full list payment by making the following request:
GET /api/payments/

Parameters

page Number of the page, by default = 1


limit Number of items to display per page
criteria[channel] (optional) The channel id
criteria[number] (optional) The order number
criteria[billingAddress] (optional) First or last name of the customer bill to address
criteria[createdAtFrom] (optional) Starting date
criteria[createdAtTo] (optional) End date

2.1. The API Guide

71

Sylius, Release

Response
STATUS: 200 OK
{
"page":1,
"limit":10,
"pages":8,
"total":80,
"_links":{
"self":{
"href":"\/api\/payments\/?page=1"
},
"first":{
"href":"\/api\/payments\/?page=1"
},
"last":{
"href":"\/api\/payments\/?page=12"
},
"next":{
"href":"\/api\/payments\/?page=2"
}
},
"_embedded":{
"items":[
{"to": "do"}
]
}
}

Getting a single payment


You can view a single payment by executing the following request:
GET /api/payments/251

Response
STATUS: 200 OK
{"to": "do"}

Deleting a payment
You can delete a payment from the system by making the following DELETE call:
DELETE /api/payments/99

Response
STATUS: 204 NO CONTENT

72

Chapter 2. The API Guide

Sylius, Release

2.1.10 Promotions API


Sylius promotion API endpoint is /api/promotions.
Index of all promotions
You can retrieve the full list of promotionns by making the following request:
GET /api/promotions

Parameters

page Number of the page, by default = 1


limit Number of items to display per page
Response
STATUS: 200 OK
{
"page":1,
"limit":10,
"pages":1,
"total":3,
"_links":{
"self":{
"href":"\/api\/promotions\/?page=1"
},
"first":{
"href":"\/api\/promotions\/?page=1"
},
"last":{
"href":"\/api\/promotions\/?page=12"
},
"next":{
"href":"\/api\/promotions\/?page=2"
}
},
"_embedded":{
"items":[
{
"_links": {
"coupons": {
"href": "/app_dev.php/api/promotions/1/coupons/"
},
"self": {
"href": "/app_dev.php/api/promotions/1"
}
},
"actions": [
{
"configuration": {
"amount": 500
},

2.1. The API Guide

73

Sylius, Release

"id": 1,
"type": "fixed_discount"
}
],
"coupon_based": false,
"created_at": "2014-12-03T09:54:28+0000",
"exclusive": false,
"id": 1,
"name": "New Year",
"priority": 0,
"rules": [
{
"configuration": {
"count": 3,
"equal": true
},
"id": 1,
"type": "item_count"
}
],
"updated_at": "2014-12-03T09:54:28+0000",
"used": 0
}
]
}
}

Getting a single promotion


You can view a single promotion by executing the following request:
GET /api/promotions/1

Response
STATUS: 200 OK
{
"_links": {
"coupons": {
"href": "/app_dev.php/api/promotions/1/coupons/"
},
"self": {
"href": "/app_dev.php/api/promotions/1"
}
},
"actions": [
{
"configuration": {
"amount": 500
},
"id": 1,
"type": "fixed_discount"
}
],

74

Chapter 2. The API Guide

Sylius, Release

"coupon_based": false,
"created_at": "2014-12-03T09:54:28+0000",
"exclusive": false,
"id": 1,
"name": "New Year",
"priority": 0,
"rules": [
{
"configuration": {
"count": 3,
"equal": true
},
"id": 1,
"type": "item_count"
}
],
"updated_at": "2014-12-03T09:54:28+0000",
"used": 0
}

Deleting a promotion
You can delete a promotion from the system by making the following DELETE call:
DELETE /api/promotions/1

Response
STATUS: 204 NO CONTENT

Listing all coupons


You can get the coupons associated with given promotion by performing the following request:
GET /api/promotions/1/coupons

Parameters

page Number of the page, by default = 1


limit Number of items to display per page
Response
STATUS: 200 OK
{
"_embedded": {
"items": [
{
"_links": {

2.1. The API Guide

75

Sylius, Release

"promotion": {
"href": "/api/promotions/1"
},
"self": {
"href": "/api/promotions/1/coupons/1"
}
},
"code": "XAETWESF",
"id": 1,
"usage_limit": 1,
"used": 0
}
]
},
"_links": {
"first": {
"href": "/api/promotions/1/coupons/?page=1&limit=10"
},
"last": {
"href": "/api/promotions/1/coupons/?page=1&limit=10"
},
"self": {
"href": "/api/promotions/1/coupons/?page=1&limit=10"
}
},
"limit": 10,
"page": 1,
"pages": 1,
"total": 1
}

Adding new coupon


To create a new coupon for given promotion, you can execute the following request:
POST /api/promotion/1/coupons/

Parameters

code Coupon code


usageLimit The number of times that coupon can be used
Response
STATUS: 201 CREATED
{
"_links": {
"promotion": {
"href": "/api/promotions/1"
},
"self": {
"href": "/api/promotions/1/coupons/2"

76

Chapter 2. The API Guide

Sylius, Release

}
},
"code": "SUPER-AWESOME-SALE",
"id": 1,
"usage_limit": 3,
"used": 0
}

2.1.11 StockLocation API


Sylius stock locations API endpoint is /api/stock-locations.
Index of all stock locations
To browse all stock locations configured, use the following request:
GET /api/stock-locations/

Parameters

page Number of the page, by default = 1


limit Number of items to display per page
Response

Response will contain a paginated list of stock locations.


STATUS: 200 OK
{
"_embedded": {
"items": [
{
"_links": {
"self": {
"href": "/api/stock-locations/57"
}
},
"address": {
"_links": {
"country": {
"href": "/api/countries/7517"
}
},
"city": "Naderton",
"created_at": "2014-11-26T23:00:21+0000",
"first_name": "Sabrina",
"id": 519,
"last_name": "Roberts",
"postcode": "45449-6358",
"street": "75382 Larkin Junctions",
"updated_at": "2014-11-26T23:00:21+0000"

2.1. The API Guide

77

Sylius, Release

},
"code": "LONDON-1",
"created_at": "2014-11-26T23:00:21+0000",
"enabled": true,
"id": 57,
"name": "London Werehouse",
"updated_at": "2014-11-26T23:00:21+0000"
}
]
},
"_links": {
"first": {
"href": "/api/stock-locations/?page=1&limit=10"
},
"last": {
"href": "/api/stock-locations/?page=1&limit=10"
},
"self": {
"href": "/api/stock-locations/?page=1&limit=10"
}
},
"limit": 10,
"page": 1,
"pages": 1,
"total": 4
}

Getting a single stock location


You can view a single stock location by executing the following request:
GET /api/stock-locations/519

Response
STATUS: 200 OK
{
"_links": {
"self": {
"href": "/api/stock-locations/57"
}
},
"address": {
"_links": {
"country": {
"href": "/api/countries/7517"
}
},
"city": "Naderton",
"created_at": "2014-11-26T23:00:21+0000",
"first_name": "Sabrina",
"id": 519,
"last_name": "Roberts",
"postcode": "45449-6358",

78

Chapter 2. The API Guide

Sylius, Release

"street": "75382 Larkin Junctions",


"updated_at": "2014-11-26T23:00:21+0000"
},
"code": "LONDON-1",
"created_at": "2014-11-26T23:00:21+0000",
"enabled": true,
"id": 57,
"name": "London Werehouse",
"updated_at": "2014-11-26T23:00:21+0000"
}

Create a new stock location


To create a new stock location, you must execute the following request:
POST /api/stock-locations/

Parameters

code Unique code


name The name of location
enabled (optional) Is enabled? (boolean)
Response
STATUS: 201 CREATED
{
"_links": {
"self": {
"href": "/api/stock-locations/58"
}
},
"code": "LONDON-2",
"created_at": "2014-11-26T23:00:21+0000",
"enabled": true,
"id": 58,
"name": "London Werehouse II",
"updated_at": "2014-11-26T23:00:21+0000"
}

Updating a stock location


You can update an existing stock location using PUT or PATCH method:
PUT /api/stock-locations/92
PATCH /api/stock-locations/92

2.1. The API Guide

79

Sylius, Release

Parameters

code Unique code


name The name of location
enabled (optional) Is enabled? (boolean)
Response
STATUS: 204 NO CONTENT

Deleting a stock location


You can remove a stock location from the system by making the following DELETE call:
DELETE /api/stock-locations/92

Response
STATUS: 204 NO CONTENT

Introduction to Sylius REST API


Authorization
Channels API
Orders API
Checkouts API
Products API
Users API
Shipments API
Payments API
Promotions API
StockLocation API
Introduction to Sylius REST API
Authorization
Channels API
Orders API
Checkouts API
Products API
Users API
Shipments API
Payments API

80

Chapter 2. The API Guide

Sylius, Release

Promotions API
StockLocation API

2.1. The API Guide

81

Sylius, Release

82

Chapter 2. The API Guide

CHAPTER 3

The User Guide

The Users guide around the Sylius interface and configuration.

3.1 The User Guide


3.1.1 Installation
There are several ways to install Sylius.
Either youre installing it to contribute, in which case you may prefer Sylius/Sylius, or youre bootstrapping a new
e-commerce project, and youd prefer using Sylius/Sylius-Standard.
Warning: Why two versions ? The reason is simple: Sylius/Sylius is the central repository, where all code and
commits are contributed to. All the other repositories are splitted from this main repository.
Sylius-Standard is just a distribution including these splitted repositories.

Using Composer
We assume youre familiar with Composer, a dependency manager for PHP. Otherwise, check how to install Composer.
$ composer create-project -s dev sylius/sylius # or sylius/sylius-standard
$ cd sylius # or sylius-standard
$ php app/console sylius:install

Using Git
$
$
$
$

git clone git@github.com:Sylius/Sylius.git # or Sylius-Standard


cd Sylius # or Sylius-Standard
composer install
php app/console sylius:install

Installation
Installation

83

Sylius, Release

84

Chapter 3. The User Guide

CHAPTER 4

Cookbook

Specific solutions for specific needs.

4.1 Cookbook
4.1.1 Using the Installer commands
Sylius platform ships with the sylius:install command, which takes care of creating the database, schema,
dumping the assets and basic store configuration.
This command actually uses several other commands behind the scenes and each of those is available for you:
Checking system requirements
You can quickly check all your system requirements and possible recommendations by calling the following command:
$ php app/console sylius:install:check-requirements

Database configuration
Sylius can create or even reset the database/schema for you, simply call:
$ php app/console sylius:install:database

The command will check if your database schema exists. If yes, you may decide to recreate it from scratch, otherwise
Sylius will take care of this automatically. It also allows you to load sample data.
Loading sample data
You can load sample data by calling the following command:
$ php app/console sylius:install:sample-data

85

Sylius, Release

Basic store configuration


To configure your store, use this command and answer all questions:
$ php app/console sylius:install:setup

Installing assets and dumping Assetic


You can reinstall all web assets by simply calling:
$ php app/console sylius:install:assets

4.1.2 Extending the menu


You
can
add
entries
to
the
menu
via
events
easily.
:class:Sylius\\Bundle\\WebBundle\\Event\\MenuBuilderEvent
with
ItemInterface of KnpMenu. So you can manipulate the whole menu.

You
get
passed
a
FactoryInterface
and

Available for the backend and frontend menus, by listening/subscribing to any of the event constants defined in
Sylius\Bundle\WebBundle\Event\MenuBuilderEvent.
Example Usage
// src/Acme/ReportsBundle/EventListener/MenuBuilderListener.php
namespace Acme\ReportsBundle\EventListener;
use Sylius\Bundle\WebBundle\Event\MenuBuilderEvent;
class MenuBuilderListener
{
public function addBackendMenuItems(MenuBuilderEvent $event)
{
$menu = $event->getMenu();
$menu['sales']->addChild('reports', array(
'route' => 'acme_reports_index',
'labelAttributes' => array('icon' => 'glyphicon glyphicon-stats'),
))->setLabel('Daily and monthly reports');
}
}

YAML

services:
acme_reports.menu_builder:
class: Acme\ReportsBundle\EventListener\MenuBuilderListener
tags:
- { name: kernel.event_listener, event: sylius.menu_builder.backend.main, method: ad
- { name: kernel.event_listener, event: sylius.menu_builder.backend.sidebar, method:

XML
<!-- src/Acme/ReportsBundle/Resources/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"

86

Chapter 4. Cookbook

Sylius, Release

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/ser

<services>
<service id="acme_reports.menu_builder" class="Acme\ReportsBundle\EventListener\MenuBuil
<tag name="kernel.event_listener" event="sylius.menu_builder.backend.main" method="a
<tag name="kernel.event_listener" event="sylius.menu_builder.backend.sidebar" method
</service>
</services>
</container>

PHP
// src/Acme/ReportsBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;

$definition = new Definition('Acme\ReportsBundle\EventListener\MenuBuilderListener');


$definition->->addTag('kernel.event_listener', array('event' => 'sylius.menu_builder.backend.mai
$definition->->addTag('kernel.event_listener', array('event' => 'sylius.menu_builder.backend.sid
$container->setDefinition('acme_reports.menu_builder', $definition);

4.1.3 Using the registry in your bundle


We will to show you how to set up the sylius registry. In this example, we will register two price calculators.
<!-- Resources/config/services.xml -->
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services
<parameters>
<parameter key="app.price.calculator.registry.class">App\Component\Registry\ServiceRegistry</
<parameter key="app.price.calculator.interface">App\Bundle\MyBundle\PriceCalculatorInterface<
</parameters>
<services>
<!-- You need to declare the registry as a service, it expect as argument the type of object
<service id="app.price.calculator.registry" class="%app.price.calculator.registry.class%">
<argument>%app.price.calculator.interface%</argument>
</service>

<!-- Don't forget that a registry don't create object. You need to create them before and reg
<service id="app.price.calculator.default" class="...">
<service id="app.price.calculator.custom" class="...">
</services>
</container>
namespace App\Bundle\MyBundle\DependencyInjection\Compiler;
class RegisterCalculatorServicePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
// You can check if your registry is defined

4.1. Cookbook

87

Sylius, Release

if (!$container->hasDefinition('app.price.calculator.registry')) {
return;
}
$registryDefinition = $container->getDefinition('app.price.calculator.registry');
// You can register your services like this
$registryDefinition->addMethodCall(
'register',
array(
'default',
new Reference('app.price.calculator.default'),
)
);
$registryDefinition->addMethodCall(
'register',
array(
'custom',
new Reference('app.price.calculator.default'),
)
);
}
}

Finally, you need to register your custom pass with the container
namespace App\Bundle\MyBundle;
use App\Bundle\MyBundle\DependencyInjection\Compiler\RegisterCalculatorServicePass;
class AppMyBundleBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new RegisterCalculatorServicePass());
}
}

Using the Installer commands


Extending the menu
Using the registry in your bundle
Using the Installer commands
Extending the menu
Using the registry in your bundle

88

Chapter 4. Cookbook

CHAPTER 5

Integrations

Learn about how Sylius integrates with third-party applications.

5.1 Integrations
Welcome to Sylius Integrations, where you can learn about all available connectors, bridges and integrations with
other applications.

5.1.1 Akeneo PIM


Akeneo is a Product Information Management tool built on top of Symfony.
Installation
...
Usage
...
Akeneo PIM
Akeneo PIM

89

Sylius, Release

90

Chapter 5. Integrations

CHAPTER 6

Migration Guide

Migrating to Sylius from other e-commerce platforms.

6.1 The Migration Guide


6.1.1 About Migration Guide
This section of Sylius documentation is about migrating to Sylius from existing solutions.
About Migration Guide
About Migration Guide

91

Sylius, Release

92

Chapter 6. Migration Guide

CHAPTER 7

Bundles

Documentation of all Sylius bundles.

7.1 Symfony2 Ecommerce Bundles


7.1.1 Bundles General Guide
All Sylius bundles share the same architecture and this guide will introduce you these conventions.
Youll learn how to perform basic CRUD operations on the data and how to override models, controllers, repositories,
validation mapping and forms.
Performing basic CRUD operations
Sylius is using the Doctrine Common persistence interfaces. This means that every model within Sylius bundles has
its own repository and object manager.
Some interfaces extend the Timestampable and SoftDeletable interfaces. Those interfaces are defined in the SyliusResourceBundle to not create a dependency on Doctrine ORM. They are however compatible with the GedmoDoctrineExtensions when using Doctrine ORM.
Retrieving resources

Retrieving any resource from database always happens via the repository, which implements
Sylius\Bundle\ResourceBundle\Model\RepositoryInterface. If you have been using Doctrine, you should already be familiar with this concept, as it extends the default Doctrine ObjectRepository
interface.
Lets assume you want to load a product from database. Your product repository is always accessible via the
sylius.repository.product service.
<?php
public function myAction()
{
$repository = $this->container->get('sylius.repository.product');
}

Retrieving many resources is as simple as calling the proper methods on the repository.

93

Sylius, Release

<?php
public function myAction()
{
$repository = $this->container->get('sylius.repository.product');

$product = $repository->find(4); // Get product with id 4, returns null if not found.


$product = $repository->findOneBy(array('slug' => 'my-super-product')); // Get one product by def
$products = $repository->findAll(); // Load all the products!
$products = $repository->findBy(array('special' => true)); // Find products matching some custom
}

Every Sylius repository supports paginating products. To create a Pagerfanta instance use the createPaginator
method.
<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.product');
$products = $repository->createPaginator();
$products->setMaxPerPage(3);
$products->setCurrentPage($request->query->get('page', 1));

// Now you can returns products to template and iterate over it to get products from current page
}

Paginator can be created for a specific criteria and with desired sorting.
<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.product');
$products = $repository->createPaginator(array('foo' => true), array('createdAt' => 'desc'));
$products->setMaxPerPage(3);
$products->setCurrentPage($request->query->get('page', 1));
}

Creating a new resource object

To create a new resource instance, you can simply call the createNew() method on the repository.
Now lets try something else than product, well create a new TaxRate model.
<?php
public function myAction()
{
$repository = $this->container->get('sylius.repository.tax_rate');
$taxRate = $repository->createNew();
}

Note: Creating resources via this factory method makes the code more testable, and allows you to change the model
class easily.
94

Chapter 7. Bundles

Sylius, Release

Saving and removing resources

To save or remove a resource, you can use any ObjectManager which is capable of managing the class. Every
model has its own manager alias, for example the sylius.manager.address is an alias to the ORM EntityManager.
Of course, it is also perfectly fine if you use the doctrine.orm.entity_manager service name or any other
appropriate manager service.
<?php
public function myAction()
{
$repository = $this->container->get('sylius.repository.address');
$manager = $this->container->get('sylius.manager.address'); // Alias to the appropriate doctrine
$address = $repository->createNew();
$address
->setFirstname('John')
->setLastname('Doe')
;
$manager->persist($address);
$manager->flush(); // Save changes in database.
}

To remove a resource, you also use the manager.


<?php
public function myAction()
{
$repository = $this->container->get('sylius.repository.shipping_method');
$manager = $this->container->get('sylius.manager.shipping_method');
$shippingMethod = $repository->findOneBy(array('name' => 'DHL Express'));
$manager->remove($shippingMethod);
$manager->flush(); // Save changes in database.
}

Overriding Models
...
Extending base Models

All Sylius models live in Sylius\Component\Xyz\Model namespace together with the interfaces. As an example, for Sylius Taxation Component its TaxCategory and TaxRate.
Lets assume you want to add zone field to the Sylius tax rates.
Firstly, you need to create your own TaxRate class, which will extend the base model.

7.1. Symfony2 Ecommerce Bundles

95

Sylius, Release

namespace Acme\Bundle\ShopBundle\Entity;
use Sylius\Component\Addressing\Model\ZoneInterface;
use Sylius\Component\Taxation\Model\TaxRate as BaseTaxRate;
class TaxRate extends BaseTaxRate
{
private $zone;
public function getZone()
{
return $this->zone;
}
public function setZone(ZoneInterface $zone)
{
$this->zone = $zone;
return $this;
}
}

Secondly, define the entity mapping inside Resources/config/doctrine/TaxRate.orm.xml of your bundle.


<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Acme\ShopBundle\Entity\TaxRate" table="sylius_tax_rate">
<many-to-one field="zone" target-entity="Sylius\Component\Addressing\Model\ZoneInterface">
<join-column name="zone_id" referenced-column-name="id" nullable="false" />
</many-to-one>
</entity>
</doctrine-mapping>

Finally, you configure your class in app/config/config.yml file.


sylius_taxation:
driver: doctrine/orm
classes:
tax_rate:
model: Acme\ShopBundle\Entity\TaxRate # Your tax rate entity.

Done! Sylius will now use your TaxRate model!


What has happened?
Parameter sylius.model.tax_rate.class contains Acme\\Bundle\\ShopBundle\\Entity\\TaxRate.
sylius.repository.tax_rate represents Doctrine repository for your new class.
sylius.manager.tax_rate represents Doctrine object manager for your new class.
sylius.controller.tax_rate represents the controller for your new class.

96

Chapter 7. Bundles

Sylius, Release

All Doctrine relations to Sylius\\Component\\Taxation\\Model\\TaxRateInterface are using your new class as target-entity, you do not need to update any mappings.
TaxRateType form type is using your model as data_class.
Sylius\\Component\\Taxation\\Model\\TaxRate is automatically turned into Doctrine Mapped
Superclass.
Overriding Controllers
All Sylius bundles are using SyliusResourceBundle as a foundation for database storage.
Extending base Controller

If you want to modify the controller or add your custom actions, you can do so by defining a new controller class. By
extending resource controller, you also get access to several handy methods. Lets add to our custom controller a new
method to recommend a product:
<?php
// src/Acme/ShopBundle/Controller/ProductController.php
namespace Acme\ShopBundle\Controller;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Symfony\Component\HttpFoundation\Request;

class ProductController extends ResourceController


{
public function recommendAction(Request $request, $id)
{
$product = $this->findOr404(array('id' => $id)); // Find product with given id or return 404!
$product->incrementRecommendations(); // Add +1!
$this->persistAndFlush($product); // Save product.
return $this->redirect($this->generateUrl('acme_shop_homepage'));
}
}

You also need to configure your controller class in app/config/config.yml.


# app/config/config.yml
sylius_product:
driver: doctrine/orm
classes:
product:
controller: Acme\ShopBundle\Controller\ProductController

Thats it! Now sylius.controller.product:recommendAction is available. You can use it by defining a


new route.
# app/config/routing.yml
acme_shop_product_recommend:
path: /products/{id}/recommend

7.1. Symfony2 Ecommerce Bundles

97

Sylius, Release

defaults:
_controller: sylius.controller.product:recommendAction

What has happened?

Parameter sylius.controller.product.class contains Acme\\Bundle\\ShopBundle\\Controller\\Prod


Controller service sylius.controller.product is using your new controller class.
Overriding Repositories
Overriding a Sylius model repository involves extending the base class and configuring it inside the bundle configuration.
Extending base Repository

Sylius is using both custom and default Doctrine repositories and often youll need to add your own methods. Lets
assume you want to find all orders for a given customer.
Firstly, you need to create your own repository class
<?php
// src/Acme/ShopBundle/Repository/OrderRepository.php
namespace Acme\ShopBundle\Repository;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
class OrderRepository extends EntityRepository
{
public function findByCustomer(Customer $customer)
{
return $this
->createQueryBuilder('o')
->join('o.billingAddress', 'billingAddress')
->join('o.shippingAddress', 'shippingAddress')
->join('o.customer', 'customer')
->where('o.customer = :customer')
->setParameter('customer', $customer)
->getQuery()
->getResult()
;
}
}

Secondly, need to configure your repository class in app/config/config.yml.


# app/config/config.yml
sylius_order:
driver: doctrine/orm
classes:
order:
repository: Acme\ShopBundle\Repository\OrderRepository

Thats it! Now sylius.repository.order is using your new class.

98

Chapter 7. Bundles

Sylius, Release

<?php
public function ordersAction()
{
$customer = // Obtain customer instance.
$repository = $this->container->get('sylius.repository.order');
$orders = $repository->findByCustomer($customer);
}

What has happened?

Parameter sylius.repository.order.class contains Acme\\ShopBundle\\Repository\\OrderRepositor


Repository service sylius.repository.order is using your new class.
Overriding Validation
All Sylius validation mappings and forms are using sylius as the default group.
Changing the validation group

You can configure your own validation for Sylius models.


validation.xml inside your bundle.

If the defaults do not fit your needs, create

<?xml version="1.0" encoding="UTF-8"?>

<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping
http://symfony.com/schema/dic/services/constraint-mapping-1.0
<class name="Sylius\Bundle\TaxationBundle\Model\TaxCategory">
<property name="name">
<constraint name="NotBlank">
<option name="message">Fill me in!</option>
<option name="groups">acme</option>
</constraint>
<constraint name="Length">
<option name="min">5</option>
<option name="max">255</option>
<option name="minMessage">Looonger!</option>
<option name="maxMessage">Shooorter!</option>
<option name="groups">acme</option>
</constraint>
</property>
</class>
</constraint-mapping>

You also need to configure the new validation group in app/config/config.yml.


sylius_taxation:
driver: doctrine/orm # Configure the doctrine orm driver used in documentation.
validation_groups:
tax_category: [acme]

7.1. Symfony2 Ecommerce Bundles

99

Sylius, Release

Done! Now all Sylius forms will use acme validation group on all forms of tax category.
Overriding Forms
Every form type in Sylius holds its class name in a specific parameter. This allows you to easily add or remove fields
by extending the base form class.
Extending base Forms

All Sylius form types live in Sylius\Bundle\XyzBundle\Form\Type namespace.


Lets assume you want to add phone and remove company fields to the Sylius address form.
You have to create your own AddressType class, which will extend the base form type.
namespace Acme\Bundle\ShopBundle\Form\Type;
use Sylius\Bundle\AddressingBundle\Form\Type\AddressType as BaseAddressType;
use Symfony\Component\Form\FormBuilderInterface;
class AddressType extends BaseAddressType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options); // Add default fields.
$builder->remove('company');
$builder->add('phone', 'text', array('required' => false));
}
}

Now, define the new form class in the app/config/config.yml.


# app/config/config.yml
sylius_addressing:
driver: doctrine/orm
classes:
address:
form:
default: Acme\ShopBundle\Form\Type\AddressType

Done! Sylius will now use your custom address form everywhere!
What has happened?

Parameter sylius.form.type.address.class contains Acme\\Bundle\\ShopBundle\\Form\\Type\\Addre


sylius.form.type.address form type service uses your custom class.
sylius_address form type uses your new form everywhere.
Mapping Relations
All Sylius bundles use the Doctrine RTEL functionality, which allows to map the relations using interfaces, not
implementations.

100

Chapter 7. Bundles

Sylius, Release

Configuration

In your AppKernel class, please register Sylius bundles before DoctrineBundle. This is important as we use listeners
which have to be processed first.

If you do not register bundles in this order, Doctrine will throw Doctrine\Common\Persistence\Mapping\MappingExcepti
exceptions about missing classes.
Using interfaces

When defining relation to Sylius model, use the interface name instead of concrete class. It will be automatically
replaced with the configured model, enabling much greater flexibility.
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">

<entity name="Acme\ShopBundle\Entity\Product" table="sylius_product">


<many-to-one field="taxCategory" target-entity="Sylius\Bundle\TaxationBundle\Model\TaxCategor
<join-column name="tax_category_id" referenced-column-name="id" nullable="true" />
</many-to-one>
</entity>
</doctrine-mapping>

Thanks to this approach, if the TaxCategory model has changed, you do not need to worry about remapping other
entities.
Events
All Sylius bundles are using SyliusResourceBundle, which has some built-in events.
Events reference

Event
sylius.<resource>.pre_create
sylius.<resource>.create
sylius.<resource>.post_create
sylius.<resource>.pre_update
sylius.<resource>.update
sylius.<resource>.post_update
sylius.<resource>.pre_delete
sylius.<resource>.delete
sylius.<resource>.post_delete

Description
Before persist
After persist
After flush
Before persist
After persist
After flush
Before remove
After remove
After flush

CRUD events example

Lets take the Product model as an example. As you should already know, the product controller is represented by the
sylius.controller.product service. Several useful events are dispatched during execution of every default
7.1. Symfony2 Ecommerce Bundles

101

Sylius, Release

action of this controller. When creating a new resource via the createAction method, 3 events occur.
First, before the persist() is called on the product, the sylius.product.pre_create event is dispatched.
Secondly, just before the database flush is performed, Sylius dispatches the sylius.product.create event.
Finally, after the data storage is updated, sylius.product.post_create is triggered.
The same set of events is available for the update and delete operations. All the dispatches are using the
GenericEvent class and return the Product object by the getSubject method.
Registering a listener

Well stay with the Product model and create a listener which updates Solr (search engine) document every time a
product is updated.
namespace Acme\ShopBundle\EventListener;
use Symfony\Component\EventDispatcher\GenericEvent;
class SolrListener
{
// ... Constructor with indexer injection code.
public function onProductUpdate(GenericEvent $event)
{
$this->indexer->updateProductDocument($event->getSubject());
}
}

Now you need to register the listener in services configuration.


<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="acme.listener.solr" class="Acme\ShopBundle\EventListener\SolrListener">
<tag name="kernel.event_listener" event="sylius.product.post_update" method="onProductUpd
</service>
</services>
</container>

Done! Every time a product is edited and the database updated, this listener will also use the indexer to update Solr
index accordingly.

7.1.2 SyliusAddressingBundle
This bundle integrates the Addressing into Symfony2 and Doctrine.
With minimal configuration you can introduce addresses, countries, provinces and zones management into your
project. Its fully customizable, but the default setup should be optimal for most use cases.
It also contains zone matching mechanisms, which allow you to match customer addresses to appropriate tax/shipping
(or any other) zones. There are several models inside the bundle, Address, Country, Province, Zone and ZoneMember.
102

Chapter 7. Bundles

Sylius, Release

There is also a ZoneMatcher service. Youll get familiar with it in later parts of this documentation.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use following command to add the bundle
to your composer.json and download package.
If you have Composer installed globally.
$ composer require sylius/addressing-bundle:*

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require sylius/addressing-bundle:*

Adding required bundles to the kernel

You need to enable the bundle inside the kernel.


If youre not using any other Sylius bundles, you will also need to add SyliusResourceBundle and its dependencies to
kernel. Dont worry, everything was automatically installed via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Sylius\Bundle\TranslationBundle\SyliusTranslationBundle(),
new Sylius\Bundle\AddressingBundle\SyliusAddressingBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
);
}

Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first.

Container configuration

Put this configuration inside your app/config/config.yml.


sylius_addressing:
driver: doctrine/orm # Configure the doctrine orm driver used in documentation.

You should also configure SyliusTranslationBundle, as SyliusAddressingBundle is dependant on it:

7.1. Symfony2 Ecommerce Bundles

103

Sylius, Release

sylius_translation:
driver: doctrine/orm
default_locale: en

As well as doctrine extensions which are used in assortment bundle:


stof_doctrine_extensions:
orm:
default:
timestampable: true

Routing configuration

Import the routing configuration by adding the following to your app/config/routing.yml.


sylius_addressing:
resource: @SyliusAddressingBundle/Resources/config/routing.yml

Updating database schema

Run the following command.


$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.

Templates

This bundle provides some default bootstrap templates.


Note: You can check Sylius application to see how to integrate it in your application.

ZoneMatcher
This bundle exposes the ZoneMatcher as sylius.zone_matcher service.
<?php
$zoneMatcher = $this->get('sylius.zone_matcher');
$zone = $zoneMatcher->match($user->getBillingAddress());

Forms
The bundle ships with a set of useful form types for all models. You can use the defaults or override them with your
own types.

104

Chapter 7. Bundles

Sylius, Release

Address form

The address form type is named sylius_address and you can create it whenever you need, using the form factory.
<?php
// src/Acme/ShopBundle/Controller/AddressController.php
namespace Acme\ShopBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DemoController extends Controller
{
public function fooAction(Request $request)
{
$form = $this->get('form.factory')->create('sylius_address');
}
}

You can also embed it into another form.


<?php
// src/Acme/ShopBundle/Form/Type/OrderType.php
namespace Acme\ShopBundle\Form\Type;
use Sylius\Bundle\OrderBundle\Form\Type\OrderType as BaseOrderType;
use Symfony\Component\Form\FormBuilderInterface;
class OrderType extends BaseOrderType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder
->add('billingAddress', 'sylius_address')
->add('shippingAddress', 'sylius_address')
;
}
}

7.1.3 SyliusAttributeBundle
This bundle provides easy integration of the Sylius Attribute component with any Symfony full-stack application.
Sylius uses this bundle internally for its product catalog to manage the different attributes that are specific to each
product.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download the package.

7.1. Symfony2 Ecommerce Bundles

105

Sylius, Release

If you have Composer installed globally.


$ composer require "sylius/attribute-bundle"

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require "sylius/attribute-bundle"

Adding required bundles to the kernel

You need to enable the bundle inside the kernel.


If youre not using any other Sylius bundles, you will also need to add SyliusResourceBundle and its dependencies to
kernel. Dont worry, everything was automatically installed via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Sylius\Bundle\AttributeBundle\SyliusAttributeBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
);
}

Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first.

Container configuration

Put this configuration inside your app/config/config.yml.


sylius_attribute:
driver: doctrine/orm # Configure the doctrine orm driver used in the documentation.

And configure doctrine extensions which are used by the bundle.


stof_doctrine_extensions:
orm:
default:
timestampable: true

Updating database schema

Run the following command.


106

Chapter 7. Bundles

Sylius, Release

$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.
Congratulations! The bundle is now installed and ready to use.
Configuration reference

sylius_attribute:
driver: ~ # The driver used for persistence layer. Currently only `doctrine/orm` is supported.
classes:
# `subject_name` can be any name, for example `product`, `ad`, or `blog_post`
subject_name:
subject: ~ # Required: The subject class implementing `AttributeSubjectInterface`.
attribute:
model:
~ # Required: The attribute model class implementing `AttributeInterfac
repository: ~ # Required: The repository class for the attribute.
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
form:
Sylius\Bundle\AttributeBundle\Form\Type\AttributeType
attribute_value:
model:
~ # Required: The attribute value model class implementing `AttributeVa
repository: ~ # Required: The repository class for the attribute value.
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
form:
Sylius\Bundle\AttributeBundle\Form\Type\AttributeValueType
validation_groups:
# `subject_name` should be same name as the name key defined for the classes section above.
subject_name:
attribute:
[ sylius ]
attribute_value: [ sylius ]

7.1.4 SyliusCartBundle
A generic solution for a cart system inside a Symfony2 application.
It doesnt matter if you are starting a new project or you need to implement this feature for an existing system - this
bundle should be helpful. Currently only the Doctrine ORM driver is implemented, so well use it here as an example.
There are two main models inside the bundle, Cart and CartItem.
There are also 2 main services, Provider and ItemResolver. Youll get familiar with them in further parts of the
documentation.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download package.
If you have Composer installed globally.
$ composer require "sylius/cart-bundle"

Otherwise you have to download .phar file.

7.1. Symfony2 Ecommerce Bundles

107

Sylius, Release

$ curl -sS https://getcomposer.org/installer | php


$ php composer.phar require "sylius/cart-bundle"

Adding required bundles to the kernel

First, you need to enable the bundle inside the kernel. If youre not using any other Sylius bundles, you will also need
to add the following bundles and their dependencies to the kernel:
SyliusResourceBundle
SyliusMoneyBundle
SyliusOrderBundle
Dont worry, everything was automatically installed via Composer.
Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first. It is generally a good idea to place all of the Sylius bundles at the beginning of the bundles list, as it is
done in the Sylius-Standard project.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
new Sylius\Bundle\MoneyBundle\SyliusMoneyBundle(),
new Sylius\Bundle\OrderBundle\SyliusOrderBundle(),
new Sylius\Bundle\CartBundle\SyliusCartBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
);
}

Creating your entities

This is no longer a required step in the latest version of the SyliusCartBundle, and if you are happy with the default
implementation (which is Sylius\Bundle\CartBundle\Model\CartItem), you can just skip to the next
section.
You can create your CartItem entity, living inside your application code. We think that keeping the applicationspecific and simple bundle structure is a good practice, so lets assume you have your AppBundle registered under
App\AppBundle namespace.
<?php
// src/App/AppBundle/Entity/CartItem.php
namespace App\AppBundle\Entity;
use Sylius\Component\Cart\Model\CartItem as BaseCartItem;

108

Chapter 7. Bundles

Sylius, Release

class CartItem extends BaseCartItem


{
}

Now we need to define a simple mapping for this entity to map its fields.
You should
create a mapping file in your AppBundle, put it inside the doctrine mapping directory
src/App/AppBundle/Resources/config/doctrine/CartItem.orm.xml.
<?xml version="1.0" encoding="UTF-8"?>

<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping
<entity name="App\AppBundle\Entity\CartItem" table="app_cart_item">
</entity>
</doctrine-mapping>

You do not have to map the ID field because it is already mapped in the
Sylius\Component\Cart\Model\CartItem class, together with the relation between Cart and CartItem.
Lets assume you have a Product entity, which represents your main merchandise within your webshop.
Note: Please remember that you can use anything else, Product here is just an obvious example, but it will work in a
similar way with other entities.
We need to modify the CartItem entity and its mapping a bit, so it allows us to put a product inside the cart item.
<?php
// src/App/AppBundle/Entity/CartItem.php
namespace App\AppBundle\Entity;
use Sylius\Component\Cart\Model\CartItem as BaseCartItem;
class CartItem extends BaseCartItem
{
private $product;
public function getProduct()
{
return $this->product;
}
public function setProduct(Product $product)
{
$this->product = $product;
}
}

We added a product property, and a simple getter and setter. We have to also map the Product to CartItem, lets
create this relation in mapping files.
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"

7.1. Symfony2 Ecommerce Bundles

109

Sylius, Release

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping
<entity name="App\AppBundle\Entity\CartItem" table="app_cart_item">
<many-to-one field="product" target-entity="App\AppBundle\Entity\Product">
<join-column name="product_id" referenced-column-name="id" />
</many-to-one>
</entity>
</doctrine-mapping>

Similarly, you can create a custom entity for orders.


The class that you need to extend is
Sylius\Component\Cart\Model\Cart. Carts and Orders in Sylius are in fact the same thing. Do not forget
to create the mapping file. But, again, do not put a mapping for the ID field it is already mapped in the parent class.
And that would be all about entities. Now we need to create a really simple service.
Creating ItemResolver service

The ItemResolver will be used by the controller to resolve the new cart item - based on a user request information.
Its only requirement is to implement Sylius\Component\Cart\Resolver\ItemResolverInterface.
<?php
// src/App/AppBundle/Cart/ItemResolver.php
namespace App\AppBundle\Cart;
use Sylius\Component\Cart\Model\CartItemInterface;
use Sylius\Component\Cart\Resolver\ItemResolverInterface;
class ItemResolver implements ItemResolverInterface
{
public function resolve(CartItemInterface $item, $request)
{
}
}

The class is in place, well done.


We need to do some more coding, so the service is actually doing its job. In our example we want to put Product in
our cart, so we should inject the entity manager into our resolver service.
<?php
// src/App/AppBundle/Cart/ItemResolver.php
namespace App\AppBundle\Cart;
use Sylius\Component\Cart\Model\CartItemInterface;
use Sylius\Component\Cart\Resolver\ItemResolverInterface;
use Doctrine\ORM\EntityManager;
class ItemResolver implements ItemResolverInterface
{
private $entityManager;
public function __construct(EntityManager $entityManager)
{

110

Chapter 7. Bundles

Sylius, Release

$this->entityManager = $entityManager;
}
public function resolve(CartItemInterface $item, $request)
{
}
private function getProductRepository()
{
return $this->entityManager->getRepository('AppBundle:Product');
}
}

We also added a simple method getProductRepository() to keep the resolving code cleaner.
We must use this repository to find a product with id, given by the user via the request. This can be done in various
ways, but to keep the example simple - well use a query parameter.
<?php
// src/App/AppBundle/Cart/ItemResolver.php
namespace App\AppBundle\Cart;
use
use
use
use

Sylius\Component\Cart\Model\CartItemInterface;
Sylius\Component\Cart\Resolver\ItemResolverInterface;
Sylius\Component\Cart\Resolver\ItemResolvingException;
Doctrine\ORM\EntityManager;

class ItemResolver implements ItemResolverInterface


{
private $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function resolve(CartItemInterface $item, $request)
{
$productId = $request->query->get('productId');
// If no product id given, or product not found, we throw exception with nice message.
if (!$productId || !$product = $this->getProductRepository()->find($productId)) {
throw new ItemResolvingException('Requested product was not found');
}
// Assign the product to the item and define the unit price.
$item->setVariant($product);
$item->setUnitPrice($product->getPrice());
// Everything went fine, return the item.
return $item;
}
private function getProductRepository()
{
return $this->entityManager->getRepository('AppBundle:Product');
}
}

7.1. Symfony2 Ecommerce Bundles

111

Sylius, Release

Note: Please remember that item accepts only integers as price and quantity.
Register our brand new service in the container. Well use XML as an example, but you are free to pick any other
format.
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="app.cart_item_resolver" class="App\AppBundle\Cart\ItemResolver">
<argument type="service" id="doctrine.orm.entity_manager" />
</service>
</services>
</container>

The bundle requires also a simple configuration...


Container configuration

Put this minimal configuration inside your app/config/config.yml.


sylius_cart:
resolver: app.cart_item_resolver # The id of our newly created service.
classes: ~ # This key can be empty but it must be present in the configuration.
sylius_order:
driver: doctrine/orm # Configure the doctrine orm driver used in documentation.
sylius_money: ~

Or, if you have created any custom entities, use this:


sylius_cart:
resolver: app.cart_item_resolver # The id of our newly created service.
classes: ~ # This key can be empty but it must be present in the configuration.
sylius_order:
driver: doctrine/orm # Configure the doctrine orm driver used in documentation.
classes:
order:
model: App\AppBundle\Entity\Cart # If you have created a custom Cart entity.
order_item:
model: App\AppBundle\Entity\CartItem # If you have created a custom CartItem entity.
sylius_money: ~

Importing routing configuration

Import the default routing from your app/config/routing.yml.

112

Chapter 7. Bundles

Sylius, Release

sylius_cart:
resource: @SyliusCartBundle/Resources/config/routing.yml
prefix: /cart

Updating database schema

Remember to update your database schema.


For doctrine/orm driver run the following command.
$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.

Templates

We think that providing a sensible default template is really difficult, especially when a cart summary is not the simplest
page. This is the reason why we do not currently include any, but if you have an idea for a good starter template, let
us know!
The bundle requires only the summary.html.twig template for cart summary page. The easiest way to override the
view is by placing it here app/Resources/SyliusCartBundle/views/Cart/summary.html.twig.
Note: You can use the templates from our Sylius app as inspiration.

The Cart and CartItem


Here is a quick reference of what the default models can do for you.
Cart

You can access the cart total value using the ->getTotal() method.
The denormalized number of cart items is available via ->getTotalItems() method.
Recalculation of totals can happen by calling ->calculateTotal() method, using the simplest possible math.
It will also update the item totals. The carts have their expiration time - ->getExpiresAt() returns that time and
->incrementExpiresAt() sets it to +3 hours from now by default. The collection of items (Implementing
the Doctrine\Common\Collections\Collection interface) can be obtained using the ->getItems().
CartItem

Just like for the cart, the total is available via the same method (->getTotal()), but the unit price is accessible using
the ->getUnitPrice() Each item also can calculate its total, using the quantity (->getQuantity()) and the
unit price. It also has a very important method called ->equals(CartItemInterface $item), which decides
whether the items are same or not. If they are, it should return true, false otherwise. This is taken into account when
adding an item to the cart. If the added item is equal to an existing one, their quantities are summed, but no new
item is added to the cart. By default, it compares the ids, but for our example we would prefer to check the products.
We can easily modify our CartItem entity to do that correctly.

7.1. Symfony2 Ecommerce Bundles

113

Sylius, Release

<?php
// src/App/AppBundle/Entity/CartItem.php
namespace App/AppBundle/Entity;
use Sylius\Bundle\Component\Cart\CartItem as BaseCartItem;
use Sylius\Bundle\Component\Cart\CartItemInterface;
class CartItem extends BaseCartItem
{
private $product;
public function getProduct()
{
return $this->product;
}
public function setProduct(Product $product)
{
$this->product = $product;
}
public function equals(CartItemInterface $item)
{
return $this->product === $item->getProduct();
}
}

If the user tries to add the same product twice or more, it will just sum the quantities, instead of adding duplicates to
the cart.
Routing and default actions
This bundle provides a quite simple default routing with several handy and common actions. You can see the usage
guide below.
Cart summary page

To point user to the cart summary page, you can use the sylius_cart_summary route. It will render the page
with the cart and form variables by default.
The cart is the current cart and form is the view of the cart form.
Adding cart item

In our simple example, we only need to add the following link in the places where we need the add to cart button.
<a href="{{ path('sylius_cart_item_add', {'productId': product.id}) }}">Add product to cart</a>

Clicking this link will add the selected product to the cart.

114

Chapter 7. Bundles

Sylius, Release

Removing item

On the cart summary page you have access to all the cart items, so another simple link will allow a user to remove
items from the cart.
<a href="{{ path('sylius_cart_item_remove', {'id': item.id}) }}">Remove from cart</a>

Where item variable represents one of the cart.items collection items.


Clearing the whole cart

Clearing the cart is simple as clicking the following link.


<a href="{{ path('sylius_cart_clear')}}">Clear cart</a>

Basic cart update

On the cart summary page, you have access to the cart form, if you want to save it, simply submit the form with the
following action.
<form action="{{ path('sylius_cart_save') }}" method="post">Save cart</a>

You cart will be validated and saved if everything is alright.


Using the services
When using the bundle, you have access to several handy services. You can use them to manipulate and manage the
cart.
Managers and Repositories

Note: Sylius uses Doctrine\Common\Persistence interfaces.


You have access to following services which are used to manage and retrieve resources.
This set of default services is shared across almost all Sylius bundles, but this is just a convention. Youre interacting
with them like you usually do with own entities in your project.
<?php
// ...
public function saveAction(Request $request)
{
// ObjectManager which is capable of managing the Cart resource.
// For *doctrine/orm* driver it will be EntityManager.
$this->get('sylius.manager.cart');
// ObjectRepository for the Cart resource, it extends the base EntityRepository.
// You can use it like usual entity repository in project.
$this->get('sylius.repository.cart');
// Same pair for CartItem resource.
$this->get('sylius.manager.cart_item');

7.1. Symfony2 Ecommerce Bundles

115

Sylius, Release

$this->get('sylius.repository.cart_item');
// Those repositories have some handy default methods, for example...
$item = $this->get('sylius.repository.cart')->createNew();
}

Provider and Resolver

There are also 3 more services for you.


You use the provider to obtain the current user cart, if there is none, a new one is created and saved. The
->setCart() method also allows you to replace the current cart. ->abandonCart() is resetting the current
cart, a new one will be created on the next ->getCart() call. This is useful, for example, when after completing
an order you want to start with a brand new and clean cart.
<?php
// ...
public function saveAction(Request $request)
{
$provider = $this->get('sylius.cart_provider'); // Implements the CartProviderInterface.
$currentCart = $provider->getCart();
$provider->setCart($customCart);
$provider->abandonCart();
}

The resolver is used to create a new item based on the user request.
<?php
// ...
public function addItemAction(Request $request)
{
$resolver = $this->get('sylius.cart_resolver');
$item = $resolver->resolve($this->createNew(), $request);
}

Note: A more advanced example of a resolver implementation is available in Sylius Sandbox application on GitHub.

In templates
When using Twig as your template engine, you have access to 2 handy functions.
The sylius_cart_get function uses the provider to get the current cart.
{% set cart = sylius_cart_get() %}
Current cart totals: {{ cart.total }} for {{ cart.totalItems }} items!

The sylius_cart_form returns the form view for the CartItem form. It allows you to easily build more complex
actions for adding items to cart. In this simple example we allow to provide the quantity of item. Youll need to
process this form in your resolver.

116

Chapter 7. Bundles

Sylius, Release

{% set form = sylius_cart_form({'product': product}) %} {# You can pass options as an argument. #}


<form action="{{ path('sylius_cart_item_add', {'productId': product.id}) }}" method="post">
{{ form_row(form.quantity)}}
{{ form_widget(form._token) }}
<input type="submit" value="Add to cart">
</form>

Note: An example with multiple variants of this form can be found in Sylius Sandbox app. It allows for selecting a
variation/options/quantity of the product. It also adapts to the product type.

Summary
Configuration reference
sylius_cart:
# The driver used for persistence layer.
driver: ~
# Service id of cart item resolver.
resolver: ~
# Cart provider service id.
provider: sylius.cart_provider.default
# The id of cart storage for default provider.
storage: sylius.cart_storage.session
classes:
cart:
controller: Sylius\Bundle\CartBundle\Controller\CartController
form: Sylius\Bundle\CartBundle\Form\Type\CartType
item:
controller: Sylius\Bundle\CartBundle\Controller\CartItemController
form: Sylius\Bundle\CartBundle\Form\Type\CartItemType
validation_groups:
cart: [sylius]
item: [sylius]

phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -f pretty

Bug tracking

This bundle uses GitHub issues. If you have found bug, please create an issue.

7.1.5 SyliusFlowBundle
Wizards with reusable steps for Symfony2 applications. Suitable for building checkouts or installations.

7.1. Symfony2 Ecommerce Bundles

117

Sylius, Release

Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download package.
If you have Composer installed globally.
$ composer require "sylius/flow-bundle"

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require "sylius/flow-bundle"

Adding required bundles to the kernel

First, you need to enable the bundle inside the kernel. If youre not using any other Sylius bundles, you will also need
to add SyliusResourceBundle and its dependencies to the kernel. Dont worry, everything was automatically installed
via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new Sylius\Bundle\FlowBundle\SyliusFlowBundle(),
// Other bundles...
);
}

Creating your steps

We will create a very simple wizard now, without forms, storage, to keep things simple and get started fast.
Lets create a few simple steps:
<?php
namespace Acme\DemoBundle\Process\Step;
use Sylius\Bundle\FlowBundle\Process\Context\ProcessContextInterface;
use Sylius\Bundle\FlowBundle\Process\Step\AbstractControllerStep;
class FirstStep extends AbstractControllerStep
{
public function displayAction(ProcessContextInterface $context)
{
return $this->render('AcmeDemoBundle:Process/Step:first.html.twig');
}
public function forwardAction(ProcessContextInterface $context)
{
return $this->complete();

118

Chapter 7. Bundles

Sylius, Release

}
}
<?php
namespace Acme\DemoBundle\Process\Step;
use Sylius\Bundle\FlowBundle\Process\Context\ProcessContextInterface;
use Sylius\Bundle\FlowBundle\Process\Step\AbstractControllerStep;
class SecondStep extends AbstractControllerStep
{
public function displayAction(ProcessContextInterface $context)
{
return $this->render('AcmeDemoBundle:Process/Step:second.html.twig');
}
public function forwardAction(ProcessContextInterface $context)
{
return $this->complete();
}
}

And so on, one class for each step in the wizard.


As you can see, there are two actions in each step, display and forward. Usually, there is a form in a forward action
where you can pick some data. When you do return $this->complete() the wizard will take you to the next
step.
Creating scenario

To group steps into the wizard, we will implement ProcessScenarioInterface:


<?php
namespace Acme\DemoBundle\Process;
use
use
use
use

Sylius\Bundle\FlowBundle\Process\Builder\ProcessBuilderInterface;
Sylius\Bundle\FlowBundle\Process\Scenario\ProcessScenarioInterface;
Symfony\Component\DependencyInjection\ContainerAware;
Acme\DemoBundle\Process\Step;

class SyliusScenario extends ContainerAware implements ProcessScenarioInterface


{
public function build(ProcessBuilderInterface $builder)
{
$builder
->add('first', new Step\FirstStep())
->add('second', new Step\SecondStep())
// ...
;
}
}

As you can see, we just add each step to process builder with a desired name. The name will be used in the routes to
navigate to particular step.

7.1. Symfony2 Ecommerce Bundles

119

Sylius, Release

Registering scenario

In order for this to work, we need to register SyliusScenario and tag it as sylius.process.scenario:
<service id="sylius.scenario.flow" class="Acme\DemoBundle\Process\SyliusScenario">
<call method="setContainer">
<argument type="service" id="service_container" />
</call>
<tag name="sylius.process.scenario" alias="acme_flow" />
</service>

The configured alias will be used later in the route parameters to identify the scenario as you can have more then one.
Routing configuration

Import routing configuration:


acme_flow:
resource: @SyliusFlowBundle/Resources/config/routing.yml
prefix: /flow

If you take a look into imported routing configuration, you will see that sylius_flow_start is a wizard entry
point. sylius_flow_display displays the step with the given name, sylius_flow_forward forwards to
the next step from the step with the given name. All routes have an scenarioAlias as a required parameter to identify
the scenario.
Templates

Step templates are like any other action template, usually due to the nature of multi-step wizards, they have back and
forward buttons:

<h1>Welcome to second step</h1>


<a href="{{ path('sylius_flow_display', {'scenarioAlias': 'acme_flow', 'stepName': 'first'}) }}" clas
<a href="{{ path('sylius_flow_forward', {'scenarioAlias': 'acme_flow', 'stepName': 'second'}) }}" cla

Using storage
Storing data with default storage

By default, flow bundle will use session for data storage. Here is simple example how to use it in your steps:
<?php
namespace Acme\DemoBundle\Process\Step;
use Sylius\Bundle\FlowBundle\Process\Context\ProcessContextInterface;
use Sylius\Bundle\FlowBundle\Process\Step\ControllerStep;
class FirstStep extends ControllerStep
{
// ...
public function forwardAction(ProcessContextInterface $context)
{

120

Chapter 7. Bundles

Sylius, Release

$request = $this->getRequest();
$form = $this->createForm('my_form');
if ($request->isMethod('POST') && $form->bind($request)->isValid()) {
$context->getStorage()->set('my_data', $form->getData());
return $this->complete();
}
return $this->render('AcmeDemoBundle:Process/Step:first.html.twig', array(
'form' => $form->createView(),
));
}
}

You can later get data with $context->getStorage()->get(my_data).


For more details about storage, check SyliusBundleFlowBundleStorageStorageInterface class.
Summary
Configuration reference
sylius_flow:
storage: sylius.process_storage.session # The storage used to save flow data.

Tests
$ composer install --dev --prefer-dist
$ phpunit

Working examples

If you want to see working implementation, try out the Sylius application.
There is also an example that shows how to integrate this bundle into Symfony Standard Edition.
Bug tracking

This bundle uses GitHub issues. If you have found bug, please create an issue.

7.1.6 SyliusGridBundle
Displaying a grid with sorting and filtering options is a common task for many web applications. This bundle integrates
the Sylius Grid component with Symfony2 framework and allows you to display grids really easily.
Some of the features worth mentioning:
Uses YAML to define the grid structure
Supports different data sources: Doctrine ORM/ODM, native SQL query.
Rich filter functionality, easy to define your own filter type with flexible form
7.1. Symfony2 Ecommerce Bundles

121

Sylius, Release

Each column type is configurable and you can create your own
Bulk actions
Automatic sorting
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download the package.
If you have Composer installed globally.
$ composer require sylius/grid-bundle:v0.15.0

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require sylius/grid-bundle:v0.15.0

Adding required bundles to the kernel

You need to enable the bundle inside the kernel.


If youre not using any other Sylius bundles, you will also need to add SyliusResourceBundle and its dependencies to
kernel. Dont worry, everything was automatically installed via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Sylius\Bundle\AttributeBundle\SyliusAttributeBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
new Sylius\Bundle\GridBundle\SyliusGridBundle(),
);
}

Congratulations! The bundle is now installed and ready to use. You need to define your first resource and grid!
Your First Grid
In order to use grids, we need to register your entity as a Sylius resource. Let us assume you have Tournament model in
your application, which represents a sport tournament and has several fields, including name, date, status and category.
In order to make it a Sylius resource, you need to configure it under sylius_resource node:
# app/config/config.yml
sylius_resource:
resources:
app.tournament:

122

Chapter 7. Bundles

Sylius, Release

driver: doctrine/orm # Use appropriate driver.


classes:
model: AppBundle\Entity\Tournament

Thats it! Your class is now a resource. In order to learn what does it mean, please refer to SyliusResourceBundle
documentation.
Columns Definition

Now we can configure our first grid:


# app/config/config.yml
sylius_grids:
grids:
app_tournament:
driver: doctrine/orm # Use appropriate driver.
resource: app.tournament
columns:
name:
type: string
date:
type: datetime
status:
type: boolean
category:
type: string
options:
path: category.name

Generating The CRUD Routing

Thats it. SyliusResourceBundle allows to generate a default CRUD interface including the grid we have just defined.
Just put this in your routing configuration!
# app/config/routing.yml
app_tournament:
resource: app.tournament
type: sylius.crud

This will generate the following paths:


GET /tournaments/ - Your grid.
GET/POST /tournaments/new - Creating new tournament.
GET/PUT /tournaments/{id}/edit - Editing an existing tournament.
DELETE /tournaments/{id} - Deleting specific tournament.
GET /tournaments/{id} - Displaying specific tournament.
Defining Filters

Okay, but we need some filters, right?

7.1. Symfony2 Ecommerce Bundles

123

Sylius, Release

# app/config/config.yml
sylius_grids:
grids:
app_tournament:
driver: doctrine/orm # Use appropriate driver.
resource: app.tournament
columns:
name:
type: string
date:
type: datetime
status:
type: boolean
category:
type: string
options:
path: category.name
filters:
name:
type: string
date:
type: datetime
status:
type: boolean
category:
type: entity
options:
entity: AppBundle:TournamentCategory

Default Sorting

We want to have our tournaments sorted by name, by default, right? That is easy!
# app/config/config.yml
sylius_grids:
grids:
app_tournament:
driver: doctrine/orm # Use appropriate driver.
resource: app.tournament
sorting:
name: asc
columns:
name:
type: string
date:
type: datetime
status:
type: boolean
category:
type: string
options:
path: category.name
filters:
name:
type: string

124

Chapter 7. Bundles

Sylius, Release

date:
type: datetime
status:
type: boolean
category:
type: entity
options:
entity: AppBundle:TournamentCategory

Actions Configuration

Next step is adding some actions to the grid. We start with the basic ones, edit and delete. We can also add a simple
custom action with external link.
# app/config/config.yml
sylius_grids:
grids:
app_tournament:
driver: doctrine/orm # Use appropriate driver.
resource: app.tournament
sorting:
name: asc
columns:
name:
type: string
date:
type: datetime
status:
type: boolean
category:
type: string
options:
path: category.name
filters:
name:
type: string
date:
type: datetime
status:
type: boolean
category:
type: entity
options:
entity: AppBundle:TournamentCategory
actions:
edit:
type: link
options:
route: app_tournament_update
delete:
type: submit
options:
route: app_tournament_delete
method: DELETE

Your grid is ready to use!

7.1. Symfony2 Ecommerce Bundles

125

Sylius, Release

Column Types
This is the list of built-in column types.
String (string)

Simplest column type, which basically renders the value at given path as a string.
path Path to the value
By default is uses the name of the column, but your can specify the path in options. For example:
sylius_grid:
grids:
app_user:
columns:
email:
type: string
options:
path: contactDetails.email

This configuration will display the value from $user->getContactDetails()->getEmail().


DateTime (datetime)

This column type works exactly the same way as string, but expects DateTime instance and outputs a formatted date
and time string.
Date (date)

This column type works exactly the same way as string, but expects DateTime instance and outputs a formatted date
string.
Twig (twig)

Twig column type is the most flexible from all of them, because it delegates the logic of rendering the value to Twig
templating engine. You just have to specify the template and it will be rendered with the data variable available to
you.
sylius_grid:
grids:
app_user:
columns:
name:
type: twig
options:
template: :Grid/Column:_prettyName.html.twig

In the :Grid/Column:_prettyName.html.twig template, you just need to render the value as you see fit:
<strong>{{ data.name }}</strong>
<p>{{ data.description|markdown }}</p>

126

Chapter 7. Bundles

Sylius, Release

Boolean (boolean)

Boolean column type expects the value to be boolean and renders a default or custom Twig template.
sylius_grid:
grids:
app_user:
columns:
status:
type: boolean
options:
path: accountDetails.enabled # Optional!
template: :Grid/Column:_userStatus.html.twig # Optional!

Array (array)

This column type is a list of values. If you do not specify the Twig template, it will simply display the values as comma
separated list. Otherwise it will render the value using Twig.
sylius_grid:
grids:
app_user:
columns:
groups:
type: array
options:
template: :Grid/Column:_userGroups.html.twig # Optional!

Column configuration
Each column can be configured with several configuration fields, to make it more suitable to your grid requirements.
Name
type
label
sortable
options

Type
string
string
bool
array

Description
Type of column. Default column types are described here.
Label displayed in column header. By default, it is column name.
Whether column should be sortable or not. True by default.
Array of column options (see below).

options field can contains following fields:


Name Type
temstring
plate
path
string
sort

Description
Available (and required) for twig column type. Path to template that is used to
render column value.
Path to property displayed in column (can be property of resource or one of its
referenced object).
string|arrayOne or more properties that will be used to column sorting (can be property of
resource or one of its referenced object).

Default

column
name
column
name

Filters
Here you can find the list of all supported filters. Keep in mind you can very easily define your own!

7.1. Symfony2 Ecommerce Bundles

127

Sylius, Release

String (string)

Simplest filter type. It can filter in one or multiple columns, by default it uses the filter name as field, but you can
specify different set of fields.
fields Array of fields to filter
sylius_grid:
grids:
app_user:
filters:
search:
type: string
options:
fields: [username, e-mail, firstName, lastName]

The filter allows the user to select following search options:


contains
not contains
starts with
ends with
empty
not empty
equals
not equals
DateTime (datetime)

This filter has the following search options:


between
not between
more than
less than
Date (date)

This filter type works exactly the same way as datetime, but does not include the time.
The filter has the following search options:
between
not between
more than
less than

128

Chapter 7. Bundles

Sylius, Release

Boolean (boolean)

This filter checks if value is true or false.


Entity (entity)

This is entity filter and allows you to select appropriate entity from list and filter using this value.
class Entity name (full or short)
multiple (Default: false) Allow to select multiple values?
sylius_grid:
grids:
app_user:
filters:
brand:
type: entity
options:
class: AppBundle:Brand
multiple: true

Choice (choice)

This filter allows the user to select one or multiple values and filter the result set.
choices Array of choices
multiple (Default: false) Allow to select multiple values?
sylius_grid:
grids:
app_user:
filters:
gender:
type: choice
options:
choices:
male: Boys
female: Girls

Country (country)

This filter allows the user to select one or multiple countries.


multiple (Default: false) Allow to select multiple values?
sylius_grid:
grids:
app_user:
filters:
from:
type: country
options:
multiple: true

7.1. Symfony2 Ecommerce Bundles

129

Sylius, Release

Currency (currency)

This filter allows the user to select one or multiple currencies.


multiple (Default: false) Allow to select multiple values?
sylius_grid:
grids:
app_user:
filters:
currency:
type: currency
options:
multiple: true

Custom Column Type


There are certain cases when built-in column types are not enough. Sylius grid allows to define new types with ease!
All you need to do is implement your own class implementing ColumnTypeInterface and registering it in the container.
<?php
namespace App\Grid\ColumnType;
use Sylius\Component\Grid\ColumnType\ColumnTypeInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TournamentMonitorType implements ColumnTypeInterface
{
public function render($data, array $options = array())
{
// Your rendering logic... Use Twig, PHP or even external api...
if ($options['dynamic']) {
$output = // ...
}
return $output;
}
public function setOptions(OptionsResolverInterface $resolver)
{
$resolver
->setDefaults(array(
'dynamic' => false
))
->setAllowedTypes(array(
'dynamic' => array('boolean')
))
;
}
public function getType()
{
return 'tournament_monitor'
}
}

130

Chapter 7. Bundles

Sylius, Release

That is all. Now let register your new column type as service.
# app/config/services.yml
services:
app.grid.column_type.tournament_monitor:
class: App\Grid\ColumnType\TournamentMonitorType
tags:
- { name: sylius.grid_column_type type: tournament_monitor }

Now you can use your new column type in the grid configuration!
sylius_grid:
grids:
app_tournament:
driver: doctrine/orm
resource: app.tournament
columns:
monitor:
type: tournament_monitor
options:
dynamic: %kernel.debug%

Custom Filter
Sylius grids come with a lot of built-in filters, but there are use-cases where you need something more than basic filter.
Grids allow you to define your own filter types!
To add a new filter, we need to create appropriate class and form type.
<?php
namespace App\Grid\Filter;
use Sylius\Component\Grid\Data\DataSourceInterface;
use Sylius\Component\Grid\Filter\FilterInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TournamentStatisticsFilter implements FilterInterface
{
public function apply(DataSourceInterface $dataSource, $data, array $options = array())
{
// Your filtering logic. DataSource is kind of query builder.
// $data['stats'] contains the submitted value!
}
public function setOptions(OptionsResolverInterface $resolver)
{
$resolver
->setDefaults(array(
'range' => array(0, 10)
))
->setAllowedTypes(array(
'range' => array('array')
))
;
}

7.1. Symfony2 Ecommerce Bundles

131

Sylius, Release

public function getType()


{
return 'tournament_statistics'
}
}

And the form type:


<?php
namespace AppBundle\Form\Type\Filterh;
use Sylius\Component\Grid\Data\DataSourceInterface;
use Sylius\Component\Grid\Filter\FilterInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class TournamentStatisticsFilterType extends AbstractType


{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('stats', 'choice', array('choices' => range($options['range'][0], $options['ran
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver
->setDefaults(array(
'range' => array(0, 10)
))
->setAllowedTypes(array(
'range' => array('array')
))
;
}

public function getType()


{
return 'sylius_filter_tournament_statistics'; // The name is important to be ``sylius_filter_
}
}

That is all. Now let register your new filter type as service.
# app/config/services.yml
services:
app.grid.filter.tournament_statistics:
class: App\Grid\Filter\TournamentStatisticsFilter
tags:
- { name: sylius.grid_filter, type: tournament_statistics }
app.form.type.filter.tournament_statistics:
class: AppBundle\Form\Type\Filter\TournamentStatisticsFilterType
tags:
- { name: form.type, alias: sylius_filter_tournament_statistics }

Now you can use your new filter type in the grid configuration!
sylius_grid:
grids:

132

Chapter 7. Bundles

Sylius, Release

app_tournament:
driver: doctrine/orm
resource: app.tournament
filters:
stats:
type: tournament_statistics
options:
range: [0, 100]

Configuration Reference
sylius_grid:
grids:
app_user: # Your grid name.
driver: doctrine/orm # Data source driver.
resource: app.user # Resource name.
sorting:
name: asc
columns:
name:
type: twig # Type of column.
label: Name # Label.
sortable: true
options:
template: :Grid/Column:_name.html.twig # Only twig column
path: name
sort: name
filters:
group:
type: entity
label: Group
options:
entity: AppBundle:Group
actions:
edit:
type: link
options:
route: app_user_update
bulk_actions:
delete:
type: delete
copy:
type: copy

7.1.7 SyliusInventoryBundle
Flexible inventory management for Symfony2 applications.
With minimal configuration you can implement inventory tracking in your project.
Its fully customizable, but the default setup should be optimal for most use cases.
There is StockableInterface and InventoryUnit model inside the bundle.
There are services AvailabilityChecker, InventoryOperator and InventoryChangeListener.
Youll get familiar with them in later parts of this documentation.

7.1. Symfony2 Ecommerce Bundles

133

Sylius, Release

Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download package.
If you have Composer installed globally.
$ composer require "sylius/inventory-bundle"

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require "sylius/inventory-bundle"

Adding required bundles to the kernel

First, you need to enable the bundle inside the kernel. If youre not using any other Sylius bundles, you will also need
to add SyliusResourceBundle and its dependencies to the kernel. Dont worry, everything was automatically installed
via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
new FOS\RestBundle\FOSRestBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
new Sylius\Bundle\InventoryBundle\SyliusInventoryBundle(),
);
}

Creating your entities

Lets assume we want to implement a book store application and track the books inventory.
You have to create a Book and an InventoryUnit entity, living inside your application code. We think that keeping
the app-specific bundle structure simple is a good practice, so lets assume you have your AppBundle registered
under App\Bundle\AppBundle namespace.
We will create Book entity.
<?php
// src/App/AppBundle/Entity/Book.php
namespace App\AppBundle\Entity;
use Sylius\Bundle\InventoryBundle\Model\StockableInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="app_book")
*/
class Book implements StockableInterface

134

Chapter 7. Bundles

Sylius, Release

{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(type="string")
*/
protected $isbn;
/**
* @ORM\Column(type="string")
*/
protected $title;
/**
* @ORM\Column(type="integer")
*/
protected $onHand;
/**
* @ORM\Column(type="boolean")
*/
protected $availableOnDemand;
public function __construct()
{
$this->onHand = 1;
$this->availableOnDemand = true;
}
public function getId()
{
return $this->id;
}
public function getIsbn()
{
return $this->isbn;
}
public function setIsbn($isbn)
{
$this->isbn = $isbn;
}
public function getSku()
{
return $this->getIsbn();
}
public function getTitle()
{
return $this->title;
}

7.1. Symfony2 Ecommerce Bundles

135

Sylius, Release

public function setTitle($title)


{
$this->title = $title;
}
public function getInventoryName()
{
return $this->getTitle();
}
public function isInStock()
{
return 0 < $this->onHand;
}
public function isAvailableOnDemand()
{
return $this->availableOnDemand;
}
public function setAvailableOnDemand($availableOnDemand)
{
$this->availableOnDemand = (Boolean) $availableOnDemand;
}
public function getOnHand()
{
return $this->onHand;
}
public function setOnHand($onHand)
{
$this->onHand = $onHand;
}
}

Note: This example shows the full power of StockableInterface. The bundle also provides an Stockable entity which
implements StockableInterface for you. By extending the Stockable entity, the example above can be dramatically
simplified.
In order to track the books inventory our Book entity must implement StockableInterface. Note that we added
->getSku() method which is alias to ->getIsbn(), this is the power of the interface, we now have full control over the entity mapping. In the same way ->getInventoryName() exposes the book title as the displayed
name for our stockable entity.
The next step requires the creating of the InventoryUnit entity, lets do this now.
<?php
// src/App/AppBundle/Entity/InventoryUnit.php
namespace App\AppBundle\Entity;
use Sylius\Bundle\InventoryBundle\Entity\InventoryUnit as BaseInventoryUnit;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity

136

Chapter 7. Bundles

Sylius, Release

* @ORM\Table(name="app_inventory_unit")
*/
class InventoryUnit extends BaseInventoryUnit
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
}

Note that we are using base entity from Sylius bundle, which means inheriting some functionality inventory bundle provides. InventoryUnit holds the reference to stockable object, which is Book in our case. So, if we use the
InventoryOperator to create inventory units, they will reference the given book entity.
Container configuration

Put this configuration inside your app/config/config.yml.


sylius_inventory:
driver: doctrine/orm
backorders: true
classes:
inventory_unit:
model: App\AppBundle\Entity\InventoryUnit
stockable:
model: App\AppBundle\Entity\Book

Routing configuration

Import the routing configuration by adding the following to your app/config/routing.yml.


sylius_inventory:
resource: @SyliusInventoryBundle/Resources/config/routing.yml

Updating database schema

Remember to update your database schema.


For doctrine/orm driver run the following command.
$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.

Templates

The bundle provides some default bootstrap templates.

7.1. Symfony2 Ecommerce Bundles

137

Sylius, Release

Models
Here is a quick reference for the default models.
InventoryUnit

Each unit holds a reference to a stockable object and its state, which can be sold or backordered. It also provides
some handy shortcut methods like isSold, isBackordered and getSku.
Stockable

In order to be able to track stock levels in your application, you must implement StockableInterface or use the Stockable
model. It uses the SKU to identify stockable, need to provide display name and to check if stockable is available on
demand. It can get/set current stock level with getOnHand and setOnHand methods.
Using the services
When using the bundle, you have access to several handy services.
AvailabilityChecker

The name speaks for itself, this service checks availability for given stockable object. It takes backorders setting into
account, so if backorders are enabled, stockable will always be available. Backorders can be enabled per stockable if
it is available on demand. If none of this is the case, it means that backorders are not enabled for the given stockable
and AvailabilityChecker will rely on the current stock level.
There are two methods for checking availability. ->isStockAvailable() just checks whether stockable object
is available in stock and doesnt care about quantity. ->isStockSufficient() checks if there is enough units in
the stock for given quantity.
InventoryOperator

Inventory operator is the heart of this bundle. It can be used to manage stock levels and inventory units. It can also
fill backorders for the given stockable, this is a very powerful feature in combination with InventoryChangeListener.
Creating/destroying inventory units with a given state is also the operators job.
InventoryChangeListener

It simply triggers InventoryOperatorInterface::fillBackorders(). This can be extended by implementing InventoryChangeListenerInterface. Events can be configured like explained later on the documentation
Summary.
Twig Extension
There are two handy twig functions bundled in: sylius_inventory_is_available and sylius_inventory_is_sufficient.
They are simple proxies to the availability checker, and can be used to show if the stockable object is available/sufficient.

138

Chapter 7. Bundles

Sylius, Release

Here is a simple example, note that product variable has to be an instance of StockableInterface.
{% if not sylius_inventory_is_available(product) %}
<span class="label label-important">out of stock</span>
{% endif %}

Summary
Configuration reference
sylius_inventory:
# The driver used for persistence layer.
driver: ~
# Enable/disable backorders.
backorders: true
# Array of events for InventoryChangeListener
events: ~
# Enable or disbale tracking inventory
track_inventory: true
# The availability checker service id.
checker: sylius.availability_checker.default
# The inventory operator service id.
operator: sylius.inventory_operator.default
classes:
inventory_unit:
model: Sylius\Component\Inventory\Model\InventoryUnit
controller: Sylius\Bundle\InventoryBundle\Controller\InventoryUnitController
repository: ~ # You can override the repository class here.
stockable:
model: ~ # The stockable model class.
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController

phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -f pretty

Bug tracking

This bundle uses GitHub issues. If you have found bug, please create an issue.

7.1.8 SyliusMailerBundle
Sending customizable e-mails has never been easier in Symfony.
You can configure different e-mail types in the YAML or in database. (and use YAML as fallback) This allows you to
send out e-mails with one simple method call, providing an unique code and data.
The bundle supports adapters, by default e-mails are rendered using Twig and sent via Swiftmailer, but you can easily
implement your own adapter and delegate the whole operation to external API.
This bundle provides easy integration of the Sylius Mailer component with any Symfony full-stack application.

7.1. Symfony2 Ecommerce Bundles

139

Sylius, Release

Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download the package.
If you have Composer installed globally.
$ composer require sylius/mailer-bundle:0.14.*@dev

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require sylius/mailer-bundle:0.14.*@dev

Adding required bundles to the kernel

You need to enable the bundle inside the kernel.


If youre not using any other Sylius bundles, you will also need to add SyliusResourceBundle and its dependencies to
kernel. Dont worry, everything was automatically installed via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Sylius\Bundle\MailerBundle\SyliusMailerBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
);
}

Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first.

Container configuration

Put this configuration inside your app/config/config.yml.


sylius_mailer:
driver: doctrine/orm # Configure the doctrine orm driver used in the documentation.
sender:
name: My website
address: no-reply@my-website.com

And configure doctrine extensions which are used by the bundle.

140

Chapter 7. Bundles

Sylius, Release

stof_doctrine_extensions:
orm:
default:
timestampable: true

Updating database schema

Run the following command.


$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.
Congratulations! The bundle is now installed and ready to use.
Your First Email
Lets say you want to send a notification to the website team when someone submits a new position to your movie
catalog website!
You can do it in few simple steps:
Configure Your E-Mail

In your app/config/config.yml, under sylius_mailer you should configure the email:


# app/config/config.yml
sylius_mailer:
driver: doctrine/orm
sender:
name: Movie Database Example
address: no-reply@movie-database-example.com
emails:
movie_added_notification:
subject: A new movie {{ movie.title }} has been submitted
template: AppBundle:Email:movieAddedNotification.html.twig

Thats it! Your unique code is movie_added_notification. Now, lets create the template.
Creating Your Template

In your AppBundle/Resources/views/Email:movieAddedNotification.html.twig put the following Twig code:


{% block subject %}
A new movie {{ movie.title }} has been submitted
{% endblock %}
{% block body %}
Hello Movie Database Example!

7.1. Symfony2 Ecommerce Bundles

141

Sylius, Release

A new movie has been submitted for review to your database.


Title: {{ movie.title }}
Added by {{ user.name }}
Please review it and accept or reject!
{% endblock %}

That should be enough!


Sending The E-Mail

The service responsible for sending an e-mail has id sylius.email_sender. All you need to do is retrieve it
from the container or inject to a listener:
<?php
namespace App\AppBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
class MovieController
{
public function submitAction(Request $request)
{
// Your code.

$this->get('sylius.email_sender')->send('movie_added_notification', array('team@website.com')
}
}

Listener example:
<?php
namespace App\AppBundle\Controller;
use App\Event\MovieCreatedEvent;
use Sylius\Component\Mailer\Sender\SenderInterface;
class MovieNotificationListener
{
private $sender;
public function __construct(SenderInterface $sender)
{
$this->sender = $sender;
}
public function onMovieCreation(MovieCreatedEvent $event)
{
$movie = $event->getMovie();
$user = $event->getUser();

$this->sender->send('movie_added_notification', array('team@website.com'), array('movie' => $


}
}

142

Chapter 7. Bundles

Sylius, Release

We recommend using events approach, but you can send e-mails from anywhere in your application. Enjoy!
Using Custom Adapter
There are certain use cases, where you do not want to send the e-mail from your app, but delegate the task to an
external API.
It is really simple with Adapters system!
Implement Your Adapter

Create your adapter class and add your custom logic for sending:
<?php
namespace App\Mailer\Adapter;
use Sylius\Component\Mailer\Sender\AdapterInterface;
use Sylius\Component\Mailer\Model\EmailInterface
class CustomAdapter implements AdapterInterface
{
public function send(EmailInterface $email, array $recipients, array $data = array())
{
// Your custom logic.
}
}

Register New Adapter In Container

In your services.yml file, simply add your adapter definition.


services:
app.email_sender.adapter.custom:
class: App\Mailer\Adapter\CustomAdapter

Configure The New Adapter

Now you just need to put service name under sylius_mailer configuration in app/config/config.yml.
sylius_mailer:
adapter: app.email_sender.adapter.custom

Thats it! Your new adapter will be used to send out e-mails. You can do whatever you want there!
Configuration reference
sylius_mailer:
driver: ~ # The driver used for persistence layer. Currently only `doctrine/orm` is supported.
sender_adapter: sylius.email_sender.adapter.swiftmailer # Adapter for sending e-mails.
renderer_adapter: sylius.email_renderer.adapter.twig # Adapter for rendering e-mails.
classes:
email:

7.1. Symfony2 Ecommerce Bundles

143

Sylius, Release

model:
Sylius\Component\Mailer\Model\Email # The email model class implementing `E
repository: ~ # Is set automatically if empty.
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
form:
Sylius\Bundle\MailerBundle\Form\Type\EmailType
validation_groups:
email: [sylius]
sender:
name: # Required - default sender name.
address: # Required - default sender e-mail address.
templates: # Your templates available for selection in backend!
label: Template path
label: Template path
label: Template path
emails:
your_email:
subject: Subject of your email
template: AppBundle:Email:yourEmail.html.twig
enabled: true/false
sender:
name: Custom name
address: Custom sender address for this e-mail
your_another_email:
subject: Subject of your another email
template: AppBundle:Email:yourAnotherEmail.html.twig
enabled: true/false
sender:
name: Custom name
address: Custom sender address for this e-mail

7.1.9 SyliusOmnipayBundle
Symfony2 integration for Omnipay library, PHP abstraction for payment gateways.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download package.
If you have Composer installed globally.
$ composer require "sylius/omnipay-bundle"

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require "sylius/omnipay-bundle"

Adding required bundles to the kernel

First, you need to enable the bundle inside the kernel.


<?php
// app/AppKernel.php

144

Chapter 7. Bundles

Sylius, Release

public function registerBundles()


{
$bundles = array(
// ...
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Sylius\Bundle\OmnipayBundle\SyliusOmnipayBundle()
);
}

Configuration

Put this configuration inside your app/config/config.yml.


sylius_omnipay:
gateways:
AuthorizeNet_AIM:
# gateway name, use anyone
type: AuthorizeNet_AIM
# predefined list of types, read father for explanation
label: Authorize.Net AIM # how is gateway will be displayed in a form, etc.
AuthorizeNet_SIM:
type: AuthorizeNet_SIM
label: Authorize.Net SIM
Stripe:
type: Stripe
label: Stripe
mode: true
# optional, default: false, activate test mode
active: false
# optional, default: true, does this gateway is active
options:
# optional, predefine list of options to get work with gateway
apikey: secretapikey
PayPal_Express:
type: PayPal_Express
label: PayPal Express
PayPal_Pro:
type: PayPal_Pro
label: PayPal Pro

Implemented gateways

The following gateways are already implemented:


2Checkout
Authorize.Net AIM
Authorize.Net SIM
CardSave
Dummy
GoCardless
Manual
Netaxept (BBS)
PayFast

7.1. Symfony2 Ecommerce Bundles

145

Sylius, Release

Payflow Pro
PaymentExpress (DPS) PxPay
PaymentExpress (DPS) PxPost
PayPal Express Checkout
PayPal Payments Pro
Pin Payments
Sage Pay Direct
Sage Pay Server
Stripe
WorldPay
The list above is always growing. The full list of supported gateways can be found at the Omnipay github repository.
How to manage gateways
The bundle provide handy services, which help to manage the activated gateways and options.
Services

Here are a couple of services which are available to use out of the box.
sylius.omnipay.gateway_factory - implement Omnipay\Common\GatewayFactory to manipulate with gateway
and options
sylius.form.type.omnipay.gateway_choice - quick proxy service to list defined in configuration gateways
Controller

To be written.

7.1.10 SyliusOrderBundle
This bundle is a foundation for sales order handling for Symfony2 projects. It allows you to use any model as the
merchandise.
It also includes a super flexible adjustments feature, which serves as a basis for any taxation, shipping charges or
discounts system.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download package.
If you have Composer installed globally.
$ composer require sylius/order-bundle:*

Otherwise you have to download .phar file.

146

Chapter 7. Bundles

Sylius, Release

$ curl -sS https://getcomposer.org/installer | php


$ php composer.phar require sylius/order-bundle:*

Adding required bundles to the kernel

First, you need to enable the bundle inside the kernel.


If youre not using any other Sylius bundles, you will also need to add SyliusResourceBundle and its dependencies to
the kernel. Dont worry, everything was automatically installed via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
new Sylius\Bundle\MoneyBundle\SyliusMoneyBundle(),
new Sylius\Bundle\OrderBundle\SyliusOrderBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
);
}

Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first.

Creating your entities

You have to create your own Order entity, living inside your application code. We think that keeping the appspecific bundle structure simple is a good practice, so lets assume you have your AppBundle registered under
App\Bundle\AppBundle namespace.
<?php
// src/App/AppBundle/Entity/Order.php
namespace App\AppBundle\Entity;
use Sylius\Component\Order\Model\Order as BaseOrder;
class Order extends BaseOrder
{
}

Now we need to define simple mapping for this entity, because it only extends the Doctrine mapped superclass. You should create a mapping file in your AppBundle, put it inside the doctrine mapping directory
src/App/AppBundle/Resources/config/doctrine/Order.orm.xml.

7.1. Symfony2 Ecommerce Bundles

147

Sylius, Release

<?xml version="1.0" encoding="UTF-8"?>

<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping

<entity name="App\AppBundle\Entity\Order" table="app_order">


<id name="id" column="id" type="integer">
<generator strategy="AUTO" />
</id>
<one-to-many field="items" target-entity="Sylius\Component\Order\Model\OrderItemInterface" ma
<cascade>
<cascade-all/>
</cascade>
</one-to-many>

<one-to-many field="adjustments" target-entity="Sylius\Component\Order\Model\AdjustmentInterf


<cascade>
<cascade-all/>
</cascade>
</one-to-many>
</entity>
</doctrine-mapping>

Note: You might wonder why are we putting interface inside mapping, you can read about this Doctrine feature here.
Now lets assume you have a Product entity, which represents your main merchandise in your webshop.
Note: Please remember that you can use anything else, Product here is just an obvious example, but it will work in
similar way with other entities.
All you need to do is making your Product entity to implement ProductInterface and configure it inside Symfony settings.
<?php
// src/App/AppBundle/Entity/Product.php
namespace App\AppBundle\Entity;
use Sylius\Component\Product\Model\ProductInterface;
class Product implements ProductInterface
{
// Your code...
public function getName()
{
// Here you just have to return the nice display name of your merchandise.
return $this->name;
}
}

Now, you do not even have to map your Product model to the order items. It is all done automatically. And that would
be all about entities.

148

Chapter 7. Bundles

Sylius, Release

Container configuration

Put this configuration inside your app/config/config.yml.

sylius_order:
driver: doctrine/orm # Configure the doctrine orm driver used in documentation.
classes:
order:
model: App\AppBundle\Entity\Order # The order entity.
order_item:
model: App\AppBundle\Entity\OrderItem # Your order item entity, if you need to override i
sylius_product:
driver: doctrine/orm # Configure the doctrine orm driver used in documentation.
classes:
product:
model: App\AppBundle\Entity\Product

Updating database schema

Remember to update your database schema.


For doctrine/orm driver run the following command.
$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.

The Order and OrderItem


Here is a quick reference of what the default models can do for you.
Order basics

Each order has 2 main identifiers, an ID and a human-friendly number. You can access those by calling
->getId() and ->getNumber() respectively. The number is mutable, so you can change it by calling
->setNumber(E001) on the order instance.
<?php
$order->getId();
$order->getNumber();
$order->setNumber('E001');

Confirmation status

To check whether the order is confirmed or not, you can use the isConfirmed() method, which returns a true/false
value. To change that status, you can use the confirmation setter, setConfirmed(false). All orders are confirmed by default, unless you enabled the e-mail confirmation feature. Order also can contain a confirmation token,
accessible by the appropriate getter and setter.

7.1. Symfony2 Ecommerce Bundles

149

Sylius, Release

<?php
if ($order->isConfirmed()) {
echo 'This one is confirmed, great!';
}

Order totals

Note: All money amounts in Sylius are represented as cents - integers.


An order has 3 basic totals, which are all persisted together with the order.
The first total is the items total, it is calculated as the sum of all item totals.
The second total is the adjustments total, you can read more about this in next chapter.
<?php
echo $order->getItemsTotal(); // 1900.
echo $order->getAdjustmentsTotal(); // -250.
$order->calculateTotal();
echo $order->getTotal(); // 1650.

The main order total is a sum of the previously mentioned values. You can access the order total value using the
->getTotal() method. Recalculation of totals can happen by calling ->calculateTotal() method, using
the simplest possible math. It will also update the item totals.
Items management

The collection of items (Implementing the Doctrine\Common\Collections\Collection interface) can be


obtained using the ->getItems(). To add or remove items, you can simply use the addItem and removeItem
methods.
<?php
// $item1 and $item2 are instances of OrderItemInterface.
$order
->addItem($item)
->removeItem($item2)
;

OrderItem basics

An order item model has only the id as identifier, also it has the order to which it belongs, accessible via
->getOrder() method.
The sellable object can be retrieved and set, using the following setter and getter - ->getProduct() &
->setVariant(ProductVariantInterface $variant).
<?php
$item->setVariant($book);

150

Chapter 7. Bundles

Sylius, Release

Note: In most cases youll use the OrderBuilder service to create your orders.
Just like for the order, the total is available via the same method, but the unit price is accessible using the
->getUnitPrice() Each item also can calculate its total, using the quantity (->getQuantity()) and the
unit price.
<?php
$item = $itemRepository->createNew();
$item
->setVariant($book)
->setUnitPrice(2000)
->setQuantity(4)
->calculateTotal()
;
echo $item->getTotal(); // 8000.

An OrderItem can also hold adjustments.


The Adjustments
Adjustments are based on simple but powerful idea inspired by Spree adjustments. They serve as foundation for any
tax, shipping and discounts systems.
Adjustment model

Note: To be written.

Creating orders with OrderBuilder


Note: To be written.

Using the services


When using the bundle, you have access to several handy services. You can use them to retrieve and persist orders.
Managers and Repositories

Note: Sylius uses Doctrine\Common\Persistence interfaces.


You have access to following services which are used to manage and retrieve resources.
This set of default services is shared across almost all Sylius bundles, but this is just a convention. Youre interacting
with them like you usually do with own entities in your project.

7.1. Symfony2 Ecommerce Bundles

151

Sylius, Release

<?php
// ObjectManager which is capable of managing the resources.
// For *doctrine/orm* driver it will be EntityManager.
$this->get('sylius.manager.order');
$this->get('sylius.manager.order_item');
$this->get('sylius.manager.adjustment');
// ObjectRepository for the Order resource, it extends the base EntityRepository.
// You can use it like usual entity repository in project.
$this->get('sylius.repository.order');
$this->get('sylius.repository.order_item');
$this->get('sylius.repository.adjustment');

// Those repositories have some handy default methods, for example...


$item = $itemRepository->createNew();
$orderRepository->find(4);
$paginator = $orderRepository->createPaginator(array('confirmed' => false)); // Get Pagerfanta instan

Summary
Note: To be written.

Configuration reference
sylius_order:
# The driver used for persistence layer.
driver: ~
classes:
sellable:
# The class name of the entity you want to put inside orders.
model: ~
order:
model: Sylius\Component\Order\Model\Order
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\OrderBundle\Form\Type\OrderType
order_item:
model: Sylius\Component\Order\Model\OrderItem
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\OrderBundle\Form\Type\OrderItemType
adjustment:
model: Sylius\Component\Order\Model\Adjustment
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\OrderBundle\Form\Type\AdjustmentType
validation_groups:
order: [sylius] # Order validation groups.
order_item: [sylius]
adjustment: [sylius]

152

Chapter 7. Bundles

Sylius, Release

phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This bundle uses GitHub issues. If you have found bug, please create an issue.

7.1.11 SyliusProductBundle
The Sylius product catalog is made available as set of 2 standalone bundles. This component contains the most basic
product model with properties (attributes) support. It also includes the product prototyping system, which serves as a
template for creating similar products.
If you need product options support, please see SyliusVariationBundle which allows to manage product variations with
a very flexible architecture.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download the package.
If you have Composer installed globally.
$ composer require "sylius/product-bundle"

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require "sylius/product-bundle"

Adding required bundles to the kernel

You need to enable the bundle inside the kernel.


If youre not using any other Sylius bundles, you will also need to add SyliusResourceBundle and its dependencies to
kernel. Dont worry, everything was automatically installed via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Sylius\Bundle\ProductBundle\SyliusProductBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
new Sylius\Bundle\AttributeBundle\SyliusAttributeBundle(),

7.1. Symfony2 Ecommerce Bundles

153

Sylius, Release

new Sylius\Bundle\VariationBundle\SyliusVariationBundle(),
new Sylius\Bundle\TranslationBundle\SyliusTranslationBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
);
}

Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first.

Container configuration

Put this configuration inside your app/config/config.yml.


sylius_product:
driver: doctrine/orm # Configure the doctrine orm driver used in the documentation.

And configure doctrine extensions which are used by the bundle.


stof_doctrine_extensions:
orm:
default:
sluggable: true
timestampable: true

Routing configuration

Add the following to your app/config/routing.yml.


sylius_product:
resource: @SyliusProductBundle/Resources/config/routing.yml

Updating database schema

Run the following command.


$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.
Congratulations! The bundle is now installed and ready to use.
The Product
Retrieving products

Retrieving a product from the database should always happen via repository, which always implements
Sylius\Bundle\ResourceBundle\Model\RepositoryInterface. If you are using Doctrine, youre
already familiar with this concept, as it extends the native Doctrine ObjectRepository interface.
154

Chapter 7. Bundles

Sylius, Release

Your product repository is always accessible via the sylius.repository.product service.


<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.product');
}

Retrieving products is simple as calling proper methods on the repository.


<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.product');

$product = $repository->find(4); // Get product with id 4, returns null if not found.


$product = $repository->findOneBy(array('slug' => 'my-super-product')); // Get one product by def
$products = $repository->findAll(); // Load all the products!
$products = $repository->findBy(array('special' => true)); // Find products matching some custom
}

Product repository also supports paginating products. To create a Pagerfanta instance use the createPaginator
method.
<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.product');
$products = $repository->createPaginator();
$products->setMaxPerPage(3);
$products->setCurrentPage($request->query->get('page', 1));

// Now you can return products to the template and iterate over it to get products from the curren
}

The paginator also can be created for specific criteria and with desired sorting.
<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.product');
$products = $repository->createPaginator(array('foo' => true), array('createdAt' => 'desc'));
$products->setMaxPerPage(3);
$products->setCurrentPage($request->query->get('page', 1));
}

Creating new product object

To create new product instance, you can simply call createNew() method on the repository.

7.1. Symfony2 Ecommerce Bundles

155

Sylius, Release

<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.product');
$product = $repository->createNew();
}

Note: Creating a product via this factory method makes the code more testable, and allows you to change the product
class easily.

Saving & removing product

To save or remove a product, you can use any ObjectManager which manages Product. You can always access it via
alias sylius.manager.product. But its also perfectly fine if you use doctrine.orm.entity_manager
or other appropriate manager service.
<?php

public function myAction(Request $request)


{
$repository = $this->container->get('sylius.repository.product');
$manager = $this->container->get('sylius.manager.product'); // Alias for appropriate doctrine man
$product = $repository->createNew();
$product
->setName('Foo')
->setDescription('Nice product')
;
$manager->persist($product);
$manager->flush(); // Save changes in database.
}

To remove a product, you also use the manager.


<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.product');
$manager = $this->container->get('sylius.manager.product');
$product = $repository->find(1);
$manager->remove($product);
$manager->flush(); // Save changes in database.
}

Properties

A product can also have a set of defined Properties (think Attributes), youll learn about them in next chapter of this
documentation.

156

Chapter 7. Bundles

Sylius, Release

Product Properties
Managing Properties

Managing properties happens exactly the same way like products, you have sylius.repository.property
and sylius.manager.property at your disposal.
Assigning properties to product

Value of specific Property for one of Products, happens through ProductProperty model, which holds the references to
Product, Property pair and the value. If you want to programatically set a property value on product, use the following
code.
<?php
public function myAction(Request $request)
{
$propertyRepository = $this->container->get('sylius.repository.property');
$productPropertyRepository = $this->container->get('sylius.repository.product_property');
$property = $propertyRepository->findOneBy(array('name' => 'T-Shirt Collection'));
$productProperty = $productPropertyRepository->createNew();
$productProperty
->setProperty($property)
->setValue('Summer 2013')
;
$product->addProperty($productProperty);
$manager = $this->container->get('sylius.manager.product');
$manager->persist($product);
$manager->flush(); // Save changes in database.
}

This looks a bit tedious, doesnt it? There is a ProductBuilder service which simplifies the creation of products
dramatically, you can learn about it in appropriate chapter.
Forms
The bundle ships with a set of useful form types for all models. You can use the defaults or override them with
your own forms.
Product form

The product form type is named sylius_product and you can create it whenever you need, using the form factory.
<?php
// src/Acme/ShopBundle/Controller/ProductController.php
namespace Acme\ShopBundle\Controller;

7.1. Symfony2 Ecommerce Bundles

157

Sylius, Release

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DemoController extends Controller
{
public function fooAction(Request $request)
{
$form = $this->get('form.factory')->create('sylius_product');
}
}

The default product form consists of following fields.


Field
name
description
availableOn
metaDescription
metaKeywords

Type
text
textarea
datetime
text
text

You can render each of these using the usual Symfony way {{ form_row(form.description) }}.
Property form

Default form for the Property model has name sylius_property and contains several basic fields.
Field
name
presentation
type

Type
text
text
choice

Prototype form

The default form for the Prototype model has name sylius_prototype and is built from the following fields.
Field
name
properties

Type
text
sylius_property_choice

Miscellaneous fields

There are a few more form types, which can become useful when integrating the bundle into your app.
sylius_product_property is a form which is used to set the product properties (and their values). It has 2
fields, the property choice field and a value input.
sylius_property_choice is a ready-to-use select field, with a list of all Properties from database.
sylius_product_to_identifier can be used to render a text field, which will transform the value into a
product.
If you need to customize existing fields or add your own, please read the overriding forms chapter.

158

Chapter 7. Bundles

Sylius, Release

Prototypes
...
Prototype Builder

Used to build product based on given prototype.


Here is an example:
<?php
$prototype = $this->findOr404(array('id' => $prototypeId));
$product = $this->get('sylius.repository.product')->createNew();
$this
->get('sylius.builder.prototype')
->build($prototype, $product)
;

It will add appropriate options and variants to given product based on prototype.
Product Builder
This service provides a fluent interface for easy product creation.
The example shown below is self explanatory:
<?php
$product = $this->get('sylius.builder.product')
->create('Github mug')
->setDescription("Coffee. Let's face it -- humans need to drink liquids!")
->addAttribute('collection', 2013)
->addAttribute('color', 'Red')
->addAttribute('material', 'Stone')
->save()
;

Summary
Configuration reference
sylius_product:
driver: ~ # The driver used for persistence layer.
engine: twig # Templating engine to use by default.
classes:
product:
model: Sylius\Component\Product\Model\Product
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\AssortmentBundle\Form\Type\ProductType
product_prototype:
model: Sylius\Component\Product\Model\Prototype
controller: Sylius\Bundle\ProductBundle\Controller\PrototypeController

7.1. Symfony2 Ecommerce Bundles

159

Sylius, Release

repository: ~
form: Sylius\Bundle\AssortmentBundle\Form\Type\PrototypeType
validation_groups:
product: [sylius] # Product validation groups.
product_prototype: [sylius] # Product prototype validation groups.

Tests
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This bundle uses GitHub issues. If you have found bug, please create an issue.

7.1.12 SyliusPromotionBundle
Promotions system for Symfony2 applications.
With minimal configuration you can introduce promotions and coupons into your project. The following types of
promotions are available and totally mixable:
percentage discounts
fixed amount discounts
promotions limited by time
promotions limited by a maximum number of usages
promotions based on coupons
This means you can for instance create the following promotions :
20$ discount for New Year orders having more than 3 items
8% discount for Christmas orders over 100 EUR
first 3 orders have 100% discount
5% discount this week with the coupon code WEEK5
40 C discount with the code you have received by mail
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download the package.
If you have Composer installed globally.
$ composer require "sylius/promotion-bundle"

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require "sylius/promotion-bundle"

160

Chapter 7. Bundles

Sylius, Release

Adding required bundles to the kernel

You need to enable the bundle inside the kernel.


If youre not using any other Sylius bundles, you will also need to add SyliusResourceBundle and its dependencies to kernel. Dont worry, everything was automatically installed via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Sylius\Bundle\PromotionBundle\SyliusPromotionBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
);
}

Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first.

Container configuration

Put this configuration inside your app/config/config.yml.


sylius_promotion:
driver: doctrine/orm # Configure the doctrine orm driver used in the documentation.

And configure doctrine extensions which are used by the bundle.


stof_doctrine_extensions:
orm:
default:
timestampable: true

Routing configuration

Add the following to your app/config/routing.yml.


sylius_promotion:
resource: @SyliusPromotionBundle/Resources/config/routing.yml

Updating database schema

Run the following command.

7.1. Symfony2 Ecommerce Bundles

161

Sylius, Release

$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.
Congratulations! The bundle is now installed and ready to use.
Models
All the models of this bundle are defined in Sylius\Bundle\PromotionBundle\Model.
Rule

A Rule is used to check if your order is eligible to the promotion. A promotion can have none, one or several rules.
SyliusPromotionBundle comes with 2 types of rules :
item count rule : the number of items of the order is checked
item total rule : the amount of the order is checked
A rule is configured via the configuration attribute which is an array serialized into database. For item count
rules, you have to configure the count key, whereas the amount key is used for item total rules. For both types of
rules, you can configure the equal key to false or true depending is you want the rule to be strict or not. For
instance, for an item count rule configured with equal to false and count to 4, orders with only more than 4
items will be eligible.
Action

An Action defines the the nature of the discount. Common actions are :
percentage discount
fixed amount discount
An action is configured via the configuration attribute which is an array serialized into database. For percentage
discount actions, you have to configure the percentage key, whereas the amount key is used for fixed discount
rules.
Coupon

A Coupon is a ticket having a code that can be exchanged for a financial discount. A promotion can have none, one
or several coupons.
A coupon is considered as valid if the method isValid() returns true. This method checks the number of times
this coupon can be used (attribute usageLimit), the number of times this has already been used (attribute used)
and the coupon expiration date (attribute expiresAt). If usageLimit is not set, the coupon will be usable an
unlimited times.
PromotionSubjectInterface

A PromotionSubjectInterface is the object you want to apply the promotion on. For instance, in Sylius
Standard, a Sylius\Bundle\CoreBundle\Model\Order can be subject to promotions.

162

Chapter 7. Bundles

Sylius, Release

By implementing PromotionSubjectInterface, your object will have to define the following methods :
- getPromotionSubjectItemTotal() should return the amount of your order - getPromotionSubjectItemCount() should return the number of items of your order getPromotionCoupon() should return the coupon linked to your order. If you do not want to use coupon, simply
return null.
Promotion

The Promotion is the main model of this bundle. A promotion has a name, a description and :
can have none, one or several rules
should have at least one action to be effective
can be based on coupons
can have a limited number of usages by using the attributes usageLimit and used. When used reaches
usageLimit the promotion is no longer valid. If usageLimit is not set, the promotion will be usable an
unlimited times.
can be limited by time by using the attributes startsAt and endsAt
How rules are checked ?
Everything related to this subject is located in Sylius\Bundle\PromotionBundle\Checker.
Rule checkers

New rules can be created by implementing RuleCheckerInterface. This interface provides the method
isEligible which aims to determine if the promotion subject respects the current rule or not.
I told you before that SyliusPromotionBundle ships with 2 types of rules : item count rule and item total rule.
Item count rule is defined via the service sylius.promotion_rule_checker.item_count which uses the
class ItemCountRuleChecker. The method isEligible checks here if the promotion subject has the minimum number of items (method getPromotionSubjectItemCount() of PromotionSubjectInterface)
required by the rule.
Item total rule is defined via the service sylius.promotion_rule_checker.item_total which uses
the class ItemTotalRuleChecker. The method isEligible checks here if the promotion subject has the
minimum amount (method getPromotionSubjectItemTotal() of PromotionSubjectInterface) required by the rule.
The promotion eligibility checker service

To be eligible to a promotion, a subject must :


1. respect all the rules related to the promotion
2. respect promotion dates if promotion is limited by time
3. respect promotions usages count if promotion has a limited number of usages
4. if a coupon is provided with this order, it must be valid and belong to this promotion

7.1. Symfony2 Ecommerce Bundles

163

Sylius, Release

The service sylius.promotion_eligibility_checker checks all these constraints for you


with the method isEligible() which returns true or false.
This service uses the class
PromotionEligibilityChecker.
How actions are applied ?
Everything related to this subject is located in Sylius\Bundle\PromotionBundle\Action.
Actions

Actions can be created by implementing PromotionActionInterface.


This interface provides the
method execute which aim is to apply a promotion to its subject.
It also provides the method
getConfigurationFormType which has to return the form name related to this action.
Actions have to be defined as services and have to use the tag named sylius.promotion_action with the
attributes type and label.
As SyliusPromotionBundle is totally independent, it does not provide some actions out of the box. Great
examples of actions are provided by Sylius/Standard-Edition.
Note:
Sylius\Bundle\CoreBundle\Promotion\Action\FixedDiscountAction from
Sylius/Standard-Edition is an example of action for a fixed amount discount. The related service is
called sylius.promotion_action.fixed_discount.
Note:
Sylius\Bundle\CoreBundle\Promotion\Action\PercentageDiscountAction from
Sylius/Standard-Edition is an example of action for a discount based on percentage. The related service
is called sylius.promotion_action.percentage_discount.
All actions that you have defined as services will be automatically registered thanks
Sylius\Bundle\PromotionBundle\Action\Registry\PromotionActionRegistry.

to

Applying actions to promotions

We have seen above how actions can be created. Now lets see how they are applied to their subject.
The PromotionApplicator is responsible of this via its method apply. This method will execute all the
registered actions of a promotion on a subject.
How promotions are applied ?
By using the promotion eligibility checker and the promotion applicator checker services, the promotion processor
applies all the possible promotions on a subject.
The promotion processor is defined via the service sylius.promotion_processor which uses the class
Sylius\Bundle\PromotionBundle\Processor\PromotionProcessor. Basically, it calls the method
apply of the promotion applicator for all the active promotions that are eligible to the given subject.
Coupon based promotions
Coupon based promotions require special needs that are covered by this documentation.

164

Chapter 7. Bundles

Sylius, Release

Coupon generator

SyliusPromotionBundle provides a way of generating coupons for a promotion : the coupon


generator.
Provided as a service sylius.generator.promotion_coupon via the class
Sylius\Bundle\PromotionBundle\Generator\CouponGenerator, its goal is to generate unique
coupon codes.
Coupon to code transformer

SyliusPromotionBundle
provides
a
way
to
transform
a
simple
string
code
to
a
real
Coupon
object
(and
vice
versa).
This
is
done
via
the
Sylius\Bundle\PromotionBundle\Form\DataTransformer\CouponToCodeTransformer
class.
This data transformer is used by default with the Sylius\Bundle\PromotionBundle\Form\Type\CouponToCodeType
form, provided as the service sylius.form.type.promotion_coupon_to_code.
Note:
An
example
of
integration
of
this
form
can
be
found
in
Sylius\Bundle\CoreBundle\Form\Type\CartType class of Sylius/Standard-Edition.

the

Coupon controller

The Sylius\Bundle\PromotionBundle\Controller\CouponController provides an interface for


easily generating new coupons.
Summary
sylius_promotion:
# The driver used for persistence layer.
driver: ~
classes:
promotion:
model: Sylius\Component\Promotion\Model\Promotion
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\PromotionBundle\Form\Type\PromotionType
promotion_rule:
model: Sylius\Component\Promotion\Model\Rule
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\PromotionBundle\Form\Type\RuleType
promotion_action:
model: Sylius\Component\Promotion\Model\Action
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\PromotionBundle\Form\Type\ActionType
promotion_coupon:
model: Sylius\Component\Promotion\Model\Coupon
controller: Sylius\Bundle\PromotionBundle\Controller\CouponController
repository: ~
form: Sylius\Bundle\PromotionsBundle\Form\Type\CouponType
promotion_subject:
model: ~

7.1. Symfony2 Ecommerce Bundles

165

Sylius, Release

controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
validation_groups:
promotion: [sylius]
promotion_rule: [sylius]
promotion_coupon: [sylius]
promotion_action: [sylius]
promotion_rule_item_total_configuration: [sylius]
promotion_rule_item_count_configuration: [sylius]
promotion_rule_user_loyality_configuration: [sylius]
promotion_rule_shipping_country_configuration: [sylius]
promotion_rule_taxonomy_configuration: [sylius]
promotion_rule_nth_order_configuration: [sylius]
promotion_action_fixed_discount_configuration: [sylius]
promotion_action_percentage_discount_configuration: [sylius]
promotion_action_add_product_configuration: [sylius]
promotion_coupon_generate_instruction: [sylius]
promotion_action_shipping_discount_configuration: [sylius]

phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This bundle uses GitHub issues. If you have found bug, please create an issue.

7.1.13 SyliusRbacBundle
Role-Based-Access-Control for Symfony2 applications, integrated with Doctrine ORM. Managable via UI.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download the package.
If you have Composer installed globally.
$ composer require sylius/rbac-bundle:0.15.0

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require sylius/rbac-bundle:0.15.0

Adding required bundles to the kernel

You need to enable the bundle inside the kernel.


If youre not using any other Sylius bundles, you will also need to add SyliusResourceBundle and its dependencies
to kernel. This bundle also uses DoctrineCacheBundle. Dont worry, everything was automatically installed via
Composer.

166

Chapter 7. Bundles

Sylius, Release

<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle(),
new Sylius\Bundle\RbacBundle\SyliusRbacBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
);
}

Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first.

Container configuration

Put this configuration inside your app/config/config.yml.


sylius_rbac:
driver: doctrine/orm # Configure the doctrine orm driver used in the documentation.

And configure doctrine extensions which are used by the bundle.


stof_doctrine_extensions:
orm:
default:
timestampable: true

Implement IdentityInterface

Your User class needs to implement Sylius\Component\Rbac\Model\IdentityInterface and define


associated roles.
<?php
// src/App/AppBundle/Entity/User.php
namespace App\AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Sylius\Component\Rbac\Model\IdentityInterface;
use Sylius\Component\Rbac\Model\RoleInterface;
class User implements IdentityInterface
{
private $authorizationRoles;

7.1. Symfony2 Ecommerce Bundles

167

Sylius, Release

public function __construct()


{
$this->authorizationRoles = new ArrayCollection();
}
public function getAuthorizationRoles()
{
return $this->authorizationRoles;
}
// Your methods for adding/removing roles.
}

Mapping the relation


Updating database schema

Run the following command.


$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.
Congratulations! The bundle is now installed and ready to configure your first roles and permissions.
Setup
...
Define Permissions

In your app/config/config.yml, under sylius_rbac you should define your permissions:


# app/config/config.yml
sylius_rbac:
permissions:
app.product.update: Update product

Configure Basic Roles

In your app/config/config.yml, under sylius_rbac you should configure the roles:


# app/config/config.yml
sylius_rbac:
roles:
app.product_manager:
name: Product Manager
permissions: app.product.update

168

Chapter 7. Bundles

Sylius, Release

Thats it! Now you have to initialize the roles and permission in the database.
Setup Roles and Permissions in the Database

Run the following command:


$ app/console sylius:rbac:initialize

Assign roles to the user.


Checking Permissions
Service sylius.authorization_checker is responsible for granting access.
In Controller
namespace App\Bundle\AppBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class YourController extends Controller
{
public function securedAction(Request $request)
{
if (!$this->get('sylius.authorization_checker')->isGranted('your.permission.code')) {
throw new AccessDeniedHttpException();
}
}
}

In Templates
{% if sylius_is_granted('your.permission.code') %}
{{ product.price }}
{% endif %}

Configuration reference
sylius_rbac:
driver: ~ # The driver used for persistence layer. Currently only `doctrine/orm` is supported.
authorization_checker: sylius.authorization_checker.default
identity_provider: sylius.authorization_identity_provider.security
permission_map: sylius.permission_map.cached
security_roles:
ROLE_ADMINISTRATION_ACCESS: Can access backend
roles:
app.admin:
name: Administrator
app.cash_manager:

7.1. Symfony2 Ecommerce Bundles

169

Sylius, Release

name: Cash Manager


description: People responsible for managing money.
permissions: [app.view_cash, app.add_cash, app.remove_cash]
security_roles: [ROLE_ADMINISTRATION_ACCESS]
roles_hierarchy:
app.admin: [app.cash_manager]
permissions:
app.view_cash: View cash
app.add_cash: Add cash
app.remove_cash: Remove cash
app.manage_cash: Manage cash
permissions_hierarchy:
app.manage_cash: [app.view_cash, app.add_cash, app.remove_cash]

classes:
role:
model:
Sylius\Component\Rbac\Model\Role # The role model class implementing `RoleI
repository: ~ # Is set automatically if empty.
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
form:
default: Sylius\Bundle\RbacBundle\Form\Type\RoleType
permission:
model:
Sylius\Component\Rbac\Model\Permission # The permission model class impleme
repository: ~ # Is set automatically if empty.
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
form:
default: Sylius\Bundle\RbacBundle\Form\Type\PermissionType
validation_groups:
role: [sylius]
permission: [sylius]

7.1.14 SyliusReportBundle
SyliusReportBundle is a flexible reports management system for Symfony2 applications.
Using this bundle, you can generate various and complex reports for your system, starting from user registrations
report or total sales report. Implemented renderers allows you to display data in many different ways - from simple
but clear table, to impressive and colorful charts.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download the package.
If you have Composer installed globally.
$ composer require "sylius/report-bundle"

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require "sylius/report-bundle"

170

Chapter 7. Bundles

Sylius, Release

Adding required bundles to the kernel

You need to enable the bundle inside the kernel.


If youre not using any other Sylius bundles, you will also need to add SyliusResourceBundle and its dependencies to
kernel. Dont worry, everything was automatically installed via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Sylius\Bundle\ProductBundle\SyliusReportBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
);
}

Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first.

Container configuration

Put this configuration inside your app/config/config.yml.


sylius_report:
driver: doctrine/orm # Configure the doctrine orm driver used in the documentation.

Routing configuration

Add the following to your app/config/routing.yml.


sylius_report:
resource: @SyliusReportBundle/Resources/config/routing.yml

Updating database schema

Run the following command.


$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.
Congratulations! The bundle is now installed and ready to use.
7.1. Symfony2 Ecommerce Bundles

171

Sylius, Release

Forms
The bundle ships with a useful form types for report model, but also for default renderers and data fetchers.
Report form

The report form type is named sylius_report and you can create it whenever you need, using the form factory.
<?php
// src/Acme/DemoBundle/Controller/DemoController.php
namespace Acme\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DemoController extends Controller
{
public function fooAction(Request $request)
{
$form = $this->get('form.factory')->create('sylius_report');
}
}

The default report form consists of following fields.


Field
name
code
description
renderer
dataFetcher

Type
text
text
textarea
choice
choice

You can render each of these using the usual Symfony way {{ form_row(form.description) }}.
SyliusReportBundle provides some default data fetchers and renderers. Each of them have custom configuration and adds diffrent part to report form.
Data fetchers Basic data fetcher extend same time provider. This implementation results in same configuration
fields for three different data fetchers.
Caution:
database.

Default data fetchers are part of SyliusCoreBundle, cause they are strictly connected with Sylius

Already available data fetchers:


User registrations
Sales total
Numbers of orders
Field
Start date
End date
Time period
Print empty records?

172

Type
datetime
datetime
choice
checkbox

Chapter 7. Bundles

Sylius, Release

Already available time periods:


Daily
Monthly
Yearly
Note: Print empty records? is inconspicuous, but really important part of data fetcher form - it can make your
report beautiful and clear, or ruin your day with tones of unusable data. Be aware of it!

User registrations Provides statistic about user registration in time period


Sales total Provides statistic about total completed sales over time
Number of orders Provides statistic about number of completed orders over time
Renderers

Table Renderer

Field
template

Type
choice

Already available templates:


Default - one simple table

Chart Renderer

Field
type
template

Type
choice
choice

Already available types:


Bar chart
Line chart
Radar chart
Polar chart
Pie chart
Doughnut chart
Note: All chart are rendered at html5 canvas element, with some defaults style and colors, via Chart.js plugin
Already available templates:
Default - one, full-width chart
Adding custom data fetcher
SyliusReportBundle has some default data fetchers implemented, however, obviously they can be insufficient for some
e-commerce systems. This chapter shows step-by-step way, how to add new data fetcher to make reports customization
easier and more corresponding to your needs.

7.1. Symfony2 Ecommerce Bundles

173

Sylius, Release

Create custom data fetcher class

First step is creation of our new data fetcher class. It should provide proper logic and has to implement Sylius\Component\
fetch(array $configuration), which fetches data
getType, which returns unique name of data fetcher
Note: It is highly recommended to place all data fetchers types as constants. Default data fetchers have their types
included in Sylius\Component\Report\Renderer\DefaultDataFetchers
Caution: Data fetcher has to return Data class(Sylius\Component\Report\DataFetcher\Data),
which is part of Report component
<?php
namespace Acme\DemoBundle\DataFetchers\CustomDataFetcher;
use Sylius\Component\Report\DataFetcher\DataFetcherInterface;
use Sylius\Component\Report\DataFetcher\Data;
use Acme\Component\DataFetcher\DefaultRenderers;
class CustomDataFetcher implements DataFetcherInterface
{
/**
* {@inheritdoc}
*/
public function fetch(array $configuration)
{
$data = new Data();
//Some operations, that will provide data for your renderer
return $data;
}
/**
* {@inheritdoc}
*/
public function getType()
{
return DefaultDataFetchers::CUSTOM;
}
}

Create data fetcher configuration type

Each data fetcher has its own, specific cofiguration form, which is added to main report form. It has to extend
Symfony\Component\Form\AbstractType. To be able to configure our data fetcher in form, this class should
override buildForm(FormBuilderInterface $builder, array $options) method. It should also
have getName method, that returns data fetcher string identifier.

174

Chapter 7. Bundles

Sylius, Release

<?php
namespace Acme\DemoBundle\Form\Type\DataFetcher;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class CustomConfigurationType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
//custom options you want to provide in your custom data fetcher
))
;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'acme_data_fetcher_custom';
}
}

Register custom data fetcher class as service

To be able to use our new data fetcher, it must be registered as service in our services file. We should take care of
two classes we just created, means CustomDataFetcher and CustomConfigurationType. They have to be
tagged with proper tags, to be visible for CompilerPass.

<parameters>
//other parameters
<parameter key="acme.report.data_fetcher.custom.class">Acme\DemoBundle\DataFetchers\CustomDataFet
<parameter key="acme.form.type.data_fetcher.custom.class">Acme\DemoBundle\Form\Type\DataFetcher\C
</parameters>

<services>
//other services
<service id="acme.report.data_fetcher.custom" class="%acme.report.data_fetcher.custom.class%">
<argument type="service" id="acme.repository.order" />
<tag name="sylius.report.data_fetcher" fetcher="custom" label="Custom data fetcher" />
</service>
<service id="acme.form.type.data_fetcher.custom" class="%acme.form.type.data_fetcher.custom.class
<tag name="form.type" alias="acme_data_fetcher_custom" />
</service>
</services>

7.1. Symfony2 Ecommerce Bundles

175

Sylius, Release

Summary

With this three simple steps, you can create your own, great data fetcher. Renderers can not wait for it.
Adding custom renderer
SyliusReportBundle has some default renderers implemented, however, obviously they can be insufficient for some
e-commerce systems. This chapter shows step-by-step way, how to add new renderer to make reports customization
easier and more corresponding to our needs.
Create custom renderer class

First step is creation of our new renderer class. It must implement Sylius\Component\Report\Renderer\RendererIn
render(ReportInterface $report, Data $data), which generates response based on
given report and data fetcher data
getType, which returns unique name of renderer
Note: It is highly recommended to place all renderers types as constants. Default renderers have their types included
in Sylius\Component\Report\Renderer\DefaultRenderers
<?php
namespace Acme\DemoBundle\Renderer\CustomRenderer;
use Sylius\Component\Report\Model\ReportInterface;
use Sylius\Component\Report\Renderer\RendererInterface;
use Acme\Component\Renderer\DefaultRenderers;
class CustomRenderer implements RendererInterface
{
public function render(ReportInterface $report, Data $data)
{
//Some operations on given data, that returns Response, which
//is next catched by controller and used to display view
}
public function getType()
{
return DefaultRenderers::CUSTOM;
}
}

Create renderer configuration type

Each renderer has its own, specific cofiguration form, which is added to main report form. It has to extend
Symfony\Component\Form\AbstractType. To be able to configure our renderer in form, this class should
override buildForm(FormBuilderInterface $builder, array $options) method. It should also
have getName method, that returns renderer string identifier.

176

Chapter 7. Bundles

Sylius, Release

<?php
namespace Acme\DemoBundle\Form\Type\Renderer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;

/**
* Custom renderer configuration form type
*/
class CustomConfigurationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('template', 'choice', array(
'label' => 'acme.form.report.renderer.template', //Default template label - "templat
'choices' => array(
'AcmeDemoBundle:Custom:default.html.twig' => 'Default',
),
))
;
}
public function getName()
{
return 'acme_renderer_custom';
}
}

Create renderer template

To be able to display your renderer, you should create its template or templates. Luckily, your renderer templates dont
have to provide all layout of report display page, only simple renderer view, which will be injected in report details
template. Thanks to that, all reports have systematized display, however each renderer move us to completely different
level of data perception.
Default renderers templates are placed in Sylius\Bundle\ReportBundle\Resources\views\ catalogue.
Register custom rednerer class as service

To be able to use our new renderer, it must be registered as service in our services file. We should take care of two
classes we just created, means CustomRenderer and CustomConfigurationType. They have to be tagged
with proper tags, to be visible for CompilerPass.

<parameters>
//other parameters
<parameter key="acme.renderer.custom.class">Acme\DemoBundle\Renderer\CustomRenderer</parameter>
<parameter key="acme.form.type.renderer.custom_configuration.class">Acme\DemoBundle\Form\Type\Ren
</parameters>
<services>
//other services
<service id="acme.renderer.custom" class="%acme.renderer.custom.class%">
<tag name="sylius.report.renderer" renderer="custom" label="Custom renderer" />

7.1. Symfony2 Ecommerce Bundles

177

Sylius, Release

</service>
<service id="acme.form.type.report.renderer.custom_configuration" class="%acme.form.type.report.r
<tag name="form.type" alias="sylius_renderer_custom" />
</service>
</services>

Summary

With this three simple steps, you can create your own, great renderer, which allows you to display fetched data however
you want.
Summary
Configuration reference
sylius_report:
driver: ~
classes:
report:
model: Sylius\Component\Report\Model\Report
costroller: Sylius\Bundle\ReportBundle\Controller\ReportController
repository: ~
form: Sylius\Bundle\ReportBundle\Form\Type\ReportType

Tests
$ composer install --dev --prefer-dist
$ phpunit

Working examples

If you want to see working implementation, try out the Sylius application.
Bug tracking

This bundle uses GitHub issues. If you have found bug, please create an issue.

7.1.15 SyliusResourceBundle
Easy CRUD and persistence for Symfony2 apps.
During our work on Sylius, we noticed a lot of duplicated code across all controllers. We started looking for a good
solution to the problem. Were not big fans of administration generators (theyre cool, but not for our use case!) - we
wanted something simpler and more flexible.
Another idea was to not limit ourselves to one persistence backend. Initial implementation included custom manager
classes, which was quite of an overhead, so we decided to simply stick with Doctrine Common Persistence interfaces.
If you are using Doctrine ORM or any of the ODMs, youre already familiar with those concepts. Resource bundle
relies mainly on the ObjectManager and ObjectRepository interfaces.
178

Chapter 7. Bundles

Sylius, Release

The last annoying problem this bundle tries to solve, is having separate backend and frontend controllers, or any
other duplication for displaying the same resource, with different presentation (view). We also wanted an easy way to
filter some resources from list, sort them or display them by id, slug or any other criteria - without having to define
another super simple action for that purpose.
If these are issues youre struggling with, this bundle may be helpful!
Please note that this bundle is not an admin generator. It wont create forms, filters and grids for you. It only
provides format agnostic controllers as foundation to build on, with some basic sorting and filter mechanisms.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download the package.
If you have Composer installed globally.
$ composer require "sylius/resource-bundle"

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require "sylius/resource-bundle"

Adding required bundles to the kernel

You just need to enable proper bundles inside the kernel.


<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle(),
);
}

To benefit from bundle services, you have to first register your bundle class as a resource.
You also need to enable HTTP method parameter override, by calling the following method on the Request object.
<?php
// web/app{_dev|_test}.php
use Symfony\Component\HttpFoundation\Request;
Request::enableHttpMethodParameterOverride();

7.1. Symfony2 Ecommerce Bundles

179

Sylius, Release

Configuring your resources


Now you need to configure your resources! It means that you will tell to this bundle which model, controller, repository, etc. will be used for each configured resources. It exists two ways for doing that, we will call them Basic
configuration and Advanced configuration. You need to use the first one if you want to build a simple application. It
is pretty easy because you just need to write configuration (in your config.yml, for example). The second one allows
you to embed configuration into your bundles but you will need to write some extra lines of code. We will explain the
both ways in the next chapters.
Basic configuration

In your app/config.yml (or in an imported configuration file), you need to define which resources you want to
use :
sylius_resource:
resources:
my_app.entity_name:
driver: doctrine/orm
object_manager: default
templates: App:User
classes:
model: MyApp\Entity\EntityName
interface: MyApp\Entity\EntityNameInterface
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository
my_app.document_name:
driver: doctrine/mongodb-odm
object_manager: default
templates: App:User
classes:
model: MyApp\Document\DocumentName
interface: MyApp\Document\DocumentNameInterface
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: Sylius\Bundle\ResourceBundle\Doctrine\ODM\DocumentRepository

At this step, we can see what happen in the container:


Advanced configuration

First you must list the supported doctrine driver by your bundle, the available drivers are:
SyliusResourceBundle::DRIVER_DOCTRINE_ORM
SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM
SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM
class MyBundle extends AbstractResourceBundle
{
public static function getSupportedDrivers()
{
return array(
SyliusResourceBundle::DRIVER_DOCTRINE_ORM
);
}
}

180

Chapter 7. Bundles

Sylius, Release

Note: Since the 0.11 your bundle class must implement ResourceBundleInterface. You can extends the
AbstractResourceBundle which already implements this interface, it will bring you extra functionalities too.
You need to expose a semantic configuration for your bundle.
Configuration that the resource bundle needs to work.

The following example show you a basic

class Configuration implements ConfigurationInterface


{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('bundle_name');
$rootNode
->children()
// Driver used by the resource bundle
->scalarNode('driver')->isRequired()->cannotBeEmpty()->end()
// Object manager used by the resource bundle, if not specified "default" will used
->scalarNode('object_manager')->defaultValue('default')->end()
// Validation groups used by the form component
->arrayNode('validation_groups')
->addDefaultsIfNotSet()
->children()
->arrayNode('MyEntity')
->prototype('scalar')->end()
->defaultValue(array('your_group'))
->end()
->end()
->end()

// Configure the template namespace used by each resource


->arrayNode('templates')
->addDefaultsIfNotSet()
->children()
->scalarNode('my_entity')->defaultValue('MyCoreBundle:Entity')->end()
->scalarNode('my_other_entity')->defaultValue('MyOtherCoreBundle:Entity')->en
->end()
->end()

// The resources
->arrayNode('classes')
->addDefaultsIfNotSet()
->children()
->arrayNode('my_entity')
->addDefaultsIfNotSet()
->children()
->scalarNode('model')->defaultValue('MyApp\MyCustomBundle\Model\MyEnt
->scalarNode('controller')->defaultValue('Sylius\Bundle\ResourceBundl
->scalarNode('repository')->end()
->scalarNode('form')->defaultValue('MyApp\MyCustomBundle\Form\Type\My
->end()
->end()
->arrayNode('my_other_entity')
->addDefaultsIfNotSet()

7.1. Symfony2 Ecommerce Bundles

181

Sylius, Release

->children()
->scalarNode('model')->defaultValue('MyApp\MyCustomBundle\Model\MyOth
->scalarNode('controller')->defaultValue('Sylius\Bundle\ResourceBundl
->scalarNode('form')->defaultValue('MyApp\MyCustomBundle\Form\Type\My
// you can use an array, useful when you want to register the choice
->arrayNode('form')
->addDefaultsIfNotSet()
->children()
->scalarNode('default')->defaultValue('MyApp\MyCustomBundle\F
->scalarNode('choice')->defaultValue('MyApp\MyCustomBundle\Fo
->end()
->end()
->end()
->end()
->end()
->end()
->end()
;
return $treeBuilder;
}
}

The resource bundle provide you AbstractResourceExtension, your bundle extension have to extends it.
use Sylius\Bundle\ResourceBundle\DependencyInjection\AbstractResourceExtension;

class MyBundleExtension extends AbstractResourceExtension


{
// You can choose your application name, it will use to prefix the configuration keys in the cont
protected $applicationName = 'my_app';
// You can define where yours service definitions are
protected $configDirectory = '/../Resources/config';
// You can define what service definitions you want to load
protected $configFiles = array(
'services',
'forms',
);
// You can define the file formats of the files loaded
protected $configFormat = self::CONFIG_XML;

public function load(array $config, ContainerBuilder $container)


{
$this->configure(
$config,
new Configuration(),
$container,
self::CONFIGURE_LOADER | self::CONFIGURE_DATABASE | self::CONFIGURE_PARAMETERS | self::CO
);
}
}

The last parameter of the AbstractResourceExtension::configure() allows you to define what functionalities you want to use :
CONFIGURE_LOADER : load yours service definitions located in $applicationName

182

Chapter 7. Bundles

Sylius, Release

CONFIGURE_PARAMETERS : set to the container the configured resource classes


using
the
pattern
my_app.serviceType.resourceName.class
For
example
:
sylius.controller.product.class. For a form, it is a bit different : sylius.form.type.product.class
CONFIGURE_VALIDATORS : set to the container the configured validation groups using the pattern
my_app.validation_group.modelName For example sylius.validation_group.product
CONFIGURE_DATABASE : Load the database driver,
doctrine/mongodb-odm and doctrine/phpcr-odm

available drivers are doctrine/orm,

CONFIGURE_FORMS : Register the form as a service (you must register the form as array)
At this step:

$ php app/console container:debug | grep my_entity


my_app.controller.my_entity
container Sylius\Bundle\ResourceBundle\Controller\ResourceCo
my_app.form.type.my_entity
container MyApp\MyCustomBundle\Form\Type\TaxonomyType
my_app.manager.my_entity
n/a
alias for doctrine.orm.default_entity_manager
my_app.repository.my_entity
container MyApp\MyCustomer\ModelRepository
//...
$ php app/console container:debug --parameters | grep my_entity
my_app.config.classes
{...}
my_app.controller.my_entity.class
MyApp\MyCustomBundle\ModelController
my_app.form.type.my_entity.class
MyApp\MyCustomBundle\FormType
my_app.model.my_entity.class
MyApp\MyCustomBundle\Model
my_app.repository.my_entity.class
MyApp\MyCustomBundle\ModelRepository
my_app.validation_group.my_entity
["my_app"]
my_app_my_entity.driver
doctrine/orm
my_app_my_entity.driver.doctrine/orm
true
my_app_my_entity.object_manager
default
//...

You can overwrite the configuration of your bundle like that :


bundle_name:
driver: doctrine/orm
object_manager: my_custom_manager
validation_groups:
my_entity: [myCustomGroup]
templates:
my_entity: AppBundle:Backend/MyEntity
classes:
my_entity:
model: MyApp\MyOtherCustomBundle\Model
controller: MyApp\MyOtherCustomBundle\Entity\ModelController
repository: MyApp\MyOtherCustomBundle\Repository\ModelRepository
form: MyApp\MyOtherCustomBundle\Form\Type\FormType

Note: Caution: Your form is not declared as a service for now.

Combining the both configurations

For now, with the advanced configuration you can not use several drivers but they can be overwritten. Example,
you want to use doctrine/odm for my_other_entity (see previous chapter), you just need to add this extra
configuration to the app/config.yml.

7.1. Symfony2 Ecommerce Bundles

183

Sylius, Release

sylius_resource:
resources:
my_app.other_entity_key:
driver: doctrine/odm
object_manager: my_custom_manager
classes:
model: %my_app.model.my_entity.class%

And your manager will be overwrite:

$ php app/console container:debug | grep my_app.object_manager.other_entity_key


my_app.object_manager.other_entity_key
n/a
alias for doctrine.odm.my_custom_manager_docum

And... were done!


Getting a single resource
Note: ResourceController class is built using FOSRestBundle, thus its format agnostic, and can serve resources in
many formats, like html, json or xml.
Your newly created controller service has a few basic crud actions and is configurable via routing, which allows you
to do some really tedious tasks - easily.
The most basic action is showAction. It is used to display a single resource. To use it, the only thing you need to do
is register a proper route.
# routing.yml
app_user_show:
path: /users/{id}
methods: [GET]
defaults:
_controller: app.controller.user:showAction

Done! Now when you go to /users/3, ResourceController will use the repository (app.repository.user) to
find user with given id. If the requested user resource does not exist, it will throw a 404 exception.
When a user is found, the default template will be rendered - App:User:show.html.twig (like you configured
it in config.yml) with the User result as the user variable. Thats the most basic usage of the simple showAction
action.
Using a custom template

Okay, but what if you want now to display same User resource, but with a different representation?
# routing.yml
app_backend_user_show:
path: /backend/users/{id}
methods: [GET]
defaults:
_controller: app.controller.user:showAction
_sylius:
template: App:Backend/User:show.html.twig

184

Chapter 7. Bundles

Sylius, Release

Nothing more to do here, when you go to /backend/users/3, the controller will try to find the user and render it
using the custom template you specified under the route configuration. Simple, isnt it?
Overriding default criteria

Displaying the user by id can be boring... and lets say we do not want to allow viewing disabled users? There is a
solution for that!
# routing.yml
app_user_show:
path: /users/{username}
methods: [GET]
defaults:
_controller: app.controller.user:showAction
_sylius:
criteria:
username: $username
enabled: true

With this configuration, the controller will look for a user with the given username and exclude disabled users. Internally, it simply uses the $repository->findOneBy(array $criteria) method to look for the resource.
Using custom repository methods

By default, resource repository uses findOneBy(array $criteria), but in some cases its not enough - for example you want to do proper joins or use a custom query. Creating yet another action to change the method called could be
a solution but there is a better way. The configuration below will use a custom repository method to get the resource.
# routing.yml
app_user_show:
path: /users/{username}
methods: [GET]
defaults:
_controller: app.controller.user:showAction
_sylius:
repository:
method: findOneWithFriends
arguments: [$username]

Internally, it simply uses the $repository->findOneWithFriends($username) method, where


username is taken from the current request.
Getting a paginated (or flat) list of resources
To get a paginated list of users, we will use indexAction of our controller! In the default scenario, it will return an
instance of paginator, with a list of Users.
# routing.yml
app_user_index:
path: /users
methods: [GET]

7.1. Symfony2 Ecommerce Bundles

185

Sylius, Release

defaults:
_controller: app.controller.user:indexAction

When you go to /users, ResourceController will use the repository (app.repository.user) to create a
paginator. The default template will be rendered - App:User:index.html.twig with the paginator as the
users variable. A paginator can be a simple array if you disable the pagination otherwise it is a instance of
Pagerfanta\Pagerfanta which is the library used to manage the pagination.
Overriding the template and criteria

Just like for the showAction, you can override the default template and criteria.
# routing.yml
app_user_index_inactive:
path: /users/inactive
methods: [GET]
defaults:
_controller: app.controller.user:indexAction
_sylius:
criteria:
enabled: false
template: App:User:inactive.html.twig

This action will render a custom template with a paginator only for disabled users.
Sorting collection or paginator

Except filtering, you can also sort users.


# routing.yml
app_user_index_top:
path: /users/top
methods: [GET]
defaults:
_controller: app.controller.user:indexAction
_sylius:
sortable: true
sorting:
score: desc
template: App:User:top.html.twig

Under that route, you can paginate over the users by their score.
Using a custom repository method

You can define your own repository method too, you can use the same way explained in show_resource.
Note:
If
you
want
to
paginate
your
resources
you
need
to
use
EntityRepository::getPaginator($queryBuilder). It will transform your doctrine query builder into
Pagerfanta\Pagerfanta object.

186

Chapter 7. Bundles

Sylius, Release

Changing the max per page option of paginator

You can also control the max per page for paginator, using paginate parameter.
# routing.yml
app_user_index_top:
path: /users/top
methods: [GET]
defaults:
_controller: app.controller.user:indexAction
_sylius:
paginate: 5
sortable: true
sorting:
score: desc
template: App:User:top.html.twig

This will paginate users by 5 per page, where 10 is the default.


Disabling pagination - getting flat list

Pagination is handy, but you do not always want to do it, you can disable pagination and simply request a collection
of resources.
# routing.yml
app_user_index_top3:
path: /users/top
methods: [GET]
defaults:
_controller: app.controller.user:indexAction
_sylius:
paginate: false
limit: 3
sortable: true
sorting:
score: desc
template: App:User:top3.html.twig

That action will return the top 3 users by score, as the users variable.
Updating the position of your resource

You need to define two routes, they will use to update the position of the resource.
# routing.yml
my_route_move_up:
pattern: /{id}/move-up
methods: [PUT]
defaults:
_controller: sylius.controller.resource:moveUpAction
_sylius:
redirect: referer
sortable_position: priority # the default value is position

7.1. Symfony2 Ecommerce Bundles

187

Sylius, Release

my_route_move_down:
pattern: /{id}/move-down
methods: [PUT]
defaults:
_controller: sylius.controller.resource:moveDownAction
_sylius:
redirect: referer
sortable_position: priority # the default value is position

You need to update your doctrine mapping :


<!-- resource.orm.xml -->
<field name="priority" type="integer">
<gedmo:sortable-position/>
</field>

In your template, you can use the macro move to print the move up and move down buttons:
{# index.html.twig #}
{% import 'SyliusResourceBundle:Macros:buttons.html.twig' as buttons %}

{{ buttons.move(path('my_route_move_up', {'id': resource.id}), 'up', loop.first and not resources.has


{{ buttons.move(path('my_route_move_down', {'id': resource.id}), 'down', loop.first and not resources

Listing tools

Sorting your resources (sylius_resource_sort) This TWIG extension renders the title of your columns (in your
table), it created the link used to sort your resources. You will need to enable it per route
# routing.yml
app_user_index:
path: /users
methods: [GET]
defaults:
_controller: app.controller.user:indexAction
sortable: true

or globally
# config.yml
sylius_resource:
settings:
sortable: true

188

Chapter 7. Bundles

Sylius, Release

Parameters

Parameter
property
label
order
options

Manda- Type Description


tory
YES

string Name of the property (attribute defined in your classes)

NO
NO
NO

string
string
array

Default order, it can be asc or desc (default : asc)


Unique id of the address
Additional options : template (string) : Path to the template route (string) : Key of
the new route route_params (array) : Additional route parameters

This extension renders the following template : SyliusResourceBundle:Twig:sorting.html.twig. You will need to
enable it per route
# routing.yml
app_user_index:
path: /users
methods: [GET]
defaults:
_controller: app.controller.user:indexAction
paginate: $paginate

or globally
# config.yml
sylius_resource:
settings:
paginate: $paginate

Example
<table>
<tr>

<td>
{{ sylius_resource_sort('productId', 'product.id'|trans) }}
</td>
<td>
{{ sylius_resource_sort('productName', 'product.name'|trans, 'desc', {'route': 'my_custom
</td>
</tr>
<table>

Number of item by page (sylius_resource_paginate) This TWIG extension renders a HTML select which allows
the user to choose how many items he wants to display in the page.

Parameters

Parameter
paginator
limits
options

Manda- Type Description


tory
YES

string An instance of PagerFanta

YES
NO

string An array of paginate value


arAdditional options : template (string) : Path to the template route (string) : Key of
ray the new route route_params (array) : Additional route parameters

This extension renders the following template : SyliusResourceBundle:Twig:paginate.html.twig


7.1. Symfony2 Ecommerce Bundles

189

Sylius, Release

Example
{{ sylius_resource_paginate(paginator, [10, 30, 50]) }}
<table>
<!-- ... -->
</table>
{{ sylius_resource_paginate(paginator, [10, 30, 50]) }}

Rendering pagination For now, you need to create your own macro, it could look like :
{% macro pagination(paginator, options) %}
{% if paginator.haveToPaginate()|default(false) %}
{{ pagerfanta(paginator, 'twitter_bootstrap3_translated', options|default({})) }}
{% endif %}
{% endmacro %}

Creating new resource


To display a form, handle submit or create a new resource via API, you should use createAction of your
app.controller.user service.
# routing.yml
app_user_create:
path: /users/new
methods: [GET, POST]
defaults:
_controller: app.controller.user:createAction

Done! Now when you go to /users/new, ResourceController will use the repository (app.repository.user)
to create new user instance. Then it will try to create an app_user form, and set the newly created user as data.
Note: Currently, this bundle does not generate a form for you, the right form type has to be created and registered in
the container manually.
As a response, it will render the App:User:create.html.twig template with form view as the form variable.
Declaring your form as a service

As said previously, you need to declare your form as a service. Its name must be contructed with the following pattern
application_resource (Example: sylius_product). (see the Configuration chapter to see how configure
your resources and your application name)

<parameters>
<!-- If you use the basic configuration this paramter is not available in the container. You need
<parameter key="app.form.type.user.class">App\Bundle\Form\UserType</parameter>
</parameters>
<services>
<service id="app.form.type.user" class="%app.form.type.user.class%">
<tag name="form.type" alias="app_user" />
</service>
</services>

190

Chapter 7. Bundles

Sylius, Release

Submitting the form

You can use exactly the same route to handle the submit of the form and create the user.
<form method="post" action="{{ path('app_user_create') }}">

On submit, the create action with method POST, will bind the request on the form, and if it is valid it will use the right
manager to persist the resource. Then, by default it redirects to app_user_show to display the created user, but you
can easily change that behavior - youll see this in later sections.
When validation fails, it will render the form just like previously with the errors to display.
Changing the template

Just like for the show and index actions, you can customize the template per route.
# routing.yml
app_user_create:
path: /users/new
methods: [GET, POST]
defaults:
_controller: app.controller.user:createAction
_sylius:
template: App:Backend/User:create.html.twig

Using different form

You can also use custom form type on per route basis. By default it generates the form type name following the simple
convention bundle prefix + _ + resource logical name. Below you can see the usage for specifying
a custom form.
# routing.yml
app_user_create:
path: /users/new
methods: [GET, POST]
defaults:
_controller: app.controller.user:createAction
_sylius:
template: App:Backend/User:create.html.twig
form: app_user_custom

or use directly a class.


# routing.yml
app_user_create:
path: /users/new
methods: [GET, POST]
defaults:
_controller: app.controller.user:createAction
_sylius:
template: App:Backend/User:create.html.twig
form: App\Bundle\Form\UserType

7.1. Symfony2 Ecommerce Bundles

191

Sylius, Release

Using custom factory method

By default, ResourceController will use the createNew method with no arguments to create a new instance
of your object. However, this behavior can be modified. To use different method of your repository, you can simply
configure the factory option.
# routing.yml
app_user_create:
path: /users/new
methods: [GET, POST]
defaults:
_controller: app.controller.user:createAction
_sylius:
factory: createNewWithGroups

Additionally, if you want to provide your custom method with arguments from the request, you can do so by adding
more parameters.
# routing.yml
app_user_create:
path: /users/{groupId}/new
methods: [GET, POST]
defaults:
_controller: app.controller.user:createAction
_sylius:
factory:
method: createNewWithGroups
arguments: [$groupId]

With this configuration, $repository->createNewWithGroups($request->get(groupId)) will


be called to create new resource within createAction.
Custom redirect after success

By default the controller will try to get the id of the newly created resource and redirect to the show route. You can
easily change that. For example, to redirect user to list after successfully creating a new resource - you can use the
following configuration.
# routing.yml
app_user_create:
path: /users/new
methods: [GET, POST]
defaults:
_controller: app.controller.user:createAction
_sylius:
redirect: app_user_index

You can also perform more complex redirects, with parameters. For example...
# routing.yml
app_user_create:
path: /competition/{competitionId}/users/new
methods: [GET, POST]

192

Chapter 7. Bundles

Sylius, Release

defaults:
_controller: app.controller.user:createAction
_sylius:
redirect:
route: app_competition_show
parameters: { id: $competitionId }

In addition to the request parameters, you can access some of the newly created objects properties, using the
resource. prefix.
# routing.yml
app_user_create:
path: /users/new
methods: [GET, POST]
defaults:
_controller: app.controller.user:createAction
_sylius:
redirect:
route: app_user_show
parameters: { email: resource.email }

With this configuration, the email parameter for route app_user_show will be obtained from your newly created
user.
Updating existing resource
To display an edit form of a particular resource, change it or update it via API, you should use the updateAction
action of your app.controller.user service.
# routing.yml
app_user_update:
path: /users/{id}/edit
methods: [GET, PUT]
defaults:
_controller: app.controller.user:updateAction

Done!
Now when you go to /users/5/edit, ResourceController will use the repository
(app.repository.user) to find the user with ID === 5. If found it will create the app_user form,
and set the existing user as data.
Note: Currently, this bundle does not generate a form for you, the right form type has to be updated and registered in
the container manually.
As a response, it will render the App:User:update.html.twig template with form view as the form variable
and the existing User as the user variable.
Submitting the form

You can use exactly the same route to handle the submit of the form and update the user.
<form method="post" action="{{ path('app_user_update', {'id': user.id}) }}">
<input type="hidden" name="_method" value="PUT" />

7.1. Symfony2 Ecommerce Bundles

193

Sylius, Release

On submit, the update action with method PUT, will bind the request on the form, and if it is valid it will use the right
manager to persist the resource. Then, by default it redirects to app_user_show to display the updated user, but
like for creation of the resource - its customizable.
When validation fails, it will simply render the form again.
Changing the template

Just like for other actions, you can customize the template.
# routing.yml
app_user_update:
path: /users/{id}/edit
methods: [GET, PUT]
defaults:
_controller: app.controller.user:updateAction
_sylius:
template: App:Backend/User:update.html.twig

Using different form

Same way like for createAction you can override the default form.
# routing.yml
app_user_update:
path: /users/{id}/edit
methods: [GET, PUT]
defaults:
_controller: app.controller.user:updateAction
_sylius:
template: App:Backend/User:update.html.twig
form: app_user_custom

Overriding the criteria

By default, the updateAction will look for the resource by id. You can easily change that criteria.
# routing.yml
app_user_update:
path: /users/{username}/edit
methods: [GET, PUT]
defaults:
_controller: app.controller.user:updateAction
_sylius:
criteria: { username: $username }

Custom redirect after success

By default the controller will try to get the id of resource and redirect to the show route. To change that, use the
following configuration.

194

Chapter 7. Bundles

Sylius, Release

# routing.yml
app_user_update:
path: /users/{id}/edit
methods: [GET, PUT]
defaults:
_controller: app.controller.user:updateAction
_sylius:
redirect: app_user_index

You can also perform more complex redirects, with parameters. For example...
# routing.yml
app_user_update:
path: /competition/{competitionId}/users/{id}/edit
methods: [GET, PUT]
defaults:
_controller: app.controller.user:updateAction
_sylius:
redirect:
route: app_competition_show
parameters: { id: $competitionId }

Deleting resource
Deleting a resource is simple.
# routing.yml
app_user_delete:
path: /users/{id}
methods: [DELETE]
defaults:
_controller: app.controller.user:deleteAction

Calling the action with method DELETE

Currently browsers do not support the DELETE http method. Fortunately, Symfony has a very useful feature. You
can make a POST call with override parameter, which will force the framework to treat the request as specified method.
<form method="post" action="{{ path('app_user_delete', {'id': user.id}) }}">
<input type="hidden" name="_method" value="DELETE" />
<button type="submit">
Delete
</button>
</form>

On submit, the delete action with the method DELETE, will remove and flush the resource. Then, by default it redirects
to app_user_index to display the users index, but like for other actions - its customizable.
Overriding the criteria

By default, the deleteAction will look for the resource by id. However, you can easily change that. For example, you
want to delete the user who belongs to particular company, not only by his id.
7.1. Symfony2 Ecommerce Bundles

195

Sylius, Release

# routing.yml
app_user_delete:
path: /companies/{companyId}/users/{id}
methods: [DELETE]
defaults:
_controller: app.controller.user:deleteAction
_sylius:
criteria:
id:
$id
company: $companyId

There are no magic hacks behind that, it simply takes parameters from request and builds the criteria array for the
findOneBy repository method.
Custom redirect after success

By default the controller will try to get the id of the resource and redirect to the index route. To change that, use the
following configuration.
# routing.yml
app_user_delete:
path: /competition/{competitionId}/users/{id}
methods: [DELETE]
defaults:
_controller: app.controller.user:deleteAction
_sylius:
redirect:
route: app_competition_show
parameters: { id: $competitionId }

Managing flash messages


By default the resource bundle generate default messages for each action. They use the following translation key
pattern sylius.resource.actionName (actionName can be create, update and delete).
You can manage flash messages by resource, the following pattern appName.resource.actionName is used to translate
them.
appName: the name of your application (its default value is sylius)
resource: the name of your resource
actionName: the current action (create, update or delete)
Example:
sylius_resource:
resources:
my_app.entity_key:
driver: doctrine/orm
templates: App:User
classes:
model: MyApp\Entity\EntityName
interface: MyApp\Entity\EntityKeyInterface
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository

196

Chapter 7. Bundles

Sylius, Release

For the current this resource, you can use my_app.entity_key.create, my_app.entity_key.update
and my_app.entity_key.delete in your translation files.
Note:
Caution: The domain used for translating flash messages is flashes.
flashes.locale.yml (Exemple: flashes.en.yml).

You need to create your own

Doctrine tools
This bundle allow you easy usage of two extra doctrine tools: MappingDriver and ResolveDoctrineTargetEntitiesPass.
The first one allow you to put your models (entities, document, etc) and their mappings in specific directories. The
second one define relationships between different entities without making them hard dependencies. We will explain
how you can enable them in the next chapters.
Note: Caution : these tools are facultatives!

Creating a MappingDriver
class MyBundle extends AbstractResourceBundle
{
// You can specify your mapping format (xml, yaml, annotation), by defaut xml is used.
protected $mappingFormat = ResourceBundleInterface::MAPPING_XML;
// You need to specify a prefix for your bundle
protected function getBundlePrefix()
{
return 'app_bundle_name';
}
// You need specify the namespace where are stored your models
protected function getModelNamespace()
{
return 'MyApp\MyBundle\Model';
}

// You can specify the path where are stored the doctrine mapping, by default this method will re
// model. This path is relative to the Resources/config/doctrine/.
protected function getDoctrineMappingDirectory()
{
return 'model';
}
}

Using the ResolveDoctrineTargetEntitiesPass


class MyBundle extends AbstractResourceBundle
{
// You need to specify a prefix for your bundle
protected function getBundlePrefix()
{
return 'app_bundle_name';

7.1. Symfony2 Ecommerce Bundles

197

Sylius, Release

// You need to specify the mapping between your interfaces and your models. Like the following ex
// get the classname of your model in the container (See the following chapter for more informati
protected function getModelInterfaces()
{
return array(
'MyApp\MyBundle\ModelInterface' => 'sylius.model.resource.class',
);
}
}

Doctrine mapping

This bundle use the loadClassMetadata doctrine event which occurs after the mapping metadata for a class has
been loaded from a mapping source (annotations/xml/yaml). Every models can be declared as mapped superclass,
this listener will transform them in an entity or document if they have not child.
With this following mapping, Doctrine will create the table my_table with the column name.
<!-- Resource/config/Model.orm.xml-->
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
<mapped-superclass name="My/Bundle/Model" table="my_table">
<id name="id" column="id" type="integer">
<generator strategy="AUTO" />
</id>
<field name="name" column="name" type="string" />
</mapped-superclass>
</doctrine-mapping>

If you want to add an extra field, you can create a new model which extends My/Bundle/Model and its doctrine
mapping like that :
<!-- Resource/config/NewModel.orm.xml-->
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
<entity name="My/OtherBundle/NewModel" table="my_new_table">
<field name="description" column="name" type="string" />
</entity>
</doctrine-mapping>

Note: This functionality works for Doctrine ORM and ODM.

Extra tools
Parameters parser

For each route defined the parameter parser allow you to find extra data in the current request, in the resource previously
created by using the PropertyAccess component or by using the Expression Language component

198

Chapter 7. Bundles

Sylius, Release

Request You need use the following syntax $var_name. It will try to find in the request the key named var_name
$request->get(var_name).
# routing.yml
app_user_index:
path: /products
methods: [GET]
defaults:
_controller: app.controller.user:indexAction
_sylius:
criteria: {name: $name}

Resource previously created You need use the following syntax resource.attribute_name. It will try to find the
value of the attribute name named attribute_name $accessor->getValue($resource, attribute_name)).
# routing.yml
app_user_index:
path: /products
methods: [POST]
defaults:
_controller: app.controller.user:indexAction
_sylius:
redirect:
route: my_route
parameters: {name: resource.name}

Expression Language component You need use the following syntax expr:resource.my_expression.
# routing.yml
app_user_index:
path: /customer/orders
methods: [POST]
defaults:
_controller: app.controller.user:indexAction
_sylius:
repository:
method: findOrderByCustomer
arguments: ['expr:service("security.context").getToken().getUser()']

Note:
service: returns a given service (see the example above);
parameter: returns a specific parameter value, aka: expr:parameter("sylius.locale")

Event Dispatcher

The methods ResourceController:createAction,


ResourceController:updateAction
and ResourceController:deleteAction throw events before and after executing any
actions on the current resource.
The name of the events used the following pattern
app_name.resource.(pre|post)_(create|update|delete).

7.1. Symfony2 Ecommerce Bundles

199

Sylius, Release

First, you need to register event listeners, the following example show you how you can do that.
# services.xml
<service id="my_listener" class="MyBundle\MyEventListener">
<tag name="kernel.event_listener" event="sylius.order.pre_create" method="onOrderPreCreate"/>
</service>
# services.yml

services:
my_listener:
class: MyBundle\MyEventListener
tags:
- { name: kernel.event_listener, event: sylius.order.pre_create, method: onOrderPreCreate

After that, you need to create your listener


class MyEventListener
{
public function onOrderPreCreate(ResourceEvent $event)
{
// You can get your resource like that
$resource = $event->getSubject();
// You can stop propagation too.
$event->stop('my.message', array('%amount%' => $resource->getAmount()));
}
}

Note: Caution: you can use subscribers too, you can get more informations there.

Custom actions
Note: To be written.

Building a API
Header parameter parser

The header parameter parser will find the version and the validation group in the HTTP headers to give them to the
JmsSerializerBundle
Accept: application/json; version=1.0.1; groups=Sylius,Details

The previous header will be translated to :


$handler = $this->get('fos_rest.view_handler');
$handler->setExclusionStrategyVersion('1.0.1');
$handler->setExclusionStrategyGroups(array('Sylius', 'Details'));

In serializer configuration files, you can filter the data that you want to send by using the field called
since_version, until_version and group. The following example show you how it works. The lastname
wont be return after the version 0.3 and the firstname will be returned after the version 0.2. You can play with
groups to return a part of data depending on specifics cases like the symfony form.
200

Chapter 7. Bundles

Sylius, Release

# src/App/Bundle/YourBundle/Resources/serializer/Entity.User.yml
App\Bundle\YourBundle\Entity\User:
exclusion_policy: ALL
properties:
id:
expose: true
type: integer
groups: [Sylius, Details]
firstName:
expose: true
type: string
since_version: 0.2
groups: [Sylius]
lastName:
expose: true
type: string
until_version: 0.3
groups: [Details]

More details about serializer can be found in its official documentation.


Api Loader

Routes for your API will be automatically generated by ApiLoader class. The resource is the concatenation of the
application and resource name.
api_resource:
resource: my_api.resource
type: sylius.api

It will generate the following routes :


my_app_api_resource_index
my_app_api_resource_show
my_app_api_resource_create
my_app_api_resource_update
my_app_api_resource_delete

GET
GET
POST
PUT|PATCH
DELETE

ANY
ANY
ANY
ANY
ANY

ANY
ANY
ANY
ANY
ANY

/resources/
/resources/{id}
/resources/
/resources/{id}
/resources/{id}

Extra Form types


Collection form extension

Two options are added to the basic collection form type :


button_add_label (default : form.collection.add) : Label of the addition button
button_delete_label (default : form.collection.delete) : Label of the deletion button
HTML markup The container element needs to have data-form-type=collection as html attribute. It is used to
enable the form collection plugin. All data attributes beginning by data-form-collection are used by the plugin and
should not be removed.

7.1. Symfony2 Ecommerce Bundles

201

Sylius, Release

<div data-form-type="collection" data-prototype='...'>


<div data-form-collection="list" class="row collection-list">
<input type="hidden" data-form-prototype="prototypeName" value="..." />
<div data-form-collection="item"
data-form-collection-index="XX"
class="col-md-{{ boxWidth }} collection-item">
<!-- Put here your sub form -->
<a href="#" data-form-collection="delete">
Delete
</a>
</div>
</div>
<a href="#" data-form-collection="add">
Add
</a>
</div>

List of HTML attributes :


data-prototype : form prototype
data-form-collection=list : container of the list of the collection item
data-form-collection=item : container of the collection item
data-form-collection-index=XX : index of the current collection item
data-form-collection=delete : HTML element used to remove collection item
data-form-collection=add : HTML element used to add a new collection item using the form prototype.
data-form-collection=update : HTML element used to update the collection item when on change
event is fired. Element has to belong to the current collection item.
data-form-prototype=update : HTML element used to update the form prototype when change event
is fired.
Updating dynamically the form Prototype When a HTML element which has data-form-prototype=update as
HTML attribute fired change event, the plugin will find the new prototype from the server if the data-form-url=Url
is specified. The value of the input/select and the position of the collection item will be submitted to the server.
<div data-form-collection="item" data-form-collection-index="1">
<select data-form-prototype="update" data-form-url="example.com/update">
<option value="rule">Rule</option>
<option value="action">Action</option>
</select>
</div>

In this example, the value of the select and the position will be sent to the server.
Another way exists, hidden inputs can be used too if you dont want to generate the prototype by the server. You
need to insert as many hidden inputs as select options in the page. They need to have attribute like data-formprototype=prototypeName. prototypeName needs to match to one of all the select options.

202

Chapter 7. Bundles

Sylius, Release

<div data-form-collection="item" data-form-collection-index="1">


<input type="hidden" data-form-prototype="rule" value="..." />
<input type="hidden" data-form-prototype="action" value="..." />
<select data-form-prototype="update">
<option value="rule">Rule</option>
<option value="action">Action</option>
</select>
</div>

In this example, when you select Rule, the plugin will replace the current form prototype by the value of the hidden
input which has data-form-prototype=rule.
Activation You need to use the jquery plugin and the form theme provided by this bundle.
{% javascripts
'bundles/syliusresource/js/form-collection.js'
%}
<script type="text/javascript" src="{{ asset(asset_url) }}"></script>
{% endjavascripts %}
twig:
form:
resources:
- SyliusResourceBundle::form-collection.html.twig

Entity hidden type

It creates a input type hidden, its value will be the identifier of the resource. you need to specify the class (data_class)
will be used and the property (identifier) that you want to get.
In the following example, we will add the sku of the product in the form.
ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('product', 'entity_hidden', array(
'data_class' => 'App\Bundle\Product\Model\Product'
'identifier' => 'sku'
))
;
}
}

The symfony form type will render this HTML :


<input type="hidden" name="product" value="132FDQS12" />

Resource choice type

It creates a document or entity or phpcr_document form type depending on the driver used by the resource. You need
to register it as a service, its contructor requires three paramters, the first one is the FQDN of the resource, the second

7.1. Symfony2 Ecommerce Bundles

203

Sylius, Release

one is the driver used by it and the last on is the name of the form type.
services:
app.form.type.entity:
class:
argument:
- App/Bundle/Model/Product
- doctrine/orm
- product_choice

Note: Caution : If you use the advanced configuration, the resource extension will register it automatically.

Summary
Configuration reference
sylius_resource:
settings:
# Enable pagination
paginate: true
# If the pagination is disabled, you can specify a limit
limit: false
# If the pagination is enabled, you can specify the allowed page size
allowed_paginate: [10, 20, 30]
# Default page size
default_page_size: 10
# Enable sorting
sortable: false
# Default sorting parameters
sorting: []
# Enable filtering
filterable: false
# Default filtering parameters
criteria: []
resources:
app.user:
driver: doctrine/orm # Also supported - doctrine/mongodb-odm.
templates: AppBundle:User
classes:
model: App\Entity\User
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository

Route configuration reference


route_name:
defaults:
_sylius:
# Name of the form, by default it is built with the prefix of the bundle and the name of
form: bundlePrefix_resource # string
# Name of the route where the user will be redirected
redirect: my_route # string
# If your route has extra parameters you can use the following syntax:
route: my_route # string

204

Chapter 7. Bundles

Sylius, Release

parameters: [] # array
# Number of item in the list (should be set to false if you want to disable it)
limit: 10 # integer or boolean
# Number of item by page (should be set to false if you want to disable it)
paginate: 10 # integer or boolean
# Enabling the filter
filterable: true # boolean
# Parameter used for filtering resources when filterable is enabled, using a $ to find th
criteria: $paginate # string or array
# Enabling the sorting
sortable: false # boolean
# Parameter used for sorting resources when sortable is enabled, using a $ to find the pa
sorting: $sorting # string or array
repository:
# The method of the repository used to retrieve resources
method: findActiveProduct # string
# Arguments gave to the 'method'
arguments: [] # array
factory:
# The method of the repository used to create the new resource
method: createNew # string
# Arguments gave to the 'method'
arguments: [] # array
# Key used by the translator for managing flash messages, by default it is built with the
flash: sylius.product.create # string
# Name of the property used to manage the position of the resource
sortable_position: position # string
# API request, version used by the serializer
serialization_version: null
# API request, groups used by the serializer
serialization_groups: []

phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This bundle uses GitHub issues. If you have found bug, please create an issue.

7.1.16 SyliusSettingsBundle
Settings system with web editing interface for Symfony2 applications.
Allowing application users to edit some global configuration and storing it in database is a common need in a variety
of projects. This bundle provides exactly this feature - you only need to define the configuration.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download the package.

7.1. Symfony2 Ecommerce Bundles

205

Sylius, Release

If you have Composer installed globally.


$ composer require "sylius/settings-bundle"

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require "sylius/settings-bundle"

Adding required bundles to the kernel

First, you need to enable the bundle inside the kernel. If youre not using any other Sylius bundles, you will also need
to add SyliusResourceBundle and its dependencies to the kernel. This bundle also uses DoctrineCacheBundle. Dont
worry, everything was automatically installed via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Sylius\Bundle\SettingsBundle\SyliusSettingsBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
);
}

Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first.

Container configuration

Put this configuration inside your app/config/config.yml.


sylius_settings:
driver: doctrine/orm
doctrine_cache:
providers:
sylius_settings:
type: file_system

Importing routing configuration

Import default routing from your app/config/routing.yml.

206

Chapter 7. Bundles

Sylius, Release

sylius_settings:
resource: @SyliusSettingsBundle/Resources/config/routing.yml
prefix: /settings

Note: We used default namespace in this example. If you want to use other namespaces for saving your settings,
routing config should be updated as it contains the namespace parameter.

Updating database schema

Run the following command.


$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.

Creating your settings schema


You have to create a new class implementing SchemaInterface, which will represent the structure of your configuration. For purpose of this tutorial, lets define the page metadata settings.
<?php
// src/Acme/ShopBundle/Settings/MetaSettingsSchema.php
namespace Acme\ShopBundle\Settings;
use Sylius\Bundle\SettingsBundle\Schema\SchemaInterface;
use Sylius\Bundle\SettingsBundle\Schema\SettingsBuilderInterface;
use Symfony\Component\Form\FormBuilderInterface;

class MetaSettingsSchema implements SchemaInterface


{
public function buildSettings(SettingsBuilderInterface $builder)
{
$builder
->setDefaults(array(
'title'
=> 'Sylius - Modern ecommerce for Symfony2',
'meta_keywords'
=> 'symfony, sylius, ecommerce, webshop, shopping cart',
'meta_description' => 'Sylius is modern ecommerce solution for PHP. Based on the Symf
))
->setAllowedTypes(array(
'title'
=> array('string'),
'meta_keywords'
=> array('string'),
'meta_description' => array('string'),
))
;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('title')
->add('meta_keywords')

7.1. Symfony2 Ecommerce Bundles

207

Sylius, Release

->add('meta_description', 'textarea')
;
}
}

Note: SettingsBuilderInterface is extended version of Symfonys OptionsResolver component.


As you can see there are two methods in our schema and both are very simple. First one, the ->buildSettings()
defines default values and allowed data types. Second, ->buildForm() creates the form to be used in the web
interface to update the settings.
Now, lets register our MetaSettingsSchema service. Remember that we are tagging it as sylius.settings_schema:
<service id="acme.settings_schema.meta" class="Acme\ShopBundle\Settings\MetaSettingsSchema">
<tag name="sylius.settings_schema" namespace="meta" />
</service>

Your new settings schema is available for use.


Editing the settings
To edit the settings via the web interface, simply point users to the sylius_settings_update route with proper
parameters.
In order to update our meta settings, generate the following link.
<a href="{{ path('sylius_settings_update', {'namespace': 'meta'}) }}">Edit SEO</a>

A proper form will be generated, with a submit action, which updates the settings in database.
Using in templates
Bundle provides handy SyliusSettingsExtension which you can use in your templates.
In our example, it can be something like:
{% set meta = sylius_settings_all('meta') %}
<head>
<title>{{ meta.title }}</title>
<meta name="keywords" content="{{ meta.meta_keywords }}">
<meta name="description" content="{{ meta.meta_description }}">
</head>

There is also sylius_settings_get() to get particular setting directly.


{% set title = sylius_settings_get('meta.title') %}
<head>
<title>{{ title }}</title>
</head>

Using the settings in services


You can also load and save the settings in any service. Simply use the SettingsManager service, available under the
sylius.settings.manager id.
208

Chapter 7. Bundles

Sylius, Release

Loading the settings


<?php
// src/Acme/ShopBundle/Taxation/TaxApplicator.php
namespace Acme\ShopBundle\Taxation;
use Sylius\Bundle\SettingsBundle\Manager\SettingsManagerInterface;
class TaxApplicator
{
private $settingsManager;
public function __construct(SettingsManagerInterface $settingsManager)
{
$this->settingsManager = $settingsManager;
}
public function applyTaxes(Order $order)
{
$taxationSettings = $this->settingsManager->loadSettings('taxation');
$itemsTotal = $order->getItemsTotal();
$order->setTaxTotal($taxationSettings->get('rate') * $itemsTotal);
}
}

Injecting the settings manager is as simple as using any other service.


<service id="acme.tax_applicator" class="Acme\ShopBundle\Taxation\TaxApplicator">
<argument type="service" id="sylius.settings.manager" />
</service>

Saving the settings

Note: To be written.

Summary
Configuration Reference
sylius_settings:
# The driver used for persistence layer.
driver: ~
classes:
parameter:
model: Sylius\Bundle\SettingsBundle\Model\Parameter
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\SettingsBundle\Form\Type\ParameterType

7.1. Symfony2 Ecommerce Bundles

209

Sylius, Release

Tests
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This bundle uses GitHub issues. If you have found bug, please create an issue.

7.1.17 SyliusShippingBundle
SyliusShippingBundle is the shipment management component for Symfony2 e-commerce applications.
If you need to manage shipments, shipping methods and deal with complex cost calculation, this bundle can help you
a lot!
Your products or whatever you need to deliver, can be categorized under unlimited set of categories. You can display
appropriate shipping methods available to the user, based on object category, weight, dimensions and anything you
can imagine.
Flexible shipping cost calculation system allows you to create your own calculator services.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download package.
If you have Composer installed globally.
$ composer require "sylius/shipping-bundle"

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require "sylius/shipping-bundle"

Adding required bundles to the kernel

You need to enable the bundle inside the kernel.


If youre not using any other Sylius bundles, you will also need to add SyliusResourceBundle and its dependencies.
Dont worry, everything was automatically installed via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),

210

Chapter 7. Bundles

Sylius, Release

new Sylius\Bundle\ShippingBundle\SyliusShippingBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
);
}

Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first.

Container configuration

Put this configuration inside your app/config/config.yml.


sylius_shipping:
driver: doctrine/orm # Configure the Doctrine ORM driver used in documentation.

Configure doctrine extensions which are used by this bundle.


stof_doctrine_extensions:
orm:
default:
timestampable: true

Routing configuration

Add the following to your app/config/routing.yml.


sylius_shipping:
resource: @SyliusShipping/Resources/config/routing.yml

Updating database schema

Run the following command.


$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.

The ShippableInterface
In order to handle your merchandise through the Sylius shipping engine, your models need to implement ShippableInterface.
Implementing the interface

Lets assume that you have a Book entity in your application.

7.1. Symfony2 Ecommerce Bundles

211

Sylius, Release

First step is to implement the simple interface, which contains few simple methods.
namespace Acme\Bundle\ShopBundle\Entity;
use Sylius\Component\Shipping\Model\ShippableInterface;
use Sylius\Component\Shipping\Model\ShippingCategoryInterface;
class Book implements ShippableInterface
{
private $shippingCategory;
public function getShippingCategory()
{
return $this->shippingCategory;
}

public function setShippingCategory(ShippingCategoryInterface $shippingCategory) // This method i


{
$this->shippingCategory = $shippingCategory;
return $this;
}
public function getShippingWeight()
{
// return integer representing the object weight.
}
public function getShippingWidth()
{
// return integer representing the book width.
}
public function getShippingHeight()
{
// return integer representing the book height.
}
public function getShippingDepth()
{
// return integer representing the book depth.
}
}

Second and last task is to define the relation inside Resources/config/doctrine/Book.orm.xml of your
bundle.
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Acme\ShopBundle\Entity\Book" table="acme_book">
<!-- your mappings... -->

<many-to-one field="shippingCategory" target-entity="Sylius\Bundle\ShippingBundle\Model\Shipp


<join-column name="shipping_category_id" referenced-column-name="id" nullable="false" />

212

Chapter 7. Bundles

Sylius, Release

</many-to-one>
</entity>
</doctrine-mapping>

Done! Now your Book model can be used in Sylius shippingation engine.
Forms

If you want to add a shipping category selection field to your model form,
sylius_shipping_category_choice type.

simply use the

namespace Acme\ShopBundle\Form\Type;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\AbstractType;
class BookType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', 'text')
->add('shippingCategory', 'sylius_shipping_category_choice')
;
}
}

The ShippingSubjectInterface
The find available shipping methods or calculate shipping cost you need to use object implementing
ShippingSubjectInterface.
The default Shipment model is already implementing ShippingSubjectInterface.
Interface methods

The getShippingMethod returns a ShippingMethodInterface instance, representing the method.


The getShippingItemCount provides you with the count of items to ship.
The getShippingItemTotal returns the total value of shipment, if applicable. The default Shipment
model returns 0.
The getShippingWeight returns the total shipment weight.
The getShippables returns a collection of unique ShippableInterface instances.
The Shipping Categories
Every shippable object needs to have a shipping category assigned. The ShippingCategory model is extremely simple
and described below.

7.1. Symfony2 Ecommerce Bundles

213

Sylius, Release

Attribute
id
name
description
createdAt
updatedAt

Description
Unique id of the shipping category
Name of the shipping category
Human friendly description of the classification
Date when the category was created
Date of the last shipping category update

Calculating shipping cost


Calculating shipping cost is as simple as using the sylius.shipping_calculator service and calling
calculate method on ShippingSubjectInterface.
Lets calculate the cost of existing shipment.
public function myAction()
{
$calculator = $this->get('sylius.shipping_calculator');
$shipment = $this->get('sylius.repository.shipment')->find(5);
echo $calculator->calculate($shipment); // Returns price in cents. (integer)
}

What has happened?


The delegating calculator gets the ShippingMethod from the ShippingSubjectInterface (Shipment).
Appropriate Calculator instance is loaded, based on the ShippingMethod.calculator parameter.
The calculate(ShippingSubjectInterface, array $configuration) is called, where configuration is taken from ShippingMethod.configuration attribute.
Default calculators
Default calculators can be sufficient solution for many use cases.
Flat rate

The flat_rate calculator, charges concrete amount per shipment.


Per item rate

The per_item_rate calculator, charges concrete amount per shipment item.


Flexible rate

The flexible_rate calculator, charges one price for the first item, and another price for every other item.
Weight rate

The weight_rate calculator, charges one price for certain weight of shipment. So if the shipment weights 5 kg,
and calculator is configured to charge $4 per kg, the final price is $20.

214

Chapter 7. Bundles

Sylius, Release

More calculators

Depending on community contributions and Sylius resources, more default calculators can be implemented, for example weight_range_rate.
Custom calculators
Sylius ships with several default calculators, but you can easily register your own.
Simple calculators

All shipping cost calculators implement CalculatorInterface. In our example well create a calculator which
calls an external API to obtain the shipping cost.
<?php
// src/Acme/ShopBundle\Shipping/DHLCalculator.php
namespace Acme\ShopBundle\Shipping;
use Acme\ShopBundle\Shipping\DHLService;
use Sylius\Bundle\ShippingBundle\Calculator\Calculator;
use Sylius\Bundle\ShippingBundle\Model\ShippingSubjectInterface;
class DHLCalculator extends Calculator
{
private $dhlService;
public function __construct(DHLService $dhlService)
{
$this->dhlService = $dhlService;
}
public function calculate(ShippingSubjectInterface $subject, array $configuration)
{
return $this->dhlService->getShippingCostForWeight($subject->getShippingWeight());
}
}

Now, you need to register your new service in container and tag it with sylius.shipping_calculator.
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="acme.shipping_calculator.dhl" class="Acme\ShopBundle\Shipping\DHLCalculator">
<argument type="service" id="acme.dhl_service" />
<tag name="sylius.shipping_calculator" calculator="dhl" label="DHL" />
</service>
</services>
</container>

7.1. Symfony2 Ecommerce Bundles

215

Sylius, Release

That would be all. This new option (DHL) will appear on the ShippingMethod creation form, in the calculator
field.
Configurable calculators

You can also create configurable calculators, meaning that you can have several ShippingMethods using same type
of calculator, with different settings.
Lets modify the DHLCalculator, so that it charges 0 if shipping more than X items. First step is to define the
configuration options, using the Symfony OptionsResolver component.
<?php
// src/Acme/ShopBundle\Shipping/DHLCalculator.php
namespace Acme\ShopBundle\Shipping;
use
use
use
use

Acme\ShopBundle\Shipping\DHLService;
Sylius\Bundle\ShippingBundle\Calculator\Calculator;
Sylius\Bundle\ShippingBundle\Model\ShippingSubjectInterface;
Symfony\Component\OptionsResolver\OptionsResolverInterface;

class DHLCalculator extends Calculator


{
private $dhlService;
public function __construct(DHLService $dhlService)
{
$this->dhlService = $dhlService;
}
public function calculate(ShippingSubjectInterface $subject, array $configuration)
{
return $this->dhlService->getShippingCostForWeight($subject->getShippingWeight());
}
/**
* {@inheritdoc}
*/
public function isConfigurable()
{
return true;
}
public function setConfiguration(OptionsResolverInterface $resolver)
{
$resolver
->setDefaults(array(
'limit' => 10
))
->setAllowedTypes(array(
'limit' => array('integer'),
))
;
}
}

Done, weve set the default item limit to 10. Now we have to create a form type which will be displayed if our
216

Chapter 7. Bundles

Sylius, Release

calculator is selected.
<?php
// src/Acme/ShopBundle/Form/Type/Shipping/DHLConfigurationType.php
namespace Acme\ShopBundle\Form\Type\Shipping;
use
use
use
use
use

Symfony\Component\Form\AbstractType;
Symfony\Component\Form\FormBuilderInterface;
Symfony\Component\OptionsResolver\OptionsResolverInterface;
Symfony\Component\Validator\Constraints\NotBlank;
Symfony\Component\Validator\Constraints\Type;

class DHLConfigurationType extends AbstractType


{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('limit', 'integer', array(
'label' => 'Free shipping above total items',
'constraints' => array(
new NotBlank(),
new Type(array('type' => 'integer')),
)
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver
->setDefaults(array(
'data_class' => null
))
;
}
public function getName()
{
return 'acme_shipping_calculator_dhl';
}
}

We also need to register the form type and the calculator in the container.
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="acme.shipping_calculator.dhl" class="Acme\ShopBundle\Shipping\DHLCalculator">
<argument type="service" id="acme.dhl_service" />
<tag name="sylius.shipping_calculator" calculator="dhl" label="DHL" />
</service>
<service id="acme.form.type.shipping_calculator.dhl" class="Acme\ShopBundle\Form\Type\Shippin

7.1. Symfony2 Ecommerce Bundles

217

Sylius, Release

<tag name="form.type" alias="acme_shipping_calculator_dhl" />


</service>
</services>
</container>

Finally, configure the calculator to use the form, by implementing simple getConfigurationFormType method.
<?php
// src/Acme/ShopBundle\Shipping/DHLCalculator.php
namespace Acme\ShopBundle\Shipping;
use
use
use
use

Acme\ShopBundle\Shipping\DHLService;
Sylius\Bundle\ShippingBundle\Calculator\Calculator;
Sylius\Bundle\ShippingBundle\Model\ShippingSubjectInterface;
Symfony\Component\OptionsResolver\OptionsResolverInterface;

class DHLCalculator extends Calculator


{
private $dhlService;
public function __construct(DHLService $dhlService)
{
$this->dhlService = $dhlService;
}
public function calculate(ShippingSubjectInterface $subject, array $configuration)
{
return $this->dhlService->getShippingCostForWeight($subject->getShippingWeight());
}
/**
* {@inheritdoc}
*/
public function isConfigurable()
{
return true;
}
public function setConfiguration(OptionsResolverInterface $resolver)
{
$resolver
->setDefaults(array(
'limit' => 10
))
->setAllowedTypes(array(
'limit' => array('integer'),
))
;
}
public function getConfigurationFormType()
{
return 'acme_shipping_calculator_dhl';
}
}

Perfect, now were able to use the configuration inside the calculate method.
218

Chapter 7. Bundles

Sylius, Release

<?php
// src/Acme/ShopBundle\Shipping/DHLCalculator.php
namespace Acme\ShopBundle\Shipping;
use
use
use
use

Acme\ShopBundle\Shipping\DHLService;
Sylius\Bundle\ShippingBundle\Calculator\Calculator;
Sylius\Bundle\ShippingBundle\Model\ShippingSubjectInterface;
Symfony\Component\OptionsResolver\OptionsResolverInterface;

class DHLCalculator extends Calculator


{
private $dhlService;
public function __construct(DHLService $dhlService)
{
$this->dhlService = $dhlService;
}
public function calculate(ShippingSubjectInterface $subject, array $configuration)
{
if ($subject->getShippingItemCount() > $configuration['limit']) {
return 0;
}
return $this->dhlService->getShippingCostForWeight($subject->getShippingWeight());
}
/**
* {@inheritdoc}
*/
public function isConfigurable()
{
return true;
}
public function setConfiguration(OptionsResolverInterface $resolver)
{
$resolver
->setDefaults(array(
'limit' => 10
))
->setAllowedTypes(array(
'limit' => array('integer'),
))
;
}
public function getConfigurationFormType()
{
return 'acme_shipping_calculator_dhl';
}
}

Your new configurable calculator is ready to use. When you select the DHL calculator in ShippingMethod form,
configuration fields will appear automatically.

7.1. Symfony2 Ecommerce Bundles

219

Sylius, Release

Shipping method requirements


Sylius has a very flexible system for displaying only the right shipping methods to the user.
Shipping categories

Every ShippableInterface can hold a reference to ShippingCategory. The ShippingSubjectInterface (or ShipmentInterface) returns a collection of shippables.
ShippingMethod has an optional shipping category setting as well as categoryRequirement which has 3 options. If
this setting is set to null, categories system is ignored.
Match any requirement With this requirement, the shipping method will support any shipment (or shipping
subject) which contains at least one shippable with the same category.
Match all requirement All shippables have to reference the same category as the ShippingMethod.
Match none requirement None of the shippables can have the same shipping category.
Shipping rules

The categories system is sufficient for basic use cases, for more advanced requirements, Sylius has the shipping rules
system.
Every ShippingMethod can have a collection of ShippingRule instances. Each shipping rule, has a checker and
configuration assigned. The ShippingRule.checker attribute holds the alias name of appropriate service implementing RuleCheckerInterface. Just like with cost calculators, Sylius ships with default checkers, but you can easily
implement your own.
The ShippingMethodEligibilityChecker service can check if given subject, satisfies the category and shipping rules.
Default rule checkers
Sylius ships with several shipping rule checker types, so you can easily decide whether the shipping method is applicable to given shipment.
Item count

The item_count checker, accepts the subject only if the items count fits into min and max range.
You can configure a method, which will be available only if the shipment contains more than 5 items, but less than 20.
Item total

The item_total checker, is testing if the shipping subject total value is more than configured minimum, or eventually, less than maximum.

220

Chapter 7. Bundles

Sylius, Release

Weight

The weight checker, allows to ship the shipment using the particular method, only if the shipment weight falls into
the configure min and max range.
More checkers

Depending on community contributions and Sylius resources, more default checkers can be implemented.
Custom rule checkers
Implementing a custom rule checker is really simple, just like calculators, you can use simple services, or more
complex - configurable checkers.
Simple checkers

Note: To be written.

Configurable calculators

Note: To be written.

Summary
Configuration Reference
sylius_shipping:
# The driver used for persistence layer.
driver: ~
classes:
shipment:
model: Sylius\Component\Shipping\Model\Shipment
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\ShippingBundle\Form\Type\ShipmentType
shipment_item:
model: Sylius\Component\Shipping\Model\ShipmentItem
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\ShippingBundle\Form\Type\ShipmentItemType
shipping_method:
model: Sylius\Component\Shipping\Model\ShippingMethod
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\ShippingBundle\Form\Type\ShippingMethodType
shipping_method_rule:
model: Sylius\Component\Shipping\Model\ShippingMethodRule
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController

7.1. Symfony2 Ecommerce Bundles

221

Sylius, Release

repository: ~
form: Sylius\Bundle\ShippingBundle\Form\Type\ShippingMethodRuleType
shipping_method_rule:
model: Sylius\Component\Shipping\Model\Rule
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\ShippingBundle\Form\Type\RuleType
validation_groups:
shipping_category: [sylius]
shipping_method: [sylius]
shipping_rule_item_count_configuration: [sylius]
shipping_calculator_flat_rate_configuration: [sylius]
shipping_calculator_per_item_rate_configuration: [sylius]
shipping_calculator_flexible_rate_configuration: [sylius]
shipping_calculator_weight_rate_configuration: [sylius]

Tests
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This bundle uses GitHub issues. If you have found bug, please create an issue.

7.1.18 SyliusTaxationBundle
Calculating and applying taxes is a common task for most of ecommerce applications. SyliusTaxationBundle is a
reusable taxation component for Symfony2. You can integrate it into your existing application and enable the tax
calculation logic for any model implementing the TaxableInterface.
It supports different tax categories and customizable tax calculators - youre able to easily implement your own calculator services. The default implementation handles tax included in and excluded from the price.
As with any Sylius bundle, you can override all the models, controllers, repositories, forms and services.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download package.
If you have Composer installed globally.
$ composer require "sylius/taxation-bundle"

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require "sylius/taxation-bundle"

222

Chapter 7. Bundles

Sylius, Release

Adding required bundles to the kernel

First, you need to enable the bundle inside the kernel.


If youre not using any other Sylius bundles, you will also need to add SyliusResourceBundle and its dependencies to
kernel. Dont worry, everything was automatically installed via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Sylius\Bundle\TaxationBundle\SyliusTaxationBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
);
}

Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first.

Container configuration

Put this configuration inside your app/config/config.yml.


sylius_taxation:
driver: doctrine/orm # Configure the Doctrine ORM driver used in documentation.

And configure doctrine extensions which are used by this bundle:


stof_doctrine_extensions:
orm:
default:
timestampable: true

Routing configuration

Add the following to your app/config/routing.yml.


sylius_taxation:
resource: @SyliusTaxationBundle/Resources/config/routing.yml

Updating database schema

Run the following command.

7.1. Symfony2 Ecommerce Bundles

223

Sylius, Release

$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.

The TaxableInterface
In order to calculate the taxes for a model in your application, it needs to implement the TaxableInterface It is a
very simple interface, with only one method - the getTaxCategory(), as every taxable has to belong to a specific
tax category.
Implementing the interface

Lets assume that you have a Server entity in your application. Every server has its price and other parameters, but
you would like to calculate the tax included in price. You could calculate the math in a simple method, but its not
enough when you have to handle multiple tax rates, categories and zones.
First step is to implement the simple interface.
namespace AcmeBundle\Entity;
use Sylius\Component\Taxation\Model\TaxCategoryInterface;
use Sylius\Component\Taxation\Model\TaxableInterface;
class Server implements TaxableInterface
{
private $name;
private $taxCategory;
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getTaxCategory()
{
return $this->taxCategory;
}

public function setTaxCategory(TaxCategoryInterface $taxCategory) // This method is not required.


{
$this->taxCategory = $taxCategory;
}
}

Second and last task is to define the relation inside Resources/config/doctrine/Server.orm.xml of


your bundle.
<?xml version="1.0" encoding="UTF-8"?>

224

Chapter 7. Bundles

Sylius, Release

<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="AcmeBundle\Entity\Server" table="acme_server">
<!-- your mappings... -->

<many-to-one field="taxCategory" target-entity="Sylius\Component\Taxation\Model\TaxCategoryIn


<join-column name="tax_category_id" referenced-column-name="id" nullable="false" />
</many-to-one>
</entity>
</doctrine-mapping>

Done! Now your Server model can be used in Sylius taxation engine.
Forms

If you want to add a tax category


sylius_tax_category_choice type.

selection

field

to

your

model

form,

simply

use

the

namespace AcmeBundle\Form\Type;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\AbstractType;
class ServerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text')
->add('taxCategory', 'sylius_tax_category_choice')
;
}
public function getName()
{
return 'acme_server';
}
}

Configuring taxation
To start calculating taxes, we need to configure the system. In most cases, the configuration process is done via web
interface, but you can also do it programatically.
Creating the tax categories

First step is to create a new tax category.


<?php

7.1. Symfony2 Ecommerce Bundles

225

Sylius, Release

public function configureAction()


{
$repository = $this->container->get('sylius.repository.tax_category');
$manager = $this->container->get('sylius.manager.tax_category');
$clothing = $repository
->createNew()
->setName('Clothing')
->setDescription('T-Shirts and this kind of stuff.')
;
$food = $repository
->createNew()
->setName('Food')
->setDescription('Yummy!')
;
$manager->persist($clothing);
$manager->persist($food);
$manager->flush();
}

Categorizing the taxables

Second thing to do is to classify the taxables, in our example well get two products and assign the proper categories
to them.
<?php
public function configureAction()
{
$tshirt = // ...
$banana = // ... Some logic behind loading entities.
$repository = $this->container->get('sylius.repository.tax_category');
$clothing = $repository->findOneBy(array('name' => 'Clothing'));
$food = $repository->findOneBy(array('name' => 'Food'));
$tshirt->setTaxCategory($clothing);
$food->setTaxCategory($food);
// Save the product entities.
}

Configuring the tax rates

Finally, you have to create appropriate tax rates for each of categories.
<?php
public function configureAction()
{
$taxCategoryRepository = $this->container->get('sylius.repository.tax_category');

226

Chapter 7. Bundles

Sylius, Release

$clothing = $taxCategoryRepository->findOneBy(array('name' => 'Clothing'));


$food = $taxCategoryRepository->findOneBy(array('name' => 'Food'));
$repository = $this->container->get('sylius.repository.tax_rate');
$manager = $this->container->get('sylius.repository.tax_rate');
$clothingTax = $repository
->createNew()
->setName('Clothing Tax')
->setAmount(0,08)
;
$foodTax = $repository
->createNew()
->setName('Food')
->setAmount(0,12)
;
$manager->persist($clothingTax);
$manager->persist($foodTax);
$manager->flush();
}

Done! See the Calculating Taxes chapter to see how to apply these rates.
Calculating taxes
Warning: When using the CoreBundle (i.e: full stack Sylius framework), the taxes are already calculated at each
cart change. It is implemented by the TaxationProcessor class, which is called by the OrderTaxationListener.
In order to calculate tax amount for given taxable, we need to find out the applicable tax rate. The tax rate resolver
service is available under sylius.tax_rate_resolver id, while the delegating tax calculator is accessible via
sylius.tax_calculator name.
Resolving rate and using calculator
<?php
namespace Acme\ShopBundle\Taxation
use Acme\ShopBundle\Entity\Order;
use Sylius\Bundle\TaxationBundle\Calculator\CalculatorInterface;
use Sylius\Bundle\TaxationBundle\Resolver\TaxRateResolverInterface;
class TaxApplicator
{
private $calculator;
private $taxRateResolver;
public function __construct(
CalculatorInterface $calculator,
TaxRateResolverInterface $taxRateResolver,
)

7.1. Symfony2 Ecommerce Bundles

227

Sylius, Release

{
$this->calculator = $calculator;
$this->taxRateResolver = $taxRateResolver;
}
public function applyTaxes(Order $order)
{
$tax = 0;
foreach ($order->getItems() as $item) {
$taxable = $item->getProduct();
$rate = $this->taxRateResolver->resolve($taxable);
if (null === $rate) {
continue; // Skip this item, there is no matching tax rate.
}
$tax += $this->calculator->calculate($item->getTotal(), $rate);
}
$order->setTaxTotal($tax); // Set the calculated taxes.
}
}

Using custom tax calculators


Every TaxRate model holds a calculator variable with the name of the tax calculation service, used to compute
the tax amount. While the default calculator should fit for most common use cases, youre free to define your own
implementation.
Creating the calculator

All calculators implement the TaxCalculatorInterface. First, you need to create a new class.
namespace Acme\Bundle\ShopBundle\TaxCalculator;
use Sylius\Bundle\TaxationBundle\Calculator\TaxCalculatorInterface;
use Sylius\Bundle\TaxationBundle\Model\TaxRateInterface;
class FeeCalculator implements TaxCalculatorInterface
{
public function calculate($amount, TaxRate $rate)
{
return $amount * ($rate->getAmount() + 0,15 * 0,30);
}
}

228

Chapter 7. Bundles

Sylius, Release

Summary
Configuration Reference
sylius_taxation:
# The driver used for persistence layer.
driver: ~
classes:
tax_category:
model: Sylius\Component\Taxation\Model\TaxCategory
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form:
default: Sylius\Bundle\TaxationBundle\Form\Type\TaxCategoryType
choice: Sylius\Bundle\ResourceBundle\Form\Type\ResourceChoiceType
tax_rate:
model: Sylius\Component\Taxation\Model\TaxRate
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form:
default: Sylius\Bundle\TaxationBundle\Form\Type\TaxRateType
validation_groups:
tax_category: [sylius]
tax_rate: [sylius]

Tests
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This bundle uses GitHub issues. If you have found bug, please create an issue.

7.1.19 SyliusTaxonomiesBundle
Flexible categorization system for Symfony2 applications.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download the package.
If you have Composer installed globally.
$ composer require "sylius/taxonomy-bundle"

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require "sylius/taxonomy-bundle"

7.1. Symfony2 Ecommerce Bundles

229

Sylius, Release

Note: This version is compatible only with Symfony 2.3 or newer. Please see the CHANGELOG file in the repository,
to find version to use with older vendors.

Adding required bundles to the kernel

First, you need to enable the bundle inside the kernel. If youre not using any other Sylius bundles, you will also need
to add SyliusResourceBundle and its dependencies to the kernel. Dont worry, everything was automatically installed
via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Sylius\Bundle\TaxonomyBundle\SyliusTaxonomyBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
);
}

Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first.

Container configuration

Put this configuration inside your app/config/config.yml.


sylius_taxonomy:
driver: doctrine/orm # Configure the doctrine orm driver used in documentation.

And configure doctrine extensions which are used in the taxonomy bundle:
stof_doctrine_extensions:
orm:
default:
tree: true
sluggable: true

Routing configuration

Add the following lines to your app/config/routing.yml.

230

Chapter 7. Bundles

Sylius, Release

sylius_taxonomy:
resource: @SyliusTaxonomyBundle/Resources/config/routing.yml
prefix: /taxonomy

Updating database schema

Run the following command.


$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.

Taxonomy and Taxons


Retrieving taxonomies and taxons

Retrieving taxonomy from database should always happen via repository, which implements
Sylius\Bundle\ResourceBundle\Model\RepositoryInterface.
If you are using Doctrine,
youre already familiar with this concept, as it extends the native Doctrine ObjectRepository interface.
Your taxonomy repository is always accessible via sylius.repository.taxonomy service. Of course,
sylius.repository.taxon is also available for use, but usually you obtains taxons directly from Taxonomy
model. Youll see that in further parts of this document.
<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.taxonomy');
}

Retrieving taxonomies is simpleas calling proper methods on the repository.


<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.taxonomy');

$taxonomy = $repository->find(2); // Get taxonomy with id 2, returns null if not found.


$taxonomy = $repository->findOneBy(array('name' => 'Specials')); // Get one taxonomy by defined c

$taxonomies = $repository->findAll(); // Load all the taxonomies!


$taxonomies = $repository->findBy(array('hidden' => true)); // Find taxonomies matching some cust
}

Taxonomy repository also supports paginating taxonomies.


createPaginator method.

To create a Pagerfanta instance use the

<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.taxonomy');

7.1. Symfony2 Ecommerce Bundles

231

Sylius, Release

$taxonomies = $repository->createPaginator();
$taxonomies->setMaxPerPage(5);
$taxonomies->setCurrentPage($request->query->get('page', 1));

// Now you can return taxonomies to template and iterate over it to get taxonomies from current pa
}

Paginator also can be created for specific criteria and with desired sorting.
<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.taxonomy');
$taxonomies = $repository->createPaginator(array('foo' => true), array('createdAt' => 'desc'));
$taxonomies->setMaxPerPage(3);
$taxonomies->setCurrentPage($request->query->get('page', 1));
}

Creating new taxonomy object

To create new taxonomy instance, you can simply call createNew() method on the repository.
<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.taxonomy');
$taxonomy = $repository->createNew();
}

Note: Creating taxonomy via this factory method makes the code more testable, and allows you to change taxonomy
class easily.

Saving & removing taxonomy

To save or remove a taxonomy, you can use any ObjectManager which manages Taxonomy.
You
can always access it via alias sylius.manager.taxonomy. But its also perfectly fine if you use
doctrine.orm.entity_manager or other appropriate manager service.
<?php

public function myAction(Request $request)


{
$repository = $this->container->get('sylius.repository.taxonomy');
$manager = $this->container->get('sylius.manager.taxonomy'); // Alias for appropriate doctrine ma
$taxonomy = $repository->createNew();
$taxonomy
->setName('Foo')
;

232

Chapter 7. Bundles

Sylius, Release

$manager->persist($taxonomy);
$manager->flush(); // Save changes in database.
}

To remove a taxonomy, you also use the manager.


<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.taxonomy');
$manager = $this->container->get('sylius.manager.taxonomy');
$taxonomy = $repository->find(5);
$manager->remove($taxonomy);
$manager->flush(); // Save changes in database.
}

Taxons

Taxons can be handled exactly the same way, but with usage of sylius.repository.taxon.
Taxonomy contains methods which allow you to retrieve the child taxons. Lets look again at our example tree.
| Categories
|-- T-Shirts
|
|-- Men
|
`-- Women
|-- Stickers
|-- Mugs
`-- Books

To get a flat list of taxons under taxonomy, use the getTaxonsAsList method.
<?php
public function myAction(Request $request)
{
// Find the taxonomy
$taxonomyRepository = $this->container->get('sylius.repository.taxonomy');
$taxonomy = $taxonomyRepository->findOneByName('Categories');
// Get the taxons as a list
$taxonRepository = $this->container->get('sylius.repository.taxon');
$taxons = $taxonRepository->getTaxonsAsList($taxonomy);
}

$taxons variable will now contain flat list (ArrayCollection) of taxons in following order: T-Shirts, Men, Women,
Stickers, Mugs, Books.
If, for example, you want to render them as tree.
<?php
public function myAction(Request $request)
{
$repository = $this->container->get('sylius.repository.taxonomy');

7.1. Symfony2 Ecommerce Bundles

233

Sylius, Release

$taxonomy = $repository->findOneByName('Categories');
$taxons = $taxonomy->getTaxons();
}

Now $taxons contains only the 4 main items,


$taxon->getChildren().

and you can access their children by calling

Summary
Configuration Reference
sylius_taxonomies:
# The driver used for persistence layer.
driver: ~
classes:
taxonomy:
model: Sylius\Component\Taxonomy\Model\Taxonomy
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\TaxonomiesBundle\Form\Type\TaxonomyType
taxon:
model: Sylius\Component\Taxonomy\Model\Taxon
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: ~
form: Sylius\Bundle\TaxonomiesBundle\Form\Type\TaxonType
validation_groups:
taxonomy: [sylius]
taxon: [sylius]

Tests
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This bundle uses GitHub issues. If you have found bug, please create an issue.

7.1.20 SyliusVariationBundle
This bundle allows you to manage and generate variations of any compatible object with a very flexible architecture.
Sylius uses this bundle internally for its product catalog to manage variations of the same product based on its options
and their values, but can be used with almost any object.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use the following command to add the
bundle to your composer.json and download the package.

234

Chapter 7. Bundles

Sylius, Release

If you have Composer installed globally.


$ composer require "sylius/variation-bundle"

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require "sylius/variation-bundle"

Adding required bundles to the kernel

You need to enable the bundle inside the kernel.


If youre not using any other Sylius bundles, you will also need to add SyliusResourceBundle and its dependencies to
kernel. Dont worry, everything was automatically installed via Composer.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Sylius\Bundle\VariationBundle\SyliusVariationBundle(),
new Sylius\Bundle\ResourceBundle\SyliusResourceBundle(),
// Other bundles...
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
);
}

Note: Please register the bundle before DoctrineBundle. This is important as we use listeners which have to be
processed first.

Container configuration

Put this configuration inside your app/config/config.yml.


sylius_variation:
driver: doctrine/orm # Configure the doctrine orm driver used in the documentation.

And configure doctrine extensions which are used by the bundle.


stof_doctrine_extensions:
orm:
default:
timestampable: true
softdeleteable: true

7.1. Symfony2 Ecommerce Bundles

235

Sylius, Release

Updating database schema

Run the following command.


$ php app/console doctrine:schema:update --force

Warning: This should be done only in dev environment! We recommend using Doctrine migrations, to safely
update your schema.
Congratulations! The bundle is now installed and ready to use.
Configuration reference

sylius_variation:
driver: ~ # The driver used for persistence layer. Currently only `doctrine/orm` is supported.
classes:
# `variation_name` can be any name, for example `product`, `ad`, or `blog_post`
variation_name:
variable: ~ # Required: The variable model class implementing `VariableInterface`
# of which variants can be created from
variant:
model:
~ # Required: The variant model class implementing `VariantInterface`
repository: ~ # Required: The repository class for the variant
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
form:
Sylius\Bundle\VariationBundle\Form\Type\VariantType
option:
model:
~ # Required: The option model class implementing `OptionInterface`
repository: ~ # Required: The repository class for the option
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
form:
Sylius\Bundle\VariationBundle\Form\Type\OptionType
option_value:
model:
~ # Required: The option value model class implementing `OptionValueInt
repository: ~ # Required: The repository class for the option value
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
form:
Sylius\Bundle\VariationBundle\Form\Type\OptionValueType
validation_groups:
# `variation_name` should be same name as the name key defined for the classes section abov
variation_name:
variant:
[ sylius ]
option:
[ sylius ]
option_value: [ sylius ]

Bundles General Guide


SyliusAddressingBundle
/bundles/SyliusArchetypeBundle/index
SyliusAttributeBundle
SyliusCartBundle
/bundles/SyliusContactBundle/index
SyliusFlowBundle
SyliusGridBundle
SyliusInventoryBundle

236

Chapter 7. Bundles

Sylius, Release

SyliusMailerBundle
SyliusOmnipayBundle
SyliusOrderBundle
SyliusProductBundle
SyliusPromotionBundle
SyliusRbacBundle
SyliusReportBundle
SyliusResourceBundle
SyliusSettingsBundle
SyliusShippingBundle
SyliusTaxationBundle
SyliusTaxonomiesBundle
SyliusVariationBundle
Bundles General Guide
SyliusAddressingBundle
/bundles/SyliusArchetypeBundle/index
SyliusAttributeBundle
SyliusCartBundle
/bundles/SyliusContactBundle/index
SyliusFlowBundle
SyliusGridBundle
SyliusInventoryBundle
SyliusMailerBundle
SyliusOmnipayBundle
SyliusOrderBundle
SyliusProductBundle
SyliusPromotionBundle
SyliusRbacBundle
SyliusReportBundle
SyliusResourceBundle
SyliusSettingsBundle
SyliusShippingBundle
SyliusTaxationBundle
SyliusTaxonomiesBundle
SyliusVariationBundle

7.1. Symfony2 Ecommerce Bundles

237

Sylius, Release

238

Chapter 7. Bundles

CHAPTER 8

Components

E-Commerce components for PHP.

8.1 PHP E-Commerce Components


The components are the foundation of the Sylius platform, but they can also be used standalone with any PHP application and project even if you use a different framework than Symfony. They provide you with default models, services
and logic for all aspects of e-commerce.
How to Install and Use the Sylius Components

8.1.1 How to Install and Use the Sylius Components


If youre starting a new project (or already have a project) that will use one or more components, the easiest way to
integrate everything is with Composer. Composer is smart enough to download the component(s) that you need and
take care of autoloading so that you can begin using the libraries immediately.
This article will take you through using Taxation, though this applies to using any component.
Using the Taxation Component
1. If youre creating a new project, create a new empty directory for it.
2. Open a terminal and use Composer to grab the library.
$ composer require sylius/taxation

The name sylius/taxation is written at the top of the documentation for whatever component you want.
Tip: Install composer if you dont have it already present on your system. Depending on how you install, you may
end up with a composer.phar file in your directory. In that case, no worries! Just run php composer.phar
require sylius/taxation.
3. Write your code!
Once Composer has downloaded the component(s), all you need to do is include the vendor/autoload.php file
that was generated by Composer. This file takes care of autoloading all of the libraries so that you can use them
immediately:

239

Sylius, Release

// File example: src/script.php


<?php
// update this to the path to the "vendor/"
// directory, relative to this file
require_once __DIR__.'/../vendor/autoload.php';
use Sylius\Component\Taxation\Calculator\DefaultCalculator;
use Sylius\Component\Taxation\Model\TaxRate;
$calculator = new DefaultCalculator();
$taxRate = new TaxRate();
$taxRate->setAmount(0.23);
$tax = $calculator->calculate(100, $taxRate);
// ...

Using all of the Components


If you want to use all of the Sylius Components, then instead of adding them one by one, you can include the
sylius/sylius package:
$ composer require sylius/sylius

Now what?
Now that the component is installed and autoloaded, read the specific components documentation to find out more
about how to use it.
And have fun!

8.1.2 Addressing
Sylius Addressing component for PHP E-Commerce applications which provides you with basic Address, Country,
Province and Zone models.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius\addressing on Packagist);
Use the official Git repository (https://github.com/Sylius/Addressing).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.

240

Chapter 8. Components

Sylius, Release

Basic Usage
ZoneMatcher

Zones are not very useful by themselves, but they can take part in e.g. a complex taxation/shipping system. This
service is capable of getting a Zone specific for given Address.
It uses a collaborator implementing Doctrines ObjectRepository interface to obtain all available zones, compare them
with given Address and return best fitted Zone.
First lets make some preparations.
<?php
use
use
use
use

Sylius\Component\Addressing\Model\Address;
Sylius\Component\Addressing\Model\Country;
Sylius\Component\Addressing\Model\Zone;
Sylius\Component\Addressing\Model\ZoneMemberCountry;

$zone = new Zone();


$zoneMember = new ZoneMemberCountry();
$country = new Country();
$country->setIsoName('USA');
$address = new Address();
$address->setCountry($country);
$zoneMember->setCountry($country);
$zoneMember->setBelongsTo($zone);
$zone->addMember($zoneMember);

Now that we have all the needed parts lets match something.
<?php
use Sylius\Component\Addressing\Matcher\ZoneMatcher;
$zoneRepository = // get the zone repository
$zoneMatcher = new ZoneMatcher($zoneRepository);
$zoneMatcher->match($address); // returns the best matching zone
// for the address given, in this case $zone

ZoneMatcher can also return all zones containing given Address.


<?php
$zoneMatcher->matchAll($address); // returns all zones containing given $address

To be more specific you can provide a scope which will narrow the search only to zones with same corresponding
property.
<?php
$zone->setScope('earth');
$zoneMatcher->match($address, 'earth'); // returns $zone

8.1. PHP E-Commerce Components

241

Sylius, Release

$zoneMatcher->matchAll($address, 'mars'); // returns null as there is no


// zone with 'mars' scope

Note: This service implements the ZoneMatcherInterface.


Caution: Throws \InvalidArgumentException.

Models
Address

The customers address is represented by an Address model. It should contain all data concerning customers address
and as default has the following properties:
Property
id
firstName
lastName
phoneNumber
company
country
province
street
city
postcode
createdAt
updatedAt

Description
Unique id of the address
Customers first name
Customers last name
Customers phone number
Company name
Reference to a Country object
Reference to a Province object
Address street
Address city
Address postcode
Date when address was created
Date of last address update

Note: This model implements the AddressInterface. For more detailed information go to Sylius API Address.

Country

The geographical area of a country is represented by a Country model. It should contain all data concerning a country
and as default has the following properties:
Property
id
isoName
provinces
enabled

Description
Unique id of the country
Countrys ISO code
Collection of Province objects
Indicates whether country is enabled

Note: This model implements the CountryInterface. For more detailed information go to Sylius API Country.

Province

Smaller area inside a country is represented by a Province model. You can use it to manage provinces or states and
assign it to an address as well. It should contain all data concerning a province and as default has the following
properties:

242

Chapter 8. Components

Sylius, Release

Property
id
name
isoName
country

Description
Unique id of the province
Provinces name
Provinces ISO code
The Country this province is assigned to

Note: This model implements the ProvinceInterface. For more detailed information go to Sylius API Province.

Zone

The geographical area is represented by a Zone model. It should contain all data concerning a zone and as default has
the following properties:
Property
id
name
type
scope
members

Description
Unique id of the zone
Zones name
Zones type
Zones scope
All of the ZoneMember objects assigned to this zone

Note: This model implements the ZoneInterface. For more detailed information go to Sylius API Zone.

ZoneMember

In order to add a member to a zone, a class must extend abstract ZoneMember. On default this model has the
following properties:
Property
id
belongsTo

Description
Unique id of the zone member
The Zone this member is assigned to

Note: This model implements ZoneMemberInterface For more detailed information go to Sylius API ZoneMember.
Note: Each ZoneMember instance holds a reference to the Zone object and an appropriate area entity, for example
a Country.
There are three default zone member models:
ZoneMemberCountry
ZoneMemberProvince
ZoneMemberZone
Tip: Feel free to implement your own custom zone members!

ZoneMemberCountry

Country member of a zone is represented by a ZoneMemberCountry model. It has all the properties of ZoneMember
and one additional:
Property
country

Description
The Country associated with this zone member

8.1. PHP E-Commerce Components

243

Sylius, Release

Note: For more detailed information go to Sylius API ZoneMemberCountry.

ZoneMemberProvince

Province member of a zone is represented by a ZoneMemberProvince model. It has all the properties of ZoneMember
and one additional:
Property
province

Description
The Province associated with this zone member

Note: For more detailed information go to Sylius API ZoneMemberProvince.

ZoneMemberZone

Zone member of a zone is represented by a ZoneMemberZone model. It has all the properties of ZoneMember and
one additional:
Property
zone

Description
The Zone associated with this zone member

Note: For more detailed information go to Sylius API ZoneMemberZone.

Interfaces
Model Interfaces

AddressInterface This interface should be implemented by models representing the customers address.
Note: This interface extends TimestampableInterface. For more detailed information go to Sylius API AddressInterface.

CountryInterface This interfaces should be implemented by models representing a country.


Note: This interface extends component_resource_model_toggleable-interface. For more detailed information go to
Sylius API CountryInterface.

ProvinceInterface This interface should be implemented by models representing a part of a country.


Note: For more detailed information go to Sylius API ProvinceInterface.

ZoneInterface This interface should be implemented by models representing a single zone. It also holds all the
Zone Types.
Note: For more detailed information go to Sylius API ZoneInterface.

244

Chapter 8. Components

Sylius, Release

ZoneMemberInterface This interface should be implemented by models that represent an area a specific zone
contains, e.g. all countries in the European Union.
Note: For more detailed information go to Sylius API ZoneMemberInterface.

Service Interfaces

RestrictedZoneCheckerInterface A service implementing this interface should be able to check if given Address
is in a restricted zone.
Note: For more detailed information go to Sylius API RestrictedZoneCheckerInterface.

ZoneMatcherInterface This interface should be implemented by a service responsible of finding the best matching
zone, and all zones containing the provided Address.
Note: For more detailed information go to Sylius API ZoneMatcherInterface.

Zone Types
There are three zone types available by default:
Related constant
TYPE_COUNTRY
TYPE_PROVINCE
TYPE_ZONE

Type
country
province
zone

Note: All of the above types are constant fields in the ZoneInterface.

8.1.3 Archetype
Handling of dynamic attributes and options on PHP models is a common task for most of modern business applications.
Additionally when an object has certain attributes and options, similar objects are likely to be built from the same set
of attributes and/or options.
This Sylius Archetype component makes it easier to define types of objects that have these attributes and options and
attach them when creating a new object. This is called an _archetype_ and a model can be defined as an archetype by
implementing a simple interface.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius\archetype on Packagist);
Use the official Git repository (https://github.com/Sylius/Archetype).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.

8.1. PHP E-Commerce Components

245

Sylius, Release

Basic Usage
The usage of this component bases on a few classes cooperation. Create an Archetype with given Attributes as an
ArrayCollection. To create an Archetype we can use its Builder class, which will use your AttributeValues repository.
Archetype
<?php
use
use
use
use

Doctrine\Common\Collections\ArrayCollection;
Sylius\Component\Attribute\Model\Attribute;
Sylius\Component\Archetype\Model\Archetype;
Sylius\Component\Archetype\Builder\ArchetypeBuilder;

$archetype = new Archetype();


// Before you can start using the new archetype please set its current and fallback locales.
$archetype->setCurrentLocale('en');
$archetype->setFallbackLocale('en');
// Let's create an attribute for our archetype
$attribute = new Attribute();
$attribute->setName('Mug material');
$attribute->setType('text');
// And then let's add it to a collection
$attributes = new ArrayCollection();
$attributes->add($attribute);
$archetype->setName('Mug');
$archetype->setAttributes($attributes);
$archetype->getName(); // returns 'Mug'
$archetype->getAttributes(); // returns $attributes

ArchetypeBuilder
/**
* @param RepositoryInterface $attributeValueRepository
*/
$builder = new ArchetypeBuilder($attributeValueRepository);
/**
* @var ArchetypeSubjectInterface $subject
*/
$subject = /* ... */;
$builder->build($subject);

Note: You can find more information about this class in Sylius API ArchetypeBuilder.

246

Chapter 8. Components

Sylius, Release

Models
Archetype

Every archetype is represented by the Archetype instance and has the following properties:
Property
id
code
attributes
options
parent
createdAt
updatedAt

Description
Unique id of the archetype
Machine-friendly name of the archetype (car, shoe, bean_bag)
Attributes to add when building new objects based on the archetype
Options to add when building new objects based on the archetype
The parent archetype to inherit from (examples might be Vehicle for Car or Footwear for
Shoe)
Date when attribute was created
Date of last attribute update

Note: This model implements the ArchetypeInterface. You can find more information about this class in Sylius API
Archetype.

ArchetypeTranslation

An Archetype instance needs also a corresponding ArchetypeTranslation.


Property
id
name

Description
Unique id of the archetypes translation
Name of the archetype s translation

Note: This model implements the ArchetypeTranslationInterface. You can find more information about this class in
Sylius API ArchetypeTranslation.

Interfaces
Model Interfaces

ArchetypeInterface This interface should be implemented by models representing an Archetype.


Note: This interface extends the ArchetypeTranslationInterface and the TimestampableInterface. You will find more
information about this interface in Sylius Api ArchetypeInterface.

ArchetypeTranslationInterface This interface should be implemented by models representing an ArchetypeTranslation.


Note: You will find more information about this interface in Sylius Api ArchetypeTranslationInterface.

ArchetypeSubjectInterface To characterize an object with attributes and options from a common archetype, the
object class needs to implement the ArchetypeSubjectInterface.
Note: This interface extends the AttributeSubjectInterface and the VariableInterface. You will find more information
about this interface in Sylius Api ArchetypeSubjectInterface.
8.1. PHP E-Commerce Components

247

Sylius, Release

Services Interfaces

ArchetypeBuilderInterface This interface should be implemented by services that will build the archetypes of
products.
Note: You will find more information about this interface in Sylius Api ArchetypeBuilderInterface.

8.1.4 Attribute
Handling of dynamic attributes on PHP models is a common task for most of modern business applications. Sylius
component makes it easier to handle different types of attributes and attach them to any object by implementing a
simple interface.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius/attribute on Packagist).
Use the official Git repository (https://github.com/Sylius/Attribute).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Basic Usage
Creating an attributable class

In the following example you will see a minimalistic implementation of the AttributeSubjectInterface.
<?php
namespace Example\Model;
use Sylius\Component\Attribute\Model\AttributeSubjectInterface;
use Sylius\Component\Attribute\Model\AttributeValueInterface;
class Shirt implements AttributeSubjectInterface
{
/**
* @var AttributeValueInterface[]
*/
private $attributes;
/**
* {@inheritdoc}
*/
public function getAttributes()
{
return $this->attributes;
}

248

Chapter 8. Components

Sylius, Release

/**
* {@inheritdoc}
*/
public function setAttributes(array $attributes)
{
foreach ($attributes as $attribute) {
$this->addAttribute($attribute);
}
}
/**
* {@inheritdoc}
*/
public function addAttribute(AttributeValueInterface $attribute)
{
if (!$this->hasAttribute($attribute)) {
$attribute->setSubject($this);
$this->attributes[] = $attribute;
}
}
/**
* {@inheritdoc}
*/
public function removeAttribute(AttributeValueInterface $attribute)
{
if ($this->hasAttribute($attribute)){
$attribute->setSubject(null);
$key = array_search($attribute, $this->attributes);
unset($this->attributes[$key]);
}
}
/**
* {@inheritdoc}
*/
public function hasAttribute(AttributeValueInterface $attribute)
{
return in_array($attribute, $this->attributes);
}
/**
* {@inheritdoc}
*/
public function hasAttributeByName($attributeName)
{
foreach ($this->attributes as $attribute) {
if ($attribute->getName() === $attributeName) {
return true;
}
}
return false;
}
/**
* {@inheritdoc}
*/

8.1. PHP E-Commerce Components

249

Sylius, Release

public function getAttributeByName($attributeName)


{
foreach ($this->attributes as $attribute) {
if ($attribute->getName() === $attributeName) {
return $attribute;
}
}
return null;
}
}

Note: An implementation similar to the one above has been done in the component_product_model_product model.

Adding attributes to an object

Once we have our class we can characterize it with attributes.


<?php
use Example\Model\Shirt;
use Sylius\Component\Attribute\Model\Attribute;
use Sylius\Component\Attribute\Model\AttributeValue;
$attribute = new Attribute();
$attribute->setName('Size');
$smallSize = new AttributeValue();
$mediumSize = new AttributeValue();
$smallSize->setValue('S');
$mediumSize->setValue('M');
$smallSize->setAttribute($attribute);
$mediumSize->setAttribute($attribute);
$shirt = new Shirt();
$shirt->addAttribute($smallSize);
$shirt->addAttribute($mediumSize);

Or you can just add all attributes needed using a class implementing Doctrines Collection interface, e.g. the ArrayCollection class.
<?php
use Doctrine\Common\Collections\ArrayCollection;
$attributes = new ArrayCollection();
$attributes->add($smallSize);
$attributes->add($mediumSize);
$shirt->setAttributes($attributes);

Note: Notice that you dont actually add an Attribute to the subject, instead you need to add every AttributeValue
250

Chapter 8. Components

Sylius, Release

assigned to the attribute.

Accessing attributes
<?php
$shirt->getAttributes(); // returns an array containing all set attributes
$shirt->hasAttribute($smallSize); // returns true
$shirt->hasAttribute($hugeSize); // returns false

Accessing attributes by name


<?php
$shirt->hasAttributeByName('Size'); // returns true
$shirt->getAttributeByName('Size'); // returns $smallSize

Removing an attribute
<?php
$shirt->hasAttribute($smallSize); // returns true
$shirt->removeAttribute($smallSize);
$shirt->hasAttribute($smallSize); // now returns false

Models
Attribute

Every attribute is represented by the Attribute model which by default has the following properties:
Property
id
type
name
configuration
values
createdAt
updatedAt

Description
Unique id of the attribute
Attributes type (text by default)
Attributes name
Attributes configuration
Collection of attribute values
Date when attribute was created
Date of last attribute update

Note: This model extends the AbstractTranslatable class and implements the AttributeInterface. For more detailed
information go to Sylius API Attribute.
Hint: Default attribute types are stored in the AttributeTypes class.

8.1. PHP E-Commerce Components

251

Sylius, Release

AttributeValue

This model binds the subject and the attribute, it is used to store the value of the attribute for the subject. It has the
following properties:
Property
id
subject
attribute
value

Description
Unique id of the attribute value
Reference to attributes subject
Reference to an attribute
Attributes value

Note: This model implements the AttributeValueInterface. For more detailed information go to Sylius API AttributeValue.

AttributeTranslation

The attributes presentation for different locales is represented by the AttributeTranslation model which has the
following properties:
Property
id
presentation

Description
Unique id of the attribute translation
Attributes name for given locale

Note: This model extends the AbstractTranslation class and implements the AttributeTranslationInterface. For more
detailed information go to Sylius API AttributeTranslation.

Interfaces
Model Interfaces

AttributeInterface This interface should be implemented by models used for describing a products attribute.
Note: This interface extends the TimestampableInterface and the AttributeTranslationInterface. For more detailed
information go to Sylius API AttributeInterface.

AttributeValueInterface This interface should be implemented by models used for binding an Attribute with a
model implementing the AttributeSubjectInterface e.g. the component_product_model_product.
Note: For more detailed information go to Sylius API AttributeValueInterface.

AttributeTranslationInterface This interface should be implemented by models maintaining a single translation of


an Attribute for specified locale.
Note: For more detailed information go to Sylius API AttributeTranslationInterface.

AttributeSubjectInterface This interface should be implemented by models you want to characterize with various
AttributeValue objects.
It will ask you to implement the management of AttributeValue models.
252

Chapter 8. Components

Sylius, Release

Note: For more detailed information go to Sylius API AttributeSubjectInterface.

AttributeTypes
The following attribute types are available by default in the AttributeTypes class:
Related constant
TEXT
NUMBER
PERCENTAGE
CHECKBOX
CHOICE
MONEY

Type
text
number
percentage
checkbox
choice
money

Hint: Use the static method AttributeTypes::getChoices() to get an array containing all types.
Note: For more detailed information go to Sylius API AttributeTypes.

8.1.5 Cart
Common models, services and interface to handle a shopping cart in PHP e-commerce application. It is strictly related
to Order model.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius/cart on Packagist);
Use the official Git repository (https://github.com/Sylius/Cart).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Basic Usage
Note: The cart is basically an order with an appropriate state. Check Orders State Machine.
Hint: For more examples go to Order Basic Usage.

CartContext

The CartContext provides you with useful tools to set and retrieve current cart identifier based on storage.
<?php
use Sylius\Component\Cart\Context\CartContext;
use Sylius\Component\Cart;

8.1. PHP E-Commerce Components

253

Sylius, Release

$context = new CartContext();


$cart = new Cart();
$currentCartIdentifier = $context->getCurrentCartIdentifier();
$context->setCurrentCartIdentifier($cart);
$context->resetCurrentCartIdentifier();

Models
Cart

The cart is represented by Cart instance. It inherits all the properties from component_order_model_order and add
one property:
Method
expiresAt

Description
Expiration time

Type
DateTime

Note: This model implements the CartInterface

CartItem

The items of the cart are represented by the CartItem instances. CartItem has no properties but it inherits from
OrderItem.
Note: This model implements the CartItemInterface

Interfaces
Model Interfaces

CartInterface This interface should be implemented by model representing a single Cart.


Note: This interface extends the OrderInterface For more detailed information go to Sylius API CartInterface_.

CartItemInterface This interface should be implemented by model representing a single CartItem.


Service Interfaces

CartProviderInterface A cart provider retrieves existing cart or create new one based on the storage. To characterize an object which is a Provider, it needs to implement the CartProviderInterface.
Note: For more detailed information go to Sylius Api CartProviderInterface.

254

Chapter 8. Components

Sylius, Release

PurgerInterface A cart purger purges all expired carts. To characterize an object which is a Purger, it needs to
implement the PurgerInterface.
Note: For more detailed information go to Sylius Api PurgerInterface.

ItemResolverInterface A cart resolver returns cart item that needs to be added based on given data. To characterize
an object which is a Resolver, it needs to implement the ItemResolverInterface.
Note: For more detailed information go to Sylius Api ItemResolverInterface.
Caution: This method throws ItemResolvingException if an error occurs.

CartRepositoryInterface In order to decouple from storage that provides expired carts, you should create repository
class which implements this interface.
Note: This interface extends the OrderRepositoryInterface For more detailed information go to Sylius API
CartRepositoryInterface_.

CartContextInterface This interface is implemented by the services responsible for setting and retrieving current cart identifier based on storage. To characterize an object which is a CartContext it needs to implement the
CartContextInterface
Note: For more detailed information go to Sylius API CartContextInterface.

8.1.6 Channel
Sale channels management implementation in PHP.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius/channel on Packagist);
Use the official Git repository (https://github.com/Sylius/Channel).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Basic Usage
ChannelContext

The ChannelContext allows you to manage the currently used sale channel.

8.1. PHP E-Commerce Components

255

Sylius, Release

<?php
use Sylius\Component\Channel\Context\ChannelContext;
use Sylius\Component\Channel\Model\Channel;
$channel = new Channel();
$channelContext = new ChannelContext();
$channelContext->setChannel($channel);
$channelContext->getChannel(); // will return the $channel object

Note: This service implements ChannelContextInterface.

Models
Channel

Sale channel is represented by a Channel model. It should have everything concerning channels data and as default
has the following properties:
Property
id
code
name
description
url
color
enabled
createdAt
updatedAt

Description
Unique id of the channel
Channels code
Channels name
Channels description
Channels URL
Channels color
Indicates whether channel is available
Date of creation
Date of update

Note: This model implements ChannelInterface. For more detailed information go to Sylius API Channel.

Interfaces
Model Interfaces

ChannelInterface This interface should be implemented by every custom sale channel model.
Note: This interface extends TimestampableInterface. For more detailed information go to Sylius API ChannelInterface.

ChannelAwareInterface This interface should be implemented by models associated with a specific sale channel.
Note: For more detailed information go to Sylius API ChannelAwareInterface.

ChannelsAwareInterface This interface should be implemented by models associated with multiple channels.

256

Chapter 8. Components

Sylius, Release

Note: For more detailed information go to Sylius API ChannelsAwareInterface.

Service Interfaces

ChannelContextInterface This interface should be implemented by a service responsible for managing the currently used Channel.
Note: For more detailed information go to Sylius API ChannelContextInterface.

ChannelRepositoryInterface This interface should be implemented by repositories responsible for storing the
Channel objects.
Note: For more detailed information go to Sylius API ChannelRepositoryInterface.

8.1.7 Contact
Managing contact form for PHP apps.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use following command to add the
component to your composer.json and download package.
If you have Composer installed globally.
$ composer require sylius/contact:*

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require sylius/contact:*

Models
ContactTopic
ContactRequest

Summary
phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This component uses GitHub issues. If you have found bug, please create an issue.
8.1. PHP E-Commerce Components

257

Sylius, Release

8.1.8 Currency
Managing different currencies, exchange rates and converting cash amounts for PHP apps.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use following command to add the
component to your composer.json and download package.
If you have Composer installed globally.
$ composer require sylius/currency:*

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require sylius/currency:*

Models
Currency

Every currency is represented by Currency instance and has following properties:


Method
code
exchangeRate
enabled
createdAt
updatedAt

Description
Code of the currency
Exchange rate
Date of creation
Date of last update

Type
string
float
boolean
DateTime
DateTime

CurrencyInterface

Note: This interface asks you to implement a extra method named CurrencyInterface::getName() which
will return the human-friendly name of the currency

Currency provider
The CurrencyProvider allows you
CurrencyProviderInterface.

to

get

the

available

currencies,

it

implements

the

$currencyRepository = new EntityRepository();


$currencyProvider = new CurrencyProvider($currencyRepository);
$availableCurrencies = $currencyProvider->getAvailableCurrencies();
foreach ($availableCurrencies as $currency) {
echo $currency->getCode();
}

258

Chapter 8. Components

Sylius, Release

Summary
phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This component uses GitHub issues. If you have found bug, please create an issue.

8.1.9 Grid
Sylius component used for describing data grids. Decoupled from Symfony and useful for any kind of system, which
needs to provide user with grid functionality.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use following command to add the
component to your composer.json and download package.
If you have Composer installed globally.
$ composer require sylius/grid:0.15.0

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require sylius/grid:0.15.0

Summary
phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This component uses GitHub issues. If you have found bug, please create an issue.

8.1.10 Inventory
Inventory management for PHP applications.

8.1. PHP E-Commerce Components

259

Sylius, Release

Installation
You can install the component in 2 different ways:
Install it via Composer (sylius\inventory on Packagist);
Use the official Git repository (https://github.com/Sylius/Inventory).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Basic Usage
Stockable Object

The first thing you should do it is implementing stockable object. Example implementation:
<?php
class Product implements StockableInterface
{
/**
* Get stock keeping unit.
*
* @return mixed
*/
public function getSku()
{
// TODO: Implement getSku() method.
}
/**
* Get inventory displayed name.
*
* @return string
*/
public function getInventoryName()
{
// TODO: Implement getInventoryName() method.
}
/**
* Simply checks if there any stock available.
* It should also return true for items available on demand.
*
* @return Boolean
*/
public function isInStock()
{
// TODO: Implement isInStock() method.
}
/**
* Is stockable available on demand?
*
* @return Boolean
*/
public function isAvailableOnDemand()

260

Chapter 8. Components

Sylius, Release

{
// TODO: Implement isAvailableOnDemand() method.
}
/**
* Get stock on hold.
*
* @return integer
*/
public function getOnHold()
{
// TODO: Implement getOnHold() method.
}
/**
* Set stock on hold.
*
* @param integer
*/
public function setOnHold($onHold)
{
// TODO: Implement setOnHold() method.
}
/**
* Get stock on hand.
*
* @return integer
*/
public function getOnHand()
{
// TODO: Implement getOnHand() method.
}
/**
* Set stock on hand.
*
* @param integer $onHand
*/
public function setOnHand($onHand)
{
// TODO: Implement setOnHand() method.
}
}

InventoryOperator

The InventoryOperator provides basic operations on your inventory.


<?php
use
use
use
use

Sylius\Component\Inventory\Operator\InventoryOperator;
Sylius\Component\Inventory\Checker\AvailabilityChecker;
Sylius\Component\Inventory\Operator\BackordersHandler;
Sylius\Component\Resource\Repository\InMemoryRepository;

$inMemoryRepository = new InMemoryRepository(); // Repository model.

8.1. PHP E-Commerce Components

261

Sylius, Release

$product = new Product(); // Stockable model.


$eventDispatcher; // It gives a possibilty to hook before or after each operation.
// If you are not familiar with events, check the symfony Event Dispatcher.

$availabilityChecker = new AvailabilityChecker(false);


$backordersHandler = new BackordersHandler($inventoryUnitRepository);
$inventoryOperator = new InventoryOperator($backordersHandler, $availabilityChecker, $eventDispatcher
$product->getOnHand(); // Output will be 0.
$inventoryOperator->increase($product, 5);
$product->getOnHand(); // Output will be 5.
$product->getOnHold(); // Output will be 0.
$inventoryOperator->hold($product, 4);
$product->getOnHold(); // Output will be 4.
$inventoryOperator->release($product, 3);
$product->getOnHold(); // Output will be 1.

Decrease This specific case will be more complicated. It uses backordersHandler to :ref:process-backorders_.
<?php
use
use
use
use
use
use

Sylius\Component\Inventory\Operator\InventoryOperator;
Sylius\Component\Inventory\Checker\AvailabilityChecker;
Sylius\Component\Inventory\Operator\BackordersHandler;
Doctrine\Common\Collections\ArrayCollection;
Sylius\Component\Inventory\Model\InventoryUnit;
Sylius\Component\Inventory\Model\InventoryUnitInterface;

$inventoryUnitRepository; // Repository model.


$product = new Product(); // Stockable model.
$eventDispatcher; // It gives possibilty to hook before or after each operation.
// If you are not familiar with events. Check symfony event dispatcher.

$availabilityChecker = new AvailabilityChecker(false);


$backordersHandler = new BackordersHandler($inventoryUnitRepository);
$inventoryOperator = new InventoryOperator($backordersHandler, $availabilityChecker, $eventDispatcher
$inventoryUnit1 = new InventoryUnit();
$inventoryUnit2 = new InventoryUnit();
$inventoryUnits = new ArrayCollection();
$product->getOnHand(); // Output will be 5.
$inventoryUnit1->setStockable($product);
$inventoryUnit1->setInventoryState(InventoryUnitInterface::STATE_SOLD);
$inventoryUnit2->setStockable($product);
$inventoryUnits->add($inventoryUnit1);
$inventoryUnits->add($inventoryUnit2);
count($inventoryUnits); // Output will be 2.
$inventoryOperator->decrease($inventoryUnits);
$product->getOnHand(); // Output will be 4.

262

Chapter 8. Components

Sylius, Release

Caution: All methods in InventoryOperator throw InvalidArgumentException or InsufficientStockException if


an error occurs.
Note: For more detailed information go to Sylius API InventoryOperator.
Hint: To understand how events work check Symfony EventDispatcher.

NoopInventoryOperator

In some cases, you may want to have unlimited inventory, this operator will allow you to do that.
Hint: This operator is based on the null object pattern. For more detailed information go to Null Object pattern.
Note: For more detailed information go to Sylius API NoopInventoryOperator.

BackordersHandler

The BackorderHandler changes inventory unit state.


Process backorders This method will change the inventory unit state to backordered if the quantity of requested
inventory units will be insufficient.
<?php
use
use
use
use

Sylius\Component\Inventory\Operator\BackordersHandler;
Doctrine\Common\Collections\ArrayCollection;
Sylius\Component\Inventory\Model\InventoryUnit;
Sylius\Component\Inventory\Model\InventoryUnitInterface;

$inventoryUnitRepository; // Repository model.


$product = new Product(); // Stockable model.
$backordersHandler = new BackordersHandler($inventoryUnitRepository);
$inventoryUnit1 = new InventoryUnit();
$inventoryUnit2 = new InventoryUnit();
$inventoryUnits = new ArrayCollection();

$product->getOnHand(); // Output will be 1.


$inventoryUnit1->setStockable($product);
$inventoryUnit1->setInventoryState(InventoryUnitInterface::STATE_SOLD);
$inventoryUnit2->setStockable($product);
$inventoryUnit2->setInventoryState(InventoryUnitInterface::STATE_CHECKOUT);
$inventoryUnits->add($inventoryUnit1);
$inventoryUnits->add($inventoryUnit2);
count($inventoryUnits); // Output will be 2.
$backordersHandler->processBackorders($inventoryUnits);

8.1. PHP E-Commerce Components

263

Sylius, Release

$inventoryUnit2->getInventoryState(); // Output will be 'backordered'

Fill backorders This method will change inventory unit state to sold.
<?php
use Sylius\Component\Inventory\Operator\BackordersHandler;
$inventoryUnitRepository; // Repository model.
$product = new Product(); // Stockable model.
$backordersHandler = new BackordersHandler($inventoryUnitRepository);
$product->getOnHand(); // Output will be 6.
// Let's assume that we have 9 inventory units with a 'backordered' state.
// This method will find all inventory units for that specific stockable with 'backordered' state.
$backordersHandler->fillBackorders($product);
// Now 6 of them will have 'sold' state.
$product->getOnHand(); // Output will be 0.

Note: For more detailed information go to Sylius API BackordersHandler_.

AvailabilityChecker

The AvailabilityChecker checks availability of a given stockable object. To charactrize an object which is an AvailabilityChecker, it needs to implement the :ref:component_inventory_checker_availability_checker_interface_.
Second parameter of the ->isStockSufficient() method gives a possibility to check for a given quantity of a
stockable.
<?php
use Sylius\Component\Inventory\Checker\AvailabilityChecker;
$product = new Product(); // Stockable model.
$product->isAvailableOnDemand(); // Output will be false.
$product->getOnHand(); // Output will be 5
$product->getOnHold(); // Output will be 4
$availabilityChecker = new AvailabilityChecker(false); // backorders = false;
$availabilityChecker->isStockAvailable($product); // Output will be true.
$availabilityChecker->isStockSufficient($product, 5); // Output will be false.

Backorders The backorder property generally indicates that the customers demand for a product or service exceeds
a stockables capacity to supply it.
<?php
use Sylius\Component\Inventory\Checker\AvailabilityChecker;
$product = new Product(); // Stockable model.
$availabilityChecker = new AvailabilityChecker(true); // backorders = true;

264

Chapter 8. Components

Sylius, Release

$availabilityChecker->isStockAvailable($product); // Output will be true.


$availabilityChecker->isStockSufficient($product, 5); // Output will be true.

Available On Demand
<?php
use Sylius\Component\Inventory\Checker\AvailabilityChecker;
$product = new Product(); // Stockable model.
$product->isAvailableOnDemand(); // Output will be true.
$availabilityChecker = new AvailabilityChecker(false); // backorders = false;
$availabilityChecker->isStockAvailable($product); // Output will be true.
$availabilityChecker->isStockSufficient($product, 5); // Output will be true.

Hint: In the above cases results of ->getOnHand() and ->getOnHold() will be irrelevant.
Note: For more detailed information go to Sylius API AvailabilityChecker.

InventoryUnitFactory

The InventoryUnitFactory creates a collection of new inventory units.


<?php
use Sylius\Component\Inventory\Factory\InventoryUnitFactory;
use Sylius\Component\Inventory\Model\InventoryUnitInterface;
$inventoryUnitRepository; // Repository model.
$product = new Product(); // Stockable model.
$inventoryUnitFactory = new InventoryUnitFactory($inventoryUnitRepository);

$inventoryUnits = $inventoryUnitFactory->create($product, 10, InventoryUnitInterface::STATE_RETURNED)


// Output will be collection of inventory units.
$inventoryUnits[0]->getStockable(); // Output will be your's stockable model.
$inventoryUnits[0]->getInventoryState(); // Output will be 'returned'.
count($inventoryUnits); // Output will be 10.

Note: For more detailed information go to Sylius API InventoryUnitFactory_.

Models
InventoryUnit

InventoryUnit object represents an inventory unit. InventoryUnits have the following properties:

8.1. PHP E-Commerce Components

265

Sylius, Release

Property
id
stockable
invantoryState
createdAt
updatedAt

Description
Unique id of the inventory unit
Reference to any stockable unit. (Implements LocalesAwareInterface)
State of the inventory unit (e.g. checkout, sold)
Date when inventory unit was created
Date of last change

Note: This model implements the InventoryUnitInterface For more detailed information go to Sylius API InventoryUnit.

Interfaces
Model Interfaces

InventoryUnitInterface This interface should be implemented by model representing a single InventoryUnit.


Hint: It also contains the default State Machine.
Note: This interface extends TimestampableInterface. For more detailed information go to Sylius API InventoryUnitInterface.

StockableInterface This interface provides basic operations for any model that can be stored.
Note: For more detailed information go to Sylius API StockableInterface.

Service Interfaces

AvailabilityCheckerInterface This interface provides methods for checking availability of stockable objects.
Note: For more detailed information go to Sylius API AvailabilityCheckerInterface.

InventoryUnitFactoryInterface This interface is implemented by services responsible for creating collection of


new inventory units.
Note: For more detailed information go to Sylius API InventoryUnitFactoryInterface.

BackordersHandlerInterface This interface is implemented by services responsible for changing inventory states.
Note: For more detailed information go to Sylius API BackordersHandlerInterface.

InventoryOperatorInterface This interface is implemented by services responsible for managing inventory units.
Note: For more detailed information go to Sylius API InventoryOperatorInterface.

266

Chapter 8. Components

Sylius, Release

State Machine
Inventory Unit States

Sylius itself uses a complex state machine system to manage all states of the business domain. This component has
some sensible default states defined in the InventoryUnitInterface.
All new InventoryUnit instances have the state checkout by default, which means they are in the cart and wait for
verification. The following states are defined:
Related constant
STATE_CHECKOUT
STATE_ONHOLD
STATE_SOLD
STATE_BACKORDERED
STATE_RETURNED

State
checkout
onhold
sold
backordered
returned

Description
Item is in the cart
Item is hold (e.g. waiting for the payment)
Item has been sold and is no longer in the warehouse
Item has been sold, but is not in stock and waiting for supply
Item has been sold, but returned and is in stock

Tip: Please keep in mind that these states are just default, you can define and use your own. If you use this component
with SyliusInventoryBundle and Symfony2, you will have full state machine configuration at your disposal.

Inventory Unit Transitions

There are the following orders transitions by default:


Related constant
SYLIUS_HOLD
SYLIUS_BACKORDER
SYLIUS_SELL
SYLIUS_RELEASE
SYLIUS_RETURN

Transition
hold
backorder
sell
release
return

There is also the default graph name included:


Related constant
GRAPH

Name
sylius_inventory_unit

Note: All of above transitions and the graph are constant fields in the InventoryUnitTransitions class.

8.1.11 Locales
Managing different locales for PHP apps.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius\locale on Packagist);
Use the official Git repository (https://github.com/Sylius/Locale).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.

8.1. PHP E-Commerce Components

267

Sylius, Release

Basic Usage
LocaleContext

The LocaleContext allows you to manage the currently used locale.


<?php
use Sylius\Component\Storage\StorageInterface;
class Storage implements StorageInterface
{
/**
* {@inheritdoc}
*/
public function hasData($key)
{
// TODO: Implement hasData() method.
}
/**
* {@inheritdoc}
*/
public function getData($key, $default = null)
{
// TODO: Implement getData() method.
}
/**
* {@inheritdoc}
*/
public function setData($key, $value)
{
// TODO: Implement setData() method.
}
/**
* {@inheritdoc}
*/
public function removeData($key)
{
// TODO: Implement removeData() method.
}
}
<?php
use Sylius\Component\Locale\Context\LocaleContext;
use Sylius\Component\Resource\Repository\InMemoryRepository;
$storage = new Storage();
$localeContext = new LocaleContext($storage, 'en_US');
$localeContext->getDefaultLocale() // Output will be 'en'.
$localeContext->getCurrentLocale() // Output based on your storage implementation.
$localeContext->setCurrentLocale('us') // It will set your default locale in your storage.

268

Chapter 8. Components

Sylius, Release

Note: For more detailed information go to Sylius API LocaleContext.

LocaleProvider

The LocaleProvider allows you to get all available locales.


<?php
use Sylius\Component\Locale\Provider\LocaleProvider;
$locales = new InMemoryRepository();
$localeProvider = new LocaleProvider($locales);
$localeProvider->getAvailableLocales() //Output will be a collection of all enabled locales
$localeProvider->isLocaleAvailable('en') //It will check if that locale is enabled

Note: For more detailed information go to Sylius API LocaleProvider.

Models
Locale

Locale represents one locale available in the application. It uses Symfony Intl component to return locale name.
Locale has the following properties:
Property
id
code
enabled
createdAt
updatedAt

Description
Unique id of the locale
Locales code
Indicates whether locale is available
Date when locale was created
Date of last change

Hint: This model has one const STORAGE_KEY it is key used to store the locale in storage.
Note:
This model implements the LocaleInterface
For more detailed information go to Sylius API Locale.

Interfaces
Model Interfaces

LocaleInterface This interface should be implemented by models representing a single Locale.


Note:
This interface extends TimestampableInterface and component_resource_model_toggleable-interface
For more detailed information go to Sylius API LocaleInterface.

8.1. PHP E-Commerce Components

269

Sylius, Release

LocalesAwareInterface This interface provides basic operations for locale management. If you want to have locales
in your model just implement this interface.
Note: For more detailed information go to Sylius API LocalesAwareInterface.

Service Interfaces

LocaleContextInterface This interface is implemented by the service responsible for managing the current locale.
Note: For more detailed information go to Sylius API LocaleContextInterface.

LocaleProviderInterface This interface is implemented by the service responsible for providing you with a list of
available locales.
Note: For more detailed information go to Sylius API LocaleProviderInterface.

8.1.12 Mailer
Sylius Mailer component abstracts the process of sending e-mails. It also provides interface to configure various
parameters for unique e-mails.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius\mailer on Packagist);
Use the official Git repository (https://github.com/Sylius/Mailer).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Basic usage
Sender

SenderAdapter This is an abstraction layer that allows you to create your own logic of sending an email.
<?php
use Sylius\Component\Mailer\Sender\Adapter\AbstractAdapter as BaseSenderAdapter;
use Sylius\Component\Mailer\Model\EmailInterface;
use Sylius\Component\Mailer\Model\Email;
class SenderAdapter extends BaseSenderAdapter
{
/**
* Send an e-mail.
*
* @param array $recipients
* @param string $senderAddress

270

Chapter 8. Components

Sylius, Release

* @param string $senderName


* @param RenderedEmail $renderedEmail
* @param EmailInterface $email
*/
public function send(array $recipients, $senderAddress, $senderName, RenderedEmail $renderedEmail
{
// TODO: Implement send() method.
}
}
$email = new Email();
$email->setCode('christmas_party_invitiation');
$email->setContent('Hi, we would like to invite you to christmas party');
$email->setSubject('Christmas party');
$email->setSenderAddress('mike.ehrmantraut@gmail.com');
$email->setSenderName('Mike Ehrmantraut');
$senderAdapter = new SenderAdapter();
$rendererAdapter = new RendererAdapter();
$renderedEmail = $rendererAdapter->render($email, $data);

$senderAdapter->send(array('john.doe@gmail.com'), $email->getSenderAddress(), $email->getSenderName()

Sender This service collects those two adapters SenderAdapter, RendererAdapter and deals with process of sending an email.
<?php
use Sylius\Component\Mailer\Provider\DefaultSettingsProvider;
use Sylius\Component\Mailer\Provider\EmailProvider;
use Sylius\Component\Mailer\Sender\Sender;
$sender = new Sender($rendererAdapter, $senderAdapter, $emailProvider, $defaultSettingsProvider);
$sender->send('christmas_party_invitiation', array('mike.ehrmantraut@gmail.com'));

Renderer

RendererAdapter This is an abstraction layer that allows you to create your own logic of rendering an email object.
<?php
use Sylius\Component\Mailer\Renderer\Adapter\AbstractAdapter as BaseRendererAdapter;
use Sylius\Component\Mailer\Model\EmailInterface;
use Sylius\Component\Mailer\Model\Email;
class RendererAdapter extends BaseRendererAdapter
{
/**
* Render an e-mail.
*
* @param EmailInterface $email
* @param array $data
*

8.1. PHP E-Commerce Components

271

Sylius, Release

* @return RenderedEmail
*/
public function render(EmailInterface $email, array $data = array())
{
// TODO: Implement render() method.
return new RenderedEmail($subject, $body);
}
}
$email = new Email();
$email->setCode('christmas_party_invitiation');
$email->setContent('Hi, we would like to invite you to christmas party');
$email->setSubject('Christmas party');
$email->setSenderAddress('mike.ehrmantraut@gmail.com');
$email->setSenderName('Mike Ehrmantraut');
$rendererAdapter = new RendererAdapter();
$renderedEmail = $rendererAdapter->render($email, $data); // It will render an email object based on
$renderedEmail->getBody(); // Output will be Hi, we would .....
$renderedEmail->getSubject(); // Output will be Christmas party.

Hint: Renderer should return RenderedEmail

DefaultSettingsProvider

The DefaultSettingsProvider is service which provides you with default sender address and sender name.
<?php
use Sylius\Component\Mailer\Provider\DefaultSettingsProvider;

$defaultSettingsProvider = new DefaultSettingsProvider('Mike Ehrmantraut', 'mike.ehrmantraut@gmail.co


$defaultSettingsProvider->getSenderAddress(); // mike.ehrmantraut@gmail.com
$defaultSettingsProvider->getSenderName(); // Output will be Mike Ehrmantraut

EmailProvider

The EmailProvider allows you to get specific email from storage.


<?php
use Sylius\Component\Mailer\Provider\EmailProvider;
use Sylius\Component\Resource\Repository\InMemoryRepository;
$inMemoryRepository = new InMemoryRepository();
$configuration = array(
'christmas_party_invitiation' => array(
'subject' => 'Christmas party',
'template' => 'email.html.twig',
'enabled' => true,

272

Chapter 8. Components

Sylius, Release

'sender' => array(


'name' => 'John',
'address' => 'john.doe@gmail.com',
),
),
);
$emailProvider = new EmailProvider($inMemoryRepository, $configuration);

$email = $emailProvider->getEmail('christmas_party_invitiation'); // This method will search for an e


$email->getCode(); // Output will be christmas_party_invitiation.
$email->getSenderAddress(); // Output will be john.doe@gmail.com.
$email->getSenderName(); // Output will be John.

Models
Email

Email object represents an email. Email has the following properties:


Property
id
code
enabled
subject
content
template
senderName
senderAddress
createdAt
updatedAt

Description
Unique id of the email
Code of the email
Indicates whether email is available
Subject of the email message
Content of the email message
Template of the email
Name of a sender
Address of a sender
Date when the email was created
Date of last change

Note: This model implements the EmailInterface For more detailed information go to Sylius API Email.

Interfaces
Model Interfaces

EmailInterface This interface should be implemented by model representing a single type of Email.
Note: This interface extends TimestampableInterface. For more detailed information go to Sylius API EmailInterface.

Service Interfaces

DefaultSettingsProviderInterface This interface provides methods for retrieving default sender name nad address.
Note: For more detailed information go to Sylius API DefaultSettingsProviderInterface.

8.1. PHP E-Commerce Components

273

Sylius, Release

EmailProviderInterface This interface provides methods for retrieving an email from storage.
Note: For more detailed information go to Sylius API EmailProviderInterface.

Sender The Sender it is way of sending emails


AdapterInterface This interface provides methods for sending an email. This is an abstraction layer to provide
flexibility of mailer component. The Adapter is injected into sender thanks to this you are free to inject your own logic
of sending an email, one thing you should do is just implement this interface.
SenderInterface This interface provides methods for sending an email.
Renderer
AdapterInterface This interface provides methods for rendering an email. The Adapter is inject into sender for
rendering emails content.

8.1.13 Order
E-Commerce PHP library for creating and managing sales orders.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius\order on Packagist);
Use the official Git repository (https://github.com/Sylius/Order).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Basic Usage
Order

Every order has 2 main identifiers, an ID and a human-friendly number. You can access those by calling
->getId() and ->getNumber() respectively. The number is mutable, so you can change it by calling
->setNumber(E001) on the order instance.
Order Totals
Note: All money amounts in Sylius are represented as cents - integers. An order has 3 basic totals, which are all
persisted together with the order. The first total is the items total, it is calculated as the sum of all item totals. The
second total is the adjustments total, you can read more about this in next chapter.

274

Chapter 8. Components

Sylius, Release

<?php
echo $order->getItemsTotal(); //Output will be 1900.
echo $order->getAdjustmentsTotal(); //Output will be -250.
$order->calculateTotal();
echo $order->getTotal(); //Output will be 1650.

The main order total is a sum of the previously mentioned values. You can access the order total value using the
->getTotal() method.
Recalculation of totals can happen by calling ->calculateTotal() method, using the simplest math. It will also
update the item totals.
Items Management The collection of items (Implementing the Doctrine\Common\Collections\Collection
interface) can be obtained using the ->getItems(). To add or remove items, you can simply use the addItem
and removeItem methods.
<?php
use Sylius\Component\Order\Model\Order;
use Sylius\Component\Order\Model\OrderItem;
$order = new Order();
$item1 = new OrderItem();
$item1->setName('Super cool product');
$item1->setUnitPrice(1999); // 19.99!
$item1->setQuantity(2);
$item2 = new OrderItem();
$item2->setName('Interesting t-shirt');
$item2->setUnitPrice(2549); // 25.49!
$order->addItem($item1);
$order->addItem($item2);
$order->removeItem($item1);

Order Item

An order item model has only the id property as identifier and it has the order reference, accessible via
->getOrder() method.
Order Item totals Just like for the order, the total is available via the same method, but the unit price is accessible
using the ->getUnitPrice() Each item also can calculate its total, using the quantity (->getQuantity())
and the unit price.
<?php
use Sylius\Component\Order\Model\OrderItem;
$item = new OrderItem();
$item->setUnitPrice(2000);
$item->setQuantity(4);
$item->calculateTotal();

8.1. PHP E-Commerce Components

275

Sylius, Release

$item->getTotal(); //Output will be 8000.

Applying adjustments to OrderItem An OrderItem can also hold adjustments.


<?php
use Sylius\Component\Order\Model\OrderItem;
use Sylius\Component\Order\Model\Adjustment;
$adjustment = new Adjustment();
$adjustment->setAmount(1200);
$adjustment->setType('tax');
$item = new OrderItem();
$item->addAdjustment($adjustment);
$item->setUnitPrice(2000);
$item->setQuantity(2);
$item->calculateTotal();
$item->getTotal(); //Output will be 5200.

Adjustments

Neutral Adjustments In some cases, you may want to use Adjustment just for displaying purposes. For example,
when your order items have the tax already included in the price.
Every Adjustment instance has the neutral property, which indicates if it should be counted against object total.
<?php
use Sylius\Component\Order\Order;
use Sylius\Component\Order\OrderItem;
use Sylius\Component\Order\Adjustment;
$order = new Order();
$tshirt = new OrderItem();
$tshirt->setUnitPrice(4999);
$shippingFees = new Adjustment();
$shippingFees->setAmount(1000);
$tax = new Adjustment();
$tax->setAmount(1150);
$tax->setNeutral(true);
$order->addItem($tshirt);
$order->addAdjustment($shippingFees);
$order->addAdjustment($tax);
$order->calculateTotal();
$order->getTotal(); // Output will be 5999.

Negative Adjustments Adjustments can also have negative amounts, which means that they will decrease the order
total by certain amount. Lets add a 5$ discount to the previous example.

276

Chapter 8. Components

Sylius, Release

<?php
use Sylius\Component\Order\Order;
use Sylius\Component\Order\OrderItem;
use Sylius\Component\Order\Adjustment;
$order = new Order();
$tshirt = new OrderItem();
$tshirt->setUnitPrice(4999);
$shippingFees = new Adjustment();
$shippingFees->setAmount(1000);
$tax = new Adjustment();
$tax->setAmount(1150);
$tax->setNeutral(true);
$discount = new Adjustment();
$discount->setAmount(-500);
$order->addItem($tshirt);
$order->addAdjustment($shippingFees);
$order->addAdjustment($tax);
$order->addAdjustment($discount);
$order->calculateTotal();
$order->getTotal(); // Output will be 5499.

Locked Adjustments You can also lock an adjustment, this will ensure that it wont be deleted from order or order
item.
<?php
use Sylius\Component\Order\Order;
use Sylius\Component\Order\OrderItem;
use Sylius\Component\Order\Adjustment;
$order = new Order();
$tshirt = new OrderItem();
$tshirt->setUnitPrice(4999);
$shippingFees = new Adjustment();
$shippingFees->setAmount(1000);
$shippingFees->lock();
$discount = new Adjustment();
$discount->setAmount(-500);
$order->addItem($tshirt);
$order->addAdjustment($shippingFees);
$order->addAdjustment($discount);
$order->removeAdjustment($shippingFees);
$order->calculateTotal();
$order->getTotal(); // Output will be 5499.

8.1. PHP E-Commerce Components

277

Sylius, Release

Models
Order

Order object represents order. Orders have the following properties:


Property
id
completedAt
number
itemsTotal
adjustmentsTotal
total
items
adjustments
comments
identities
createdAt
updatedAt
deletedAt
state

Description
Unique id of the order
Completion time of the order
Number is human-friendly identifier
Total value of items in order
Total value of adjustments
Calculated total (items + adjustments)
Collection of items
Collection of adjustments
Collection of comments
Collection of identities
Date when order was created
Date of last change
Date when order was deleted
State of the order (e.g. cart, pending)

Note: This model implements the OrderInterface For more detailed information go to Sylius API Order.

OrderItem

OrderItem object represents items in order. OrderItems have the following properties:
Property
id
order
quantity
unitPrice
adjustments
adjustmentsTotal
total
immutable

Description
Unique id of the orderItem
Reference to Order
Items quantity
The price of a single unit
Collection of adjustments
Total of the adjustments in orderItem
Total of the orderItem (unitPrice * quantity)
Boolean flag of immutability

Note: This model implements the OrderItemInterface For more detailed information go to Sylius API OrderItem.

Adjustment

Adjustment object represents an adjustment to the orders or order items total. Their amount can be positive (charges
- taxes, shipping fees etc.) or negative (discounts etc.). Adjustments have the following properties:

278

Chapter 8. Components

Sylius, Release

Property
id
order
orderItem
type
description
amount
neutral
locked
originId
originType
createdAt
updatedAt

Description
Unique id of the adjustment
Reference to Order
Reference to OrderItem
Type of the adjustment (e.g. tax)
e.g. Clothing Tax 9%
Adjustment amount
Boolean flag of neutrality
Adjustment lock (prevent from deletion)
Origin id of the adjustment
Origin type of the adjustment
Date when adjustment was created
Date of last change

Note: This model implements the AdjustmentInterface For more detailed information go to Sylius API Adjustment.

Comment

Comment object represents a comment assigned to the order. Comments have the following properties:
Property
id
order
notifyCustomer
comment
state
author
createdAt
updatedAt

Description
Unique id of the comment
Reference to Order
Boolean flag of notification
Comment content
State of order
Comment author
Date when comment was created
Date of last change

Note: This model implements the CommentInterface For more detailed information go to Sylius API Comment.

Identity

Identity object is used for storing external identifications, such as referring order id in some external system (e.g.
ERP). Identities have the following properties:
Property
id
order
name
value

Description
Unique id of the identity
Reference to Order
Identity name (e.g. ebay id)
Identity value (e.g. 24312332)

Note: This model implements the IdentityInterface For more detailed information go to Sylius API Identity.

Interfaces
Model Interfaces

OrderInterface This interface should be implemented by model representing a single Order.

8.1. PHP E-Commerce Components

279

Sylius, Release

Hint: It also contains the default State Machine.


Note:
This interface extends TimestampableInterface, TimestampableInterface, AdjustableInterface, CommentAwareInterface, SoftDeletableInterface and SequenceSubjectInterface. For more detailed information go to
Sylius API OrderInterface.

OrderAwareInterface This interface provides basic operations for order management. If you want to have orders
in your model just implement this interface.
Note: For more detailed information go to Sylius API OrderAwareInterface.

OrderItemInterface This interface should be implemented by model representing a single OrderItem.


Note: This interface extends the OrderAwareInterface and the AdjustableInterface, For more detailed information go
to Sylius API OrderItemInterface.

AdjustmentInterface This interface should be implemented by model representing a single Adjustment.


Note: This interface extends the TimestampableInterface and the OriginAwareInterface For more detailed information
go to Sylius API AdjustmentInterface.

AdjustableInterface This interface provides basic operations for adjustment management. Use this interface if you
want to make a model adjustable.
For example following models implement this interface:
component_resource_model_order
component_resource_model_order-item
Note: For more detailed information go to Sylius API AdjustableInterface.

CommentInterface This interface should be implemented by model representing a single Comment.


Note: This interface extends the sylius_component_resource_model_timestampable-interface For more detailed information go to Sylius API CommentInterface.

CommentAwareInterface This interface provides basic operations for comments management. If you want to have
comments in your model just implement this interface.
Note: For more detailed information go to Sylius API CommentAwareInterface_.

IdentityInterface This interface should be implemented by model representing a single Identity. It can be used for
storing external identifications.
Note: For more detailed information go to Sylius API IdentityInterface_.
280

Chapter 8. Components

Sylius, Release

Services Interfaces

OrderRepositoryInterface In order to decouple from storage that provides recently completed orders or check if
given orders number is already used, you should create repository class which implements this interface.
Note: This interface extends the RepositoryInterface and the HashSubjectRepositoryInterface. For more detailed
information about the interface go to Sylius API OrderRepositoryInterface.

State Machine
Order States

Sylius itself uses a complex state machine system to manage all states of the business domain. This component has
some sensible default states defined in the OrderInterface.
All new Order instances have the state cart by default, which means they are unconfirmed. The following states are
defined:
Related constant
STATE_CART
STATE_CART_LOCKED
STATE_PENDING
STATE_CONFIRMED
STATE_SHIPPED
STATE_ABANDONED
STATE_CANCELLED
STATE_RETURNED

State
cart
cart_locked
pending
confirmed
shipped
abandoned
cancelled
returned

Description
Unconfirmed order, ready to add/remove items
Cart which should not be removed when expired
Order waiting for confirmation
Confirmed and completed order
Order has been shipped to the customer
Been waiting too long for confirmation
Cancelled by customer or manager
Order has been returned and refunded

Tip: Please keep in mind that these states are just default, you can define and use your own. If you use this component
with SyliusOrderBundle and Symfony2, you will have full state machine configuration at your disposal.

Order Transitions

There are following orders transitions by default:


Related constant
SYLIUS_CREATE
SYLIUS_RELEASE
SYLIUS_CONFIRM
SYLIUS_SHIP
SYLIUS_ABANDON
SYLIUS_CANCEL
SYLIUS_RETURN

Transition
create
release
confirm
ship
abandon
cancel
return

There is also the default graph name included:


Related constant
GRAPH

Name
sylius_order

Note: All of above transitions and the graph are constant fields in the OrderTransitions class.

8.1. PHP E-Commerce Components

281

Sylius, Release

8.1.14 Originator
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius/originator on Packagist);
Use the official Git repository (https://github.com/Sylius/Originator).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Basic Usage
Originator

This service searches a repository for the origin object of given object implementing the OriginAwareInterface and
returns it.
Lets say we have class:
<?php
namespace Example\Model;
class Ancestor
{
/**
* @var int
*/
$identifier;
/**
* @param int $id
*/
public function __construct($id = null)
{
$this->identifier = $id;
}
/**
* @return int
*/
public function getIdentifier()
{
return $this->identifier;
}
}

Note: Any class used as an origin needs to have a getter for its identifier.
and:
<?php
namespace Example\Model;

282

Chapter 8. Components

Sylius, Release

use Sylius\Component\Originator\Model\OriginAwareInterface;
class OriginAware implements OriginAwareInterface
{
/**
* @var int
*/
$originId;
/**
* @var string
*/
$originType;
// all of the interface's methods
}

Now, to create a new Originator you will need a service which implements the Doctrines ManagerRegistry and the
requested objects identifier property name (the default setting is id):
<?php
use Sylius\Component\Originator\Originator\Originator;
use //the ManagerRegistry service, here: $registry
$originator = new Originator($registry); //using the default field name
//or
$originator = new Originator($registry, 'identifier');

Setting the origin of an object is really simple:


<?php
use
use
use
use

Sylius\Component\Originator\Originator\Originator;
Example\Model\Ancestor;
Example\Model\OriginAware;
//the ManagerRegistry service, here: $registry

$ancestor = new Ancestor(2);


$emptyAncestor = new Ancestor();
$aware = new OriginAware();
$originator = new Originator($registry, 'identifier');
$originator->setOrigin($aware, $emptyAncestor); // will throw an exception as the origin's
// id field needs to be set
$originator->setOrigin($aware, $ancestor); // this however is successful
$aware->getOriginId(); // returns 2
$aware->getOriginType(); // returns 'Example\Model\Ancestor'

Now, with origin set in $aware object and if we have our $ancestor in a repository we can:

8.1. PHP E-Commerce Components

283

Sylius, Release

$originator->getOrigin($aware); // gets the origin object from it's repository


// and returns it, so in this case returns the $ancestor

Note: This service implements the OriginatorInterface.


Caution: Throws component_resource_exception_unexpected-type-exception and \InvalidArgumentException.

Interfaces
Model Interface

OriginAwareInterface This interface should be implemented by any model which you want to characterize with an
origin.
Note: For more detailed information go to Sylius API OriginAwareInterface.

Service Interfaces

OriginatorInterface A service implementing this interface should be capable of getting any object via an implementation of OriginAwareInterface and setting an origin object.
Note: For more detailed information go to Sylius API OriginatorInterface.

8.1.15 Payment
PHP library which provides abstraction of payments management. It ships with default Payment, PaymentMethod
and CreditCard models.
Note: This component does not provide any payment gateway. Integrate it with Payum.

Installation
You can install the component in 2 different ways:
Install it via Composer (sylius\payment on Packagist);
Use the official Git repository (https://github.com/Sylius/Payment).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Basic Usage
FixedFeeCalculator

This calculator is used to get the amount of fee set at the amount key in the configuration array.

284

Chapter 8. Components

Sylius, Release

<?php
use Sylius\Component\Payment\Calculator\FixedFeeCalculator;
use Sylius\Component\Payment\Model\Payment;
$fixedFeeCalculator = new FixedFeeCalculator();
$payment = new Payment();
$payment->setAmount(500);
$configuration = array('amount' => 10);
$fixedFeeCalculator->calculate($payment, $configuration); // returns value of the 'amount' key
// so in this case 10

Note: This service implements the FeeCalculatorInterface.

PercentFeeCalculator

This calculator uses the percent keys value of the configuration array in order to calculate a payments fee.
<?php
use Sylius\Component\Payment\Calculator\PercentFeeCalculator;
use Sylius\Component\Payment\Model\Payment;
$percentFeeCalculator = new PercentFeeCalculator();
$payment = new Payment();
$payment->setAmount(200);
$configuration = array('percent' => 10);
$percentFeeCalculator->calculate($payment, $configuration); // returns 20

Note: This service implements the FeeCalculatorInterface.

DelegatingFeeCalculator

This calculator doesnt do any calculations itself, instead it uses a specified calculator to do the work and just returns
the result.
<?php
use
use
use
use

Sylius\Component\Payment\Model\Payment;
Sylius\Component\Payment\Model\PaymentMethod;
Sylius\Component\Payment\Calculator\DelegatingFeeCalculator;
Sylius\Component\Registry\ServiceRegistry;

$registry = new ServiceRegistry('Sylius\Component\Payment\Calculator\FeeCalculatorInterface');


$registry->register(DefaultFeeCalculators::FIXED, new FixedFeeCalculator());
$registry->register(DefaultFeeCalculators::PERCENT, new PercentFeeCalculator());

8.1. PHP E-Commerce Components

285

Sylius, Release

$calculator = DelegatingFeeCalculator($registry);
$configurations = array('amount' => 14, 'percent' => 23);
$paymentMethod = new PaymentMethod();
$paymentMethod->setFeeCalculatorConfiguration($configurations);
$payment = new Payment();
$payment->setAmount(200);
$payment->setMethod($paymentMethod);
$calculator->calculate($payment); // returns 14 as the FixedFeeCalculator
// is set by default
$paymentMethod->setFeeCalculator(DefaultFeeCalculators::PERCENT);
$calculator->calculate($payment); // now it returns 46
// because we changed to the PercentFeeCalculator

Hint: All the default calculator types are available via the DefaultFeeCalculators class.
Note: This service implements the DelegatingFeeCalculatorInterface.

Models
Payment

Every payment is represented by a Payment instance and has the following properties:
Property
id
method
currency
amount
state
creditCard
details
createdAt
updatedAt
deletedAt

Description
Unique id of the payment
Payment method associated with this payment
Payments currency
Payments amount
Payments state
Credit card as a source
Payments details
Date of creation
Date of the last update
Date of deletion

Note: This model implements the PaymentInterface and the PaymentSubjectInterface. For more detailed information
go to Sylius API Payment.
Hint: All default payment states are available in Payment States.

CreditCard

Every credit card is represented by a CreditCard instance and has the following properties:

286

Chapter 8. Components

Sylius, Release

Property
id
token
type
cardholderName
number
securityCode
expiryMonth
expiryYear
createdAt
updatedAt

Description
Unique id of the credit card
Payment gateway token
Type of credit card (VISA, MasterCard...)
Cardholders name
Card number
Security code
Expiry month
Expiry year
Date of creation
Date of the last update

Note: This model implements the CreditCardInterface. For more detailed information go to Sylius API CreditCard.

PaymentMethod

Every method of payment is represented by a PaymentMethod instance and has the following properties:
Property
id
name
enabled
description
gateway
environment
feeCalculator
feeCalculatorConfiguration
createdAt
updatedAt

Description
Unique id of the payment method
Payment methods name
Indicate whether the payment method is enabled
Payment methods description
Payment methods gateway to use
Required app environment
Calculator for additional fee costs (by default set to fixed)
Fee calculators configuration
Date of creation
Date of the last update

Note: This model implements the PaymentMethodInterface. For more detailed information go to Sylius API PaymentMethod.

PaymentMethodTranslation

This model is used to ensure that different locales have the correct representation of the following payment properties:
Property
id
name
description

Description
Unique id of the payment method
Payment methods name
Payment methods description

Note: This model extends the component_resource_model_abstract-translation and implements the PaymentMethodTranslationInterface. For more detailed information go to Sylius API PaymentMethodTranslation.

Interfaces
Model Interfaces

CreditCardInterface This interface should be implemented by any custom model representing a credit card. It also
contains all the default Credit Card Types.

8.1. PHP E-Commerce Components

287

Sylius, Release

Note: This interface extends the PaymentSourceInterface and the TimestampableInterface. For more detailed information go to Sylius API CreditCardInterface.

PaymentInterface This interface should be implemented by any custom model representing a payment. Also it
keeps all of the default /components/Payment/payment_states.
Note: This interface extends the TimestampableInterface and the SoftDeletableInterface. For more detailed information go to Sylius API PaymentInterface.

PaymentMethodInterface In order to create a custom payment method class, which could be used by other models
or services from this component, it needs to implement this interface.
Note: This interface extends the TimestampableInterface and the PaymentMethodTranslationInterface. For more
detailed information go to Sylius API PaymentMethodInterface.

PaymentMethodsAwareInterface This interface should be implemented by any custom storage used to store representations of the payment method.
Note: For more detailed information go to Sylius API PaymentMethodsAwareInterface.

PaymentMethodTranslationInterface This interface is needed in creating a custom payment method translation


class, which then could be used by the payment method itself.
Note: For more detailed information go to Sylius API PaymentMethodTranslationInterface.

PaymentSourceInterface This interface needs to be implemented by any custom payment source. The default
payment source is CreditCard.
Note: For more detailed information go to Sylius API PaymentSourceInterface.

PaymentSubjectInterface Only a class implementing this interface can be a used for fee calculation.
Note: For more detailed information go to Sylius API PaymentSubjectInterface.

PaymentsSubjectInterface Any container which manages multiple payments should implement this interface.
Note: For more detailed information go to Sylius API PaymentsSubjectInterface.

Service Interfaces

FeeCalculatorInterface This interface should be implemented by any service designed to calculate the fee of a
payment.

288

Chapter 8. Components

Sylius, Release

Note: For more detailed information go to Sylius API FeeCalculatorInterface.

DelegatingFeeCalculatorInterface This interface should be implemented by any service which doesnt calculate
the fee by itself, but instead chooses another calculator (from a registry etc.) to do the calculation, and then returns the
result.
Note: For more detailed information go to Sylius API DelegatingFeeCalculatorInterface.

PaymentMethodRepositoryInterface This interface should be implemented by your custom repository, used to


handle payment method objects.
Note: For more detailed information go to Sylius API PaymentMethodRepositoryInterface.

Credit Card Types


The following credit card types are available by default:
Related constant
BRAND_VISA
BRAND_MASTERCARD
BRAND_DISCOVER
BRAND_AMEX
BRAND_DINERS_CLUB
BRAND_JCB
BRAND_SWITCH
BRAND_SOLO
BRAND_DANKORT
BRAND_MAESTRO
BRAND_FORBRUGSFORENINGEN
BRAND_LASER

Type
visa
mastercard
discover
amex
diners_club
jcb
switch
solo
dankort
maestro
forbrugsforeningen
laser

Note: All of above types are constant fields in CreditCardInterface.

DefaultFeeCalculators
The following calculator types are available by default:
Related constant
FIXED
PERCENT

Type
fixed
percent

Note: Above types are constant fields in DefaultFeeCalculators class.


Tip: Feel free to implement your own custom fee calculators!

8.1. PHP E-Commerce Components

289

Sylius, Release

State Machine
Payment States

The following payment states are available by default:


Related constant
STATE_NEW
STATE_PENDING
STATE_PROCESSING
STATE_COMPLETED
STATE_AUTHORIZED
STATE_FAILED
STATE_CANCELLED
STATE_VOID
STATE_REFUNDED
STATE_UNKNOWN

State
new
pending
processing
completed
authorized
failed
cancelled
void
refunded
unknown

Description
The newly created payment
Payment waiting to be processed
Payment which is in process of verification
Completed payment
Payment which has been authorized
Payment has failed
Cancelled by a customer or manager
Payment timed out or an order with it has been canceled
A completed payment which has been refunded
Payment is in an unknown state

Note: All of above states are constant fields in the PaymentInterface.

Payment Transitions

The following payment transitions are available by default:


Related constant
SYLIUS_CREATE
SYLIUS_PROCESS
SYLIUS_COMPLETE
SYLIUS_FAIL
SYLIUS_CANCEL
SYLIUS_REFUND
SYLIUS_VOID

Transition
create
process
complete
fail
cancel
refund
void

Theres also the default graph name included:


Related constant
GRAPH

Name
sylius_payment

Note: All of above transitions and the graph are constant fields in the PaymentTransitions class.

8.1.16 Pricing
Calculating prices is a common task for most PHP E-Commerce applications. This library provides multiple strategies
and flexible calculators system.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use following command to add the
component to your composer.json and download package.
If you have Composer installed globally.

290

Chapter 8. Components

Sylius, Release

$ composer require sylius/pricing:*

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require sylius/pricing:*

Summary
phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This component uses GitHub issues. If you have found bug, please create an issue.

8.1.17 Product
Powerful products catalog for PHP applications.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use following command to add the
component to your composer.json and download package.
If you have Composer installed globally.
$ composer require sylius/product:*

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require sylius/product:*

Models
The Product

Product is the main model in SyliusProductComponent. This simple class represents every unique product in the
catalog. The default interface contains the following attributes with appropriate setters and getters.

8.1. PHP E-Commerce Components

291

Sylius, Release

Attribute
id
name
slug
description
availableOn
metaDescription
metaKeywords
createdAt
updatedAt
deletedAt

Description
Unique id of the product
Name of the product
SEO slug, by default created from the name
Description of your great product
Date when product becomes available in catalog
Description for search engines
Comma separated list of keywords for product (SEO)
Date when product was created
Date of last product update
Date of deletion from catalog

Product Properties

Except products, you can also define Properties (think Attributes) and define their values on each product. Default
property model has following structure.
Attribute
id
name
presentation
type
createdAt
updatedAt

Description
Unique id of the property
Name of the property (T-Shirt Material)
Pretty name visible for user (Material)
Property type
Date when property was created
Date of last property update

Currently there are several different property types are available, a proper form widget (Symfony Form type) will be
rendered on product form for entering the value.
Type
text
number
percentage
checkbox
choice
Summary
phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This component uses GitHub issues. If you have found bug, please create an issue.

8.1.18 Promotion
Super-flexible promotions system with support of complex rules and actions. Coupon codes included!

292

Chapter 8. Components

Sylius, Release

Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use following command to add the
component to your composer.json and download package.
If you have Composer installed globally.
$ composer require sylius/promotion:*

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require sylius/promotion:*

Summary
phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This component uses GitHub issues. If you have found bug, please create an issue.

8.1.19 RBAC
Sylius Rbac component implements a hierarchical variant of RBAC, which means that we have separate concept of
Users (Identity), Roles and Permissions. Every Identity can have multiple roles, which inherit all permissions from
their child roles. Permissions are defined as tree as well, top level tree has all the permissions defined in the lower
nodes.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius\rbac on Packagist);
Use the official Git repository (https://github.com/Sylius/Rbac).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Basic Usage
To use RBAC without Symfony2, you need to implement your own services.

8.1. PHP E-Commerce Components

293

Sylius, Release

Permissions and Roles

Permissions are connected to Roles, rather than concrete users. Every Identity can have multiple roles, which inherits
all permissions from their child roles.
<?php
use Sylius\Component\Rbac\Model\Role;
use Sylius\Component\Rbac\Model\Permission;

// Assume that in your application you want to have two roles and some permissions to manage customer
// Let's start with creating root permission.
$manageCustomer = new Permission();
$manageCustomer->setCode('sylius.manage.customer');
$manageCustomer->setDescription('Manage customers');
// Next create more specific permissions.
$deleteCustomer = new Permission();
$deleteCustomer->setCode('sylius.delete.customer');
$deleteCustomer->setDescription('Delete customer');
$createCustomer = new Permission();
$createCustomer->setCode('sylius.create.customer');
$createCustomer->setDescription('Create customer');
// Now take care of permission inheritance.
$manageCustomer->addChild($deleteCustomer);
$manageCustomer->addChild($createCustomer);
//Great! Now we have Customer Manager permission which inherits above permissions.
// Roles are defined as tree structure as well, it is the same rule as with permissions.
// Let's start with our root role.
$administrator = new Role();
$administrator->setCode('administrator');
$administrator->setName('Administrator');
$customerManager = new Role();
$customerManager->setCode('customer_manager');
$customerManager->setName('Customer Manager');
// Take care of role inheritance.
$administrator->addChild($customerManager);
$customerManager->addPermission($manageCustomer);
// Now Administrator and Catalog Manager have permission to manage customer.

AuthorizationChecker

The AuthorizationChecker checks whether current identity has permission.


<?php
use Sylius\Component\Rbac\Authorization\AuthorizationChecker;
use Sylius\Component\Rbac\Provider\CurrentIdentityProviderInterface;

294

Chapter 8. Components

Sylius, Release

use
use
use
use
use

Sylius\Component\Resource\Repository\InMemoryRepository;
Sylius\Component\Rbac\Authorization\PermissionMap;
Sylius\Component\Rbac\Provider\PermissionProvider;
Sylius\Component\Rbac\Resolver\PermissionsResolver;
Sylius\Component\Rbac\Resolver\RolesResolver;

class CurrentIdentityProvider implements CurrentIdentityProviderInterface


{
/**
* Get the identity.
*
* @return IdentityInterface
*/
public function getIdentity()
{
// TODO: Implement getIdentity() method. It should return your identity object for example Em
}
}
$permissions = new InMemoryRepository();

$currentIdentityProvider = new CurrentIdentityProvider();


$permissionProvider = new PermissionProvider($permissions); // Retrieves your permission from storage
$permissionResolver = new PermissionsResolver(); // Retrieves permissions from given role.
$rolesResolver = new RolesResolver(); // Retrieves roles from given identity.
$permissionMap = new PermissionMap($permissionProvider, $permissionResolver); // Retrieves permission

$authChecker = new AuthorizationChecker($currentIdentityProvider, $permissionMap, $rolesResolver);


$authChecker->isGranted('sylius.manage.customer'); // It will check if current identity has permissio

PermissionMap

The PermissionMap allows you to get permissions from given role.


<?php
use
use
use
use
use
use

Sylius\Component\Resource\Repository\InMemoryRepository;
Sylius\Component\Rbac\Authorization\PermissionMap;
Sylius\Component\Rbac\Provider\PermissionProvider;
Sylius\Component\Rbac\Resolver\PermissionsResolver;
Sylius\Component\Rbac\Model\Role;
Sylius\Component\Rbac\Model\Permission;

$manageCustomer = new Permission();


$manageCustomer->setCode('sylius.manage.customer');
$manageCustomer->setDescription('Manage customers');
$customerManager = new Role();
$customerManager->setCode('customer_manager');
$customerManager->setName('Customer Manager');
$customerManager->addPermission($manageCustomer);
$permissions = new InMemoryRepository();

$permissionProvider = new PermissionProvider($permissions); // Retrieves your permission from storage


$permissionResolver = new PermissionsResolver(); // Retrieves permissions from given role.

8.1. PHP E-Commerce Components

295

Sylius, Release

$permissionMap = new PermissionMap($permissionProvider, $permissionResolver); // Retrieves permission


$permissionMap->getPermissions($customerManager); // Retrieves permissions for given role.
$permissionMap->hasPermission($customerManager, 'sylius.manage.customer'); // Output will be true.

Caution: This service can throw PermissionNotFoundException.

CachedPermissionMap

If you need to get faster access to permissions you can use cache system in your application.
PermissionProvider
<?php
use Sylius\Component\Rbac\Provider\PermissionProvider;
use Sylius\Component\Resource\Repository\InMemoryRepository;
$permissions = new InMemoryRepository();

$permissionProvider = new PermissionProvider($permissions);


$permissionProvider->getPermission('sylius.manage.customer'); // It returns permission by code from y

Resolvers and Iterators

Permissions and roles are in tree model so basically resolvers and iterators have implemented logic to fetch leafs of
given permission or role.
<?php
use
use
use
use
use
use
use

Sylius\Component\Resource\Repository\InMemoryRepository;
Sylius\Component\Rbac\Model\Role;
Sylius\Component\Rbac\Model\Permission;
Sylius\Component\Rbac\Resolver\NestedSetPermissionsResolver;
Sylius\Component\Rbac\Resolver\NestedSetRolesResolver;
Sylius\Component\Rbac\Model\IdentityInterface;
Sylius\Component\Rbac\Model\RoleInterface;

class User implements IdentityInterface


{
/**
* Get roles.
*
* @return RoleInterface[]
*/
public function getAuthorizationRoles()
{
// TODO: Implement getAuthorizationRoles() method.
}
}
$permissions = // Implementation of PermissionRepositoryInterface.

296

Chapter 8. Components

Sylius, Release

$nestedSetPermissionsResolver = new NestedSetPermissionsResolver($permissions);


$nestedSetRolesResolver = new NestedSetRolesResolver($permissions);
$user = new User();
$manageCustomer = new Permission();
$manageCustomer->setCode('sylius.manage.customer');
$manageCustomer->setDescription('Manage customers');
$deleteCustomer = new Permission();
$deleteCustomer->setCode('sylius.delete.customer');
$deleteCustomer->setDescription('Delete customer');
$createCustomer = new Permission();
$createCustomer->setCode('sylius.create.customer');
$createCustomer->setDescription('Create customer');
$manageCustomer->addChild($deleteCustomer);
$manageCustomer->addChild($createCustomer);
$manageCustomer->getChildren();
$administrator = new Role();
$administrator->setCode('administrator');
$administrator->setName('Administrator');
$customerManager = new Role();
$customerManager->setCode('customer_manager');
$customerManager->setName('Customer Manager');
$administrator->addChild($customerManager);
$administrator->addPermission($manageCustomer);

$nestedSetPermissionsResolver->getPermissions($administrator); // Output will be {$manageCustomer, $d


$nestedSetRolesResolver->getRoles($user); // Output will be {$administrator, $customerManager}

Note: For more detailed information go to Sylius API Resolvers.

Models
Permission

The Permission object represents an permission. Permissions are defined as a tree, top level tree has all the permissions defined in the lower nodes. Permissions have the following properties:

8.1. PHP E-Commerce Components

297

Sylius, Release

Property
id
code
description
parent
children
left
right
level
createdAt
updatedAt

Description
Unique id of the Permission
Unique code of Permission (e.g. sylius.customer.manage, sylius.channel.show)
(e.g. Manage customers, Show channel)
Reference to parent Permission
Collection of children Permissions
Reference to left leaf
Reference to right leaf
Tree level
Date when Permission was created
Date of last change

Note: This model implements the PermissionInterface. For more detailed information go to Sylius API Permission.

Role

The Role object represents an role. Every Identity can have multiple roles, which inherit all permissions from their
child roles. If you want to have Roles in your model, just implement IdentityInterface.
Property
id
code
name
description
parent
children
left
right
level
permissions
securityRoles
createdAt
updatedAt

Description
Unique id of the Role
Code of Role
Role name (e.g. Administrator, Catalog Manager, Shipping Manager)
(e.g. Administrator user, Shipping Department)
Reference to parent Role
Collection of children Roles
Reference to left leaf
Reference to right leaf
Tree level
Collection of Permissions
Collection of security roles (e.g. {ROLE_ADMINISTRATION_ACCESS})
Date when Role was created
Date of last change

Note: This model implements the RoleInterface. For more detailed information go to Sylius API Role.
Hint: For example implementation of tree model you can use Doctrine extension.

Interfaces
Model Interfaces

IdentityInterface This interface should be implemented by model representing a single Authorization Identity,
which has certain Roles assigned.
Note: For more detailed information go to Sylius API IdentityInterface.

PermissionInterface This interface should be implemented by model representing a single Permission.


Note: For more detailed information go to Sylius API PermissionInterface.
298

Chapter 8. Components

Sylius, Release

RoleInterface This interface should be implemented by model representing a single Role.


Note: For more detailed information go to Sylius API RoleInterface.

Service Interfaces

CurrentIdentityProviderInterface Service implementing this interface should return an instance of currently used
identity.
Note: For more detailed information go to Sylius API CurrentIdentityProviderInterface.

PermissionProviderInterface Service implementing this interface should return an instance of permission by given
code.
Note: For more detailed information go to Sylius API PermissionProviderInterface.

AuthorizationCheckerInterface Service implementing this interface should check whether current identity has specific permission.
Note: For more detailed information go to Sylius API AuthorizationCheckerInterface.

PermissionMapInterface Service implementing this interface should return permissions by given code.
Note: For more detailed information go to Sylius API PermissionMapInterface.

PermissionRepositoryInterface In order to decouple from storage that provides permissions, you should create
repository class which implements this interface.
Note: For more detailed information go to Sylius API PermissionRepositoryInterface.

RoleRepositoryInterface In order to decouple from storage that provides roles, you should create repository class
which implements this interface.
Note: For more detailed information go to Sylius API RoleRepositoryInterface.

PermissionsResolverInterface Service implementing this interface should return permissions with all their child
permissions.
Note: For more detailed information go to Sylius API PermissionsResolverInterface.

8.1. PHP E-Commerce Components

299

Sylius, Release

RolesResolverInterface Service implementing this interface should return roles with their child roles.
Note: For more detailed information go to Sylius API RolesResolverInterface.

8.1.20 Registry
Simple registry component useful for all types of applications.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius\registry on Packagist);
Use the official Git repository (https://github.com/Sylius/Registry).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Basic Usage
A registry object acts as a collection of objects. The sylius ServiceRegistry allows you to store objects which implement a specific interface.
ServiceRegistry

To create a new ServiceRegistry you need to determine what kind of interface should be kept inside.
For the sake of examples lets use the FeeCalculatorInterface from the component_payment_index component.
<?php
use Sylius\Component\Registry\ServiceRegistry;
$registry = new ServiceRegistry('Sylius\Component\Payment\Calculator\FeeCalculatorInterface');

Once youve done that you can manage any object with the corresponding interface. So for starters, lets add some
services:
<?php
use Sylius\Component\Payment\Calculator\FixedFeeCalculator;
use Sylius\Component\Payment\Calculator\PercentFeeCalculator;
$registry->register('fixed', new FixedFeeCalculator());
$registry->register('percent', new PercentFeeCalculator());

Hint: The first parameter of register is incredibly important, as we will use it for all further operations. Also its
the key at which our service is stored in the array returned by all method.
After specifying the interface and inserting services, we can manage them:

300

Chapter 8. Components

Sylius, Release

<?php
$registry->has('fixed'); // returns true
$registry->get('fixed'); // returns the FixedFeeCalculator we inserted earlier on
$registry->all(); // returns an array containing both calculators

Removing a service from the registry is as easy as adding:


<?php
$registry->unregister('fixed');
$registry->has('fixed'); // now returns false

Note: This service implements the ServiceRegistryInterface.


Caution: This service throws:
\InvalidArgumentException when you try to register a service which doesnt implement the specified
interface
ExistingServiceException
NonExistingServiceException

Interfaces
ServiceRegistryInterface

This interface should be implemented by a service responsible for managing various services.
Note: For more detailed information go to Sylius API ServiceRegistryInterface.

Exceptions
ExistingServiceException

This exception is thrown when you try to register a service that is already in the registry.
Note: This exception extends the \InvalidArgumentException.

NonExistingServiceException

This exception is thrown when you try to unregister a service which is not in the registry.
Note: This exception extends the \InvalidArgumentException.

8.1. PHP E-Commerce Components

301

Sylius, Release

8.1.21 Report
Super-flexible report system with posibility of custom report creation, based on already created data fetchers and
renderers.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius\report on Packagist);
Use the official Git repository (https://github.com/Sylius/Report).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Basic Usage
Note: First of all you need to register your data fetcher and renderer. For more detailed information go to
:ref:Registry_.

DelegatingDataFetcher

This service allows you to delegate fetching to your custom registered service. Your data fetcher class should implement DataFetcherInterface.
<?php
use Sylius\Component\Report\DataFetcher\DataFetcherInterface;
/**
* Sales total data fetcher
*/
class SalesTotalDataFetcher implements DataFetcherInterface
{
const TYPE = 'sales_total';
/**
* {@inheritdoc}
*/
public function fetch(array $configuration)
{
// TODO: Implement fetch() method.
}
/**
* {@inheritdoc}
*/
public function getType()
{
return self::TYPE;
}
}

302

Chapter 8. Components

Sylius, Release

<?php
use
use
use
use
use
use
use
use

Sylius\Component\Report\DataFetcher\Data;
Sylius\Component\Report\DataFetcher\DefaultDataFetchers;
Sylius\Component\Report\Renderer\DefaultRenderers;
Sylius\Component\Report\DataFetcher\DelegatingDataFetcher;
Sylius\Component\Registry\ServiceRegistry;
Sylius\Component\Report\Model\Report;
Sylius\Component\Report\Renderer\RendererInterface;
Sylius\Component\Report\Model\ReportInterface;

$salesTotalDataFetcher = new SalesTotalDataFetcher();


$report = new Report();
$report->setDataFetcher(SalesTotalDataFetcher::TYPE);
$report->setRenderer(TableRenderer::TYPE);
// Let's register our data fetcher.
$serviceRegistry = new ServiceRegistry(DataFetcherInterface::class);
$serviceRegistry->register(SalesTotalDataFetcher::TYPE, $salesTotalDataFetcher);
$delegatingDataFetcher = new DelegatingDataFetcher($serviceRegistry);
$delegatingDataFetcher->fetch($report); // Output depends on implementation of your data fetcher.

DelegatingRenderer

This service allows you to delegate rendering to your custom registered service. Your renderer should implement
RendererInterface.
<?php
use Sylius\Component\Report\Renderer\RendererInterface;
class TableRenderer implements RendererInterface
{
const TYPE = 'table';
/**
* {@inheritdoc}
*/
public function render(ReportInterface $report, Data $data)
{
// TODO: Implement render() method.
}
/**
* {@inheritdoc}
*/
public function getType()
{
return self::TYPE;
}
}

8.1. PHP E-Commerce Components

303

Sylius, Release

<?php
use
use
use
use
use
use
use

Sylius\Component\Report\DataFetcher\Data;
Sylius\Component\Report\Renderer\DelegatingRenderer;
Sylius\Component\Report\DataFetcher\DefaultDataFetchers;
Sylius\Component\Report\Renderer\DefaultRenderers;
Sylius\Component\Registry\ServiceRegistry;
Sylius\Component\Report\Model\Report;
Sylius\Component\Report\Model\ReportInterface;

$tableRenderer = new TableRenderer();


$report = new Report();
$report->setDataFetcher(DefaultDataFetchers::SALES_TOTAL);
$report->setRenderer(DefaultRenderers::TABLE);
$data = new Data(); // Your data fetched from data fetcher.
// Let's register our table renderer.
$serviceRegistry = new ServiceRegistry(RendererInterface::class);
$serviceRegistry->register(TableRenderer::TYPE, $tableRenderer);
$delegatingRenderer = new DelegatingRenderer($serviceRegistry);
$delegatingRenderer->render($report, $data); // Output depends on implementation of your renderer.

Models
Report

Report is the main model in SyliusReportComponent. This simple class represents every unique report in the system.
The default model contains the following attributes with appropriate setters and getters.
Attribute
id
code
name
description
renderer
rendererConfiguration
dataFetcher
dataFetcherConfiguration

Description
Unique id of the report
Unique code of the report
Name of the report
Description of your report
Name of the renderer that visualize report data
Configuration of used renderer
Name of the dataFetcher that provides report data
Configuration of used data fetcher

Note: This model implements the ReportInterface For more detailed information go to Sylius API Report.

Data

Data model represents report data in a uniform form.


Attribute
labels
data

Description
Array of labels that describe data
Array of values with data

Note: For more detailed information go to Sylius API Data.

304

Chapter 8. Components

Sylius, Release

Interfaces
Model Interfaces

ReportInterface This interface should be implemented by model representing a single Report.


Note: For more detailed information go to Sylius API ReportInterface.

Service Interfaces

DataFetcherInterface This interface should be implemented by service representing data fetcher. Responsible for
providing specific data for a report, based on its configuration.
Note: For more detailed information go to Sylius API DataFetcherInterface.

RendererInterface This interface should be implemented by service representing data renderer, which renders the
data in a specific output format. Examples would be graph, table, csv.
Note: For more detailed information go to Sylius API RendererInterface.

DelegatingDataFetcherInterface This interface should be implemented by service responsible for delegating data
fetching.
Note: For more detailed information go to Sylius API DelegatingDataFetcherInterface.

DelegatingRendererInterface This interface should be implemented by service responsible for delegating data rendering.
Note: For more detailed information go to Sylius API DelegatingRendererInterface.

8.1.22 Resource
Domain management abstraction for PHP. It provides interface for most common operations on the application resources.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use following command to add the
component to your composer.json and download package.
If you have Composer installed globally.
$ composer require sylius/resource:*

Otherwise you have to download .phar file.

8.1. PHP E-Commerce Components

305

Sylius, Release

$ curl -sS https://getcomposer.org/installer | php


$ php composer.phar require sylius/resource:*

Model interfaces
SoftDeletableInterface

This interface will ask you to implement the following methods to your model, they will use by the soft deletable
Doctrine2 extension. :
Method
isDeleted()
getDeletedAt()
setDeletedAt(DateTime $deletedAt)

Description
Check if the resource has been deleted
Get the time of deletion
Cet deletion time.

Returned value
boolean
DateTime
Void

TimestampableInterface

This interface will ask you to implement the following methods to your model, they will use by the timestampable
Doctrine2 extension. :
Method
getCreatedAt()
getUpdatedAt()
setCreatedAt(DateTime $createdAt)
setUpdatedAt(DateTime $updatedAt)

Description
Get creation time
Get the time of last update
Set creation time
Set the time of last update

Returned value
DateTime
DateTime
Void
Void

Repository interfaces
RepositoryInterface

This interface will ask you to implement two methods to your repositories:
Method

Description

createNew()

Create a new instance of your


resource
Get paginated collection of your
resources

createPaginator(array $criteria = null, array $orderBy


= null)

Returned
value
mixed
mixed

Summary
phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This component uses GitHub issues. If you have found bug, please create an issue.

306

Chapter 8. Components

Sylius, Release

8.1.23 Sequence
Component for generating number/hash sequences in PHP.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius/sequence on Packagist);
Use the official Git repository (https://github.com/Sylius/Sequence).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Basic Usage
In order to benefit from the components features at first you need to create a basic class that will implement the
SequenceSubjectInterface. Lets assume that you would like to generate identifiers for orders in your system. Your
Order class therefore will implement this interface to have and ability to be a subject of sequence automatic generation
by your services.
SequenceSubjectInterface

Lets see how an exemplary class implementing SequenceSubjectInterface should look like.
<?php
namespace AppBundle\Entity\Order;
use Sylius\Component\Sequence\Model\SequenceSubjectInterface;
class Order implements SequenceSubjectInterface
{
const SEQUENCE_TYPE = 'order';
/**
* @var string
*/
private $number;
/**
* {@inheritdoc}
*/
public function getSequenceType()
{
return self::SEQUENCE_TYPE;
}
/**
* {@inheritdoc}
*/
public function getNumber()
{
return $this->number;
}

8.1. PHP E-Commerce Components

307

Sylius, Release

/**
* {@inheritdoc}
*/
public function setNumber($number)
{
$this->number = $number;
}
}

Lets now see how we can use an exemplary generator:


<?php
use Sylius\Component\Sequence\Model\Sequence;
use Sylius\Component\Sequence\Number\SequentialGenerator;
use AppBundle\Entity\Order;
// Prepare an object for the sequence you are going to generate, that will store its type.
$sequence = new Sequence('order');
$subject = new Order();
$anotherSubject = new Order();
// Instantiate the generator that will generate sequences of length 4 starting from 0077.
$generator = new SequentialGenerator(4, 77);
$generator->generate($subject, $sequence);
$generator->generate($anotherSubject, $sequence);
$subject->getNumber();
// returns '0077'
$anotherSubject->getNumber(); // returns '0078'

Hint: You can read more about each of the available generators in the Generators chapter.

Models
Sequence

Sequence is a generated set of numbers and/or letters stored on a model. They may be useful for example as order or
customer identifiers.
Property
id
index
type

Description
Unique id of the sequence
The sequences index
The sequences type

Note: This model implements the SequenceInterface. For more detailed information go to Sylius API Sequence.

Interfaces
Model Interfaces

SequenceInterface This interface should be implemented by models representing a Sequence.

308

Chapter 8. Components

Sylius, Release

Note: You will find more information about this interface in Sylius API SequenceInterface.

SequenceSubjectInterface To characterize an object with attributes and options from a sequence, the object class
needs to implement the SequenceSubjectInterface.
Note: You will find more information about this interface in Sylius API SequenceSubjectInterface.

Service Interfaces

GeneratorInterface This interface gives a possibility to generate and apply next available number for a given subject.
Note: You will find more information about this interface in Sylius API GeneratorInterface.

HashSubjectRepositoryInterface Repository interface for model which needs number uniqueness check before
applying. It provides a method isNumberUsed().
Note: You will find more information about this interface in Sylius API HashSubjectRepositoryInterface.

Generators
AbstractGenerator

A custom generator model should extend this class in order to generate sequences on a given object that implements
the SequenceSubjectInterface.
Note: This class implements the GeneratorInterface. For more detailed information go to Sylius API AbstractGenerator.

SequentialGenerator

This class is a default order number generator.


Property
numberLength
startNumber

Description
Order number max length
The sequence start number

Below you can see a snippet on how to use it:


<?php
// Instantiate the generator that will generate sequences of length 4 starting from 0077.
$generator = new SequentialGenerator(4, 77);
// And use it on subjects that implement the SequenceSubjectInterface
$generator->generate($subject, $sequence); // generates '0077'
$generator->generate($anotherSubject, $sequence); // generates '0078'

8.1. PHP E-Commerce Components

309

Sylius, Release

Note: This generator implements the GeneratorInterface and extends the AbstractGenerator. For more detailed
information go to Sylius API SequentialGenerator.

HashGenerator

This class generates hash numbers similar to the Amazon order identifiers (e.g. 105-3958356-3707476) and also
random hashed segments of a given length.
Below you can see a snippet on how to use it:
<?php
$subject = new Order();
$repository = new InMemoryRepository();
// Instantiate the generator that will generate a 3 by 7 by 7 digits number.
$generator = new HashGenerator($repository);
$index = /** index of the sequence **/
$generator->generateNumber($index, $subject);
$subject->getNumber(); // returns randomized sequence of format 'xxx-xxxxxxx-xxxxxxx'

Note: This generator implements the GeneratorInterface and extends the AbstractGenerator. For more detailed
information go to Sylius API HashGenerator.

GeneratorRegistry

It returns the generator used for a given entity.


Note: This service extends the ServiceRegistry. For more detailed information go to Sylius API GeneratorRegistry.
Caution: Throws NonExistingServiceException.

NonExistingGeneratorException

This exception is thrown when your are trying to get a generator that does not exist in your system.
Note: This exception extends the \InvalidArgumentException.

8.1.24 Shipping
Shipments and shipping methods management for PHP E-Commerce apps. It contains flexible calculators system for
computing the shipping costs.

310

Chapter 8. Components

Sylius, Release

Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use following command to add the
component to your composer.json and download package.
If you have Composer installed globally.
$ composer require sylius/shipping:*

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require sylius/shipping:*

Summary
phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This component uses GitHub issues. If you have found bug, please create an issue.

8.1.25 Taxation
Tax rates and tax classification for PHP apps. You can define different tax categories and match them to objects.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use following command to add the
component to your composer.json and download package.
If you have Composer installed globally.
$ composer require sylius/taxation:*

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require sylius/taxation:*

Models
The Tax rate

Tax rate model holds the configuration for particular tax rate.

8.1. PHP E-Commerce Components

311

Sylius, Release

Attribute
id
category
name
amount
includedInPrice
calculator
createdAt
updatedAt

Description
Unique id of the tax rate
Tax rate category
Name of the rate
Amount as float (for example 0,23)
Is the tax included in price?
Type of calculator
Date when the rate was created
Date of the last tax rate update

Note: This model implements TaxRateInterface

The Tax category

Tax category model holds the configuration for particular tax category.
Attribute
id
name
description
rates
createdAt
updatedAt

Description
Unique id of the tax category
Name of the category
Description of tax category
Collection of tax rates belonging to this tax category
Date when the category was created
Date of the last tax category update

Note: This model implements TaxCategoryInterface

Summary
phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This component uses GitHub issues. If you have found bug, please create an issue.

8.1.26 Taxonomy
Basic taxonomies library for any PHP application.
Installation
We assume youre familiar with Composer, a dependency manager for PHP. Use following command to add the
component to your composer.json and download package.
If you have Composer installed globally.
$ composer require sylius/taxonomy:*

312

Chapter 8. Components

Sylius, Release

Otherwise you have to download .phar file.


$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require sylius/taxonomy:*

Models
Taxonomy is a list constructed from individual Taxons. Every taxonomy has one special taxon, which serves as a root
of the tree. All taxons can have many child taxons, you can define as many of them as you need.
A good examples of taxonomies are Categories and Brands. Below you can see an example tree.
| Categories
|-- T-Shirts
|
|-- Men
|
`-- Women
|-- Stickers
|-- Mugs
`-- Books
| Brands
|-- SuperTees
|-- Stickypicky
|-- Mugland
`-- Bookmania

Taxonomy

Attribute
id
name
root
createdAt
updatedAt

Description
Unique id of the taxonomy
Name of the taxonomy
First, root Taxon
Date when taxonomy was created
Date of last update

Type
mixed
string
TaxonInterface
DateTime
DateTime

This model implements TaxonomyInterface, it implements these extra methods:


Method
getTaxons()
hasTaxon(TaxonInterface $taxon)
addTaxon(TaxonInterface $taxon)
removeTaxon(TaxonInterface $taxon)

Description
Get all taxons
Check if the taxonomy has taxon
Add a taxon
Remove a taxon

8.1. PHP E-Commerce Components

Returned value
TaxonInterface[]
boolean
Void
Void

313

Sylius, Release

Taxons

Attribute
id
name
slug
permalink
description
taxonomy
parent
children
left
right
level
createdAt
updatedAt

Description
Unique id of the taxon
Name of the taxon
Urlized name
Full permalink for given taxon
Description of taxon
Taxonomy
Parent taxon
Sub taxons
Location within taxonomy
Location within taxonomy
How deep it is in the tree
Date when taxon was created
Date of last update

Type
mixed
string
string
string
string
TaxonomyInterface
TaxonInterface
Collection
mixed
mixed
mixed
DateTime
DateTime

This model implements TaxonInterface, it implements these extra methods:


Method
hasChild()
addChild(TaxonInterface $taxon)
removeChild(TaxonInterface $taxon)

Description
Check whether the taxon has a child
Add child taxon
Remove child taxon.

Returned value
boolean
Void
Void

TaxonsAwareInterface

This interface should be implemented by models that support taxons.


Method
getTaxons($taxonomy = null)
setTaxons(Collection $collection)
hasTaxon(TaxonInterface $taxon)
addTaxon(TaxonInterface $taxon)
removeTaxon(TaxonInterface $taxon)

Description
Get all taxons
Set the taxons
Checks whether object has taxon
Add a taxon
Remove a taxon

Returned value
TaxonInterface[]
Void
Boolean
TaxonomyInterface
TaxonomyInterface

Summary
phpspec2 examples
$ composer install --dev --prefer-dist
$ bin/phpspec run -fpretty --verbose

Bug tracking

This component uses GitHub issues. If you have found bug, please create an issue.

8.1.27 Translation
Translations management for PHP E-Commerce applications.

314

Chapter 8. Components

Sylius, Release

Installation
You can install the component in 2 different ways:
Install it via Composer (sylius/translation on Packagist);
Use the official Git repository (https://github.com/Sylius/Translation).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Basic Usage
Implementing AbstractTranslation

First lets create a class which will keep our translatable properties:
<?php
namespace Example\Model;
use Sylius\Component\Translation\Model\AbstractTranslation;
class BookTranslation extends AbstractTranslation
{
/**
* @var string
*/
private $title;
/**
* @return string
*/
public getTitle()
{
return $this->title;
}
/**
* @param string $title
*/
public setTitle($title)
{
$this->title = $title;
}
}

Implementing AbstractTranslatable

Now the following class will be actually capable of translating the title:
<?php
namespace Example\Model;
use Sylius\Component\Translation\Model\AbstractTranslatable;

8.1. PHP E-Commerce Components

315

Sylius, Release

class Book extends AbstractTranslatable


{
/**
* @return string
*/
public getTitle()
{
return $this->translate()->getTitle();
}
/**
* @param string $title
*/
public setTitle($title)
{
$this->translate()->setTitle($title);
}
}

Note: As you could notice, inside both methods we use the translate method. More specified explanation on
what it does is described further on.

Using Translations

Once we have both abstract classes implemented we can start translating. So first we need to create a few instances of
our translation class:
<?php
use Example\Model\Book;
use Example\Model\BookTranslation;
$englishBook = new BookTranslation();
$englishBook->setLocale('en');
$englishBook->setTitle("Harry Potter and the Philosopher's Stone");
// now we have a title set for the english locale
$spanishBook = new BookTranslation();
$spanishBook->setLocale('es');
$spanishBook->setTitle('Harry Potter y la Piedra Filosofal');
// spanish
$germanBook = new BookTranslation();
$germanBook->setLocale('de');
$germanBook->setTitle('Harry Potter und der Stein der Weisen');
// and german

When we already have our translations, we can work with the Book:
<?php
$harryPotter = new Book();
$harryPotter->addTranslation($englishBook);
$harryPotter->addTranslation($spanishBook);
$harryPotter->addTranslation($germanBook);

316

Chapter 8. Components

Sylius, Release

$harryPotter->setFallbackLocale('en'); // the locale which translation should be used by default


$harryPotter->setCurrentLocale('es'); // the locale which translation we want to get
$harryPotter->getTitle(); // returns 'Harry Potter y la Piedra Filosofal'
$harryPotter->setCurrentLocale('ru');
$harryPotter->getTitle(); // now returns "Harry Potter and the Philosopher's Stone"
// as the translation for chosen locale is unavailable,
// instead the translation for fallback locale is used

You can always use the translate method by itself, but the same principal is in play:
<?php
$harryPotter->translate('de');
// but
$harryPotter->translate();
// and
$harryPotter->translate('hi');
// both return $englishBook

// returns $germanBook

Caution: The translate method throws \RuntimeException in two cases:


No locale has been specified in the parameter and the current locale is undefined
No fallback locale has been set

LocaleProvider

This service provides you with an easy way of managing locales. The first parameter set in its constructor is the
current locale and the second, fallback.
In this example lets use the provider with our Book class which extends the AbstractTranslatable:
<?php
use Example\Model\Book;
use Sylius\Component\Translation\Provider\LocaleProvider;
$provider = new LocaleProvider('de', 'en');
$book = new Book();
$book->setCurrentLocale($provider->getCurrentLocale());
$book->setFallbackLocale($provider->getFallbackLocale());
$book->getCurrentLocale(); // returns 'de'
$book->getFallbackLocale(); // returns 'en'

... and with an AbstractTranslation class such as the exemplary BookTranslation it goes:
<?php
use Example\Model\BookTranslation;
use Sylius\Component\Translation\Provider\LocaleProvider;

8.1. PHP E-Commerce Components

317

Sylius, Release

$provider = new LocaleProvider('de', 'en');


$bookTranslation = new BookTranslation();
$bookTranslation->setLocale($provider->getCurrentLocale());
$translation->getLocale(); // returns 'de'

Note: This service implements the LocaleProviderInterface.

Models
AbstractTranslatable

This class should be extended by any model which needs different presentations of its fields in various locales.
Property
translations
currentLocale
currentTranslation
fallbackLocale

Description
List of objects implementing the AbstractTranslation class
Currently set locale
Translation chosen from translations list accordingly to current locale
Locale used in case no translation is available for the current one

Note: This model implements the TranslatableInterface. For more detailed information go to Sylius API AbstractTranslatable.

AbstractTranslation

This class should be extended by a model responsible of maintaining a single translation for provided locale.
Property
locale
translatable

Description
Translations locale
The translatable model associated with this translation

Note: This model implements the TranslationInterface. For more detailed information go to Sylius API AbstractTranslation.

Interfaces
Model Interfaces

TranslatableInterface This interface should be implemented by a model used in more than one language.
Hint: Although you can implement this interface in your class, its easier to just extend the AbstractTranslatable
class.
Note: For more detailed information go to Sylius API TranslatableInterface.

318

Chapter 8. Components

Sylius, Release

TranslationInterface This interface should be implemented by a model responsible for keeping a single translation.
Hint: And as above, although you are completely free to create your own class implementing this interface, its
already implemented in the AbstractTranslation class.
Note: For more detailed information go to Sylius API TranslationInterface.

Service Interfaces

LocaleProviderInterface This interface should be implemented by a service responsible for managing locales.
Note: For more detailed information go to Sylius API LocaleProviderInterface.

TranslatableResourceRepositoryInterface This interface should be implemented by a repository responsible for


keeping the LocaleProvider and an array of fields
This interface expects you to implement a way of setting an instance of LocaleProviderInterface, and an array of
translatable fields into your custom repository.
Note: This interface extends the RepositoryInterface. For more detailed information go to Sylius API TranslatableResourceRepositoryInterface.

8.1.28 User
Users management implementation in PHP.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius/user on Packagist);
Use the official Git repository (https://github.com/Sylius/User).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.
Models
Customer

The customer is represented as a Customer instance. It should have everything concerning personal data and as default
has the following properties:

8.1. PHP E-Commerce Components

319

Sylius, Release

Property
id
email
emailCanonical
firstName
lastName
birthday
gender
user
groups
createdAt
updatedAt
deletedAt

Description
Unique id of the customer
Customers email
Normalized representation of an email (lowercase)
Customers first name
Customers last name
Customers birthday
Customers gender
Corresponding user object
Customers groups
Date of creation
Date of update
Date of deletion

Type
integer
string
string
string
string
DateTime
string
UserInterface
Collection
DateTime
DateTime
DateTime

Note: This model implements CustomerInterface and GroupableInterface

User

The registered user is represented as an User instance. It should have everything concerning application user preferences and a corresponding Customer instance. As default has the following properties:
Property
id
customer
username
usernameCanonical
enabled
salt
password
plainPassword
lastLogin
confirmationToken
passwordRequestedAt
locked
expiresAt
credentialExpiresAt
roles
oauthAccounts
createdAt
updatedAt
deletedAt

Description
Unique id of the user
Customer which is associated to this user (required)
Users username
Normalized representation of a username (lowercase)
Indicates whether user is enabled
Additional input to a function that hashes a password
Encrypted password, must be persisted
Password before encryption, must not be persisted
Last login date
Random string used to verify user
Date of password request
Indicates whether user is locked
Date when user account will expire
Date when user account credentials will expire
Security roles of a user
Associated OAuth accounts
Date of creation
Date of update
Date of deletion

Type
integer
CustomerInterface
string
string
bool
string
string
string
DateTime
string
DateTime
bool
DateTime
DateTime
array
Collection
DateTime
DateTime
DateTime

Note: This model implements UserInterface

Group

The customer group is represented as a Group instance. It can be used to classify customers. As default has the
following properties:
Property
id
name

320

Description
Unique id of the group
Group name

Type
integer
string

Chapter 8. Components

Sylius, Release

Note: This model implements GroupInterface

UserOAuth

The user OAuth account is represented as an UserOAuth instance. It has all data concerning OAuth account and as
default has the following properties:
Property
id
provider
identifier
accessToken
user

Description
Unique id of the customer
OAuth provider name
OAuth identifier
OAuth access token
Corresponding user account

Type
integer
string
string
string
UserInterface

Note: This model implements UserOAuthInterface

Basic Usage
Canonicalization

In order to be able to query or sort by some string, we should normalize it. The most common use case for that is
canonical email or username. We can then allow for case insensitive users identification by email or username.
Canonicalizer User component offers simple canonicalizer which converts given string to lowercase letters. Example usage:
// File example: src/script.php
<?php
// update this to the path to the "vendor/"
// directory, relative to this file
require_once __DIR__.'/../vendor/autoload.php';
use Sylius\Component\User\Model\Customer;
use Sylius\Component\Canonicalizer\Canonicalizer;
$canonicalizer = new Canonicalizer();
$customer = new Customer();
$customer->setEmail('MyEmail@eXample.Com');
$canonicalEmail = $canonicalizer->canonicalize($customer->getEmail());
$customer->setEmailCanonical($canonicalEmail);
$customer->getEmail() // returns 'MyEmail@eXample.Com'
$customer->getEmailCanonical() // returns 'myemail@example.com'

Updating password

In order to store users password safely you need to encode it and get rid of the plain password.

8.1. PHP E-Commerce Components

321

Sylius, Release

PasswordUpdater User component offers simple password updater and encoder. All you need to do is set the plain
password on User entity and use updatePassword method on PasswordUpdater. The plain password will be removed
and the encoded password will be set on User entity. Now you can safely store the encoded password. Example usage:
// File example: src/script.php
<?php
// update this to the path to the "vendor/"
// directory, relative to this file
require_once __DIR__.'/../vendor/autoload.php';
use Sylius\Component\User\Model\User;
use Sylius\Component\User\Security\PasswordUpdater;
use Sylius\Component\User\Security\UserPbkdf2PasswordEncoder;
$user = new User();
$user->setPlainPassword('secretPassword');
$user->getPlainPassword(); // returns 'secretPassword'
$user->getPassword(); // returns null
// after you set user's password you need to encode it and get rid of unsafe plain text
$passwordUpdater = new PasswordUpdater(new UserPbkdf2PasswordEncoder());
$passwordUpdater->updatePassword($user);
// the plain password no longer exist
$user->getPlainPassword(); // returns null
// encoded password can be safely stored
$user->getPassword(); //returns 'notPredictableBecauseOfSaltHashedPassword'

Note: The password encoder takes users salt (random, autogenerated string in the User constructor) as an additional
input to a one-way function that hashes a password. The primary function of salts is to defend against dictionary
attacks versus a list of password hashes and against pre-computed rainbow table attacks.

8.1.29 Variation
Library for managing object variants and options. This functionality can be attached to any object to create different
configurations.
Installation
You can install the component in 2 different ways:
Install it via Composer (sylius\variation on Packagist);
Use the official Git repository (https://github.com/Sylius/Variation).
Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application wont be able to find the classes of this Sylius component.

322

Chapter 8. Components

Sylius, Release

Basic Usage
VariableInterface

Lets see how an exemplary class implementing the VariableInterface should look like.
<?php
use
use
use
use
use

Sylius\Component\Variation\Model\VariableInterface;
Doctrine\Common\Collections\Collection;
Doctrine\Common\Collections\ArrayCollection;
Sylius\Component\Variation\Model\VariantInterface;
Sylius\Component\Variation\Model\OptionInterface;

class Clothing implements VariableInterface


{
/**
* @var string
*/
private $name;
/**
* @var Collection
*/
private $variants;
/**
* @var Collection
*/
private $options;
/**
* @var VariantInterface
*/
private $master;
/**
* @param string $name
*/
public function __construct($name)
{
$this->name = $name;
$this->variants = new ArrayCollection();
$this->options = new ArrayCollection();
}
/**
* {@inheritdoc}
*/
public function getMasterVariant()
{
return $this->master;
}
/**
* {@inheritdoc}
*/
public function setMasterVariant(VariantInterface $variant)

8.1. PHP E-Commerce Components

323

Sylius, Release

{
$this->master = $variant;
}
/**
* {@inheritdoc}
*/
public function hasVariants()
{
if ($this->variants->isEmpty()) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
public function getVariants()
{
return $this->variants;
}
/**
* {@inheritdoc}
*/
public function setVariants(Collection $variants)
{
$this->variants = $variants;
}
/**
* {@inheritdoc}
*/
public function addVariant(VariantInterface $variant)
{
$this->variants->add($variant);
}
/**
* {@inheritdoc}
*/
public function removeVariant(VariantInterface $variant)
{
$this->variants->removeElement($variant);
}
/**
* {@inheritdoc}
*/
public function hasVariant(VariantInterface $variant)
{
if ($this->variants->contains($variant)) {
return true;
}
return false;

324

Chapter 8. Components

Sylius, Release

}
/**
* {@inheritdoc}
*/
public function hasOptions()
{
if ($this->options->isEmpty()) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
public function getOptions()
{
return $this->options;
}
/**
* {@inheritdoc}
*/
public function setOptions(Collection $options)
{
$this->options = $options;
}
/**
* {@inheritdoc}
*/
public function addOption(OptionInterface $option)
{
$this->options->add($option);
}
/**
* {@inheritdoc}
*/
public function removeOption(OptionInterface $option)
{
$this->options->removeElement($option);
}
/**
* {@inheritdoc}
*/
public function hasOption(OptionInterface $option)
{
if ($this->options->contains($option)) {
return true;
}
return false;
}
}

8.1. PHP E-Commerce Components

325

Sylius, Release

VariantGenerator

A VariantGenerator is used to create all possible combinations of an objects options and to create Variant models
from them.
Example:
If an object such as a T-shirt has 2 options - Color and Size - with 3 possible values per option, then the generator will
create 9 variants and assign them to the object.
The generator will ignore invalid variants or variants that already exist.
T-Shirt Options
Colors
Black
White
Red

Size
Small
Medium
Large

Possible T-Shirt Variants


These variants should be generated by the VariantGenerator from the options above:
1. Black, Small
2. Black, Medium
3. Black, Large
4. White, Small
5. White, Medium
6. White, Large
7. Red, Small
8. Red, Medium
9. Red, Large
<?php
use
use
use
use

Sylius\Component\Variation\Generator\VariantGenerator;
Sylius\Component\Variation\Model\VariableInterface;
Sylius\Component\Variation\SetBuilder\CartesianSetBuilder;
Sylius\Component\Resource\Repository\InMemoryRepository;

$variantRepository = new InMemoryRepository();


$setBuilder = new CartesianSetBuilder();
$subject = new Clothing('T-Shirt');
$colors = new Option();
$colors->setValues(new ArrayCollection(array('White', 'Black', 'Red')));
$colors->setName('Color');
$sizes = new Option();
$sizes->setValues(new ArrayCollection(array('Small', 'Medium', 'Large')));
$sizes->setName('Size');
$variable->addOption($colors);
$variable->addOption($sizes);

326

Chapter 8. Components

Sylius, Release

$generator = new VariantGenerator($variantRepository, $setBuilder);


// Generate all possible variants if they don't exist yet.
$generator->generate($variable)

Note: The variant generator implements the VariantGeneratorInterface.


Note: The variant generators set builder should implement the SetBuilderInterface.

Models
Variant

Every variant is represented by Variant instance and has the following properties:
Property
id
master
presentation
object
options
createdAt
updatedAt

Description
Unique id of the variant
Defines whether variant is master
Name displayed to user
Related product
Option values
Date of creation
Date of the last update

Note: This model implements the VariantInterface. You will find more information about this interface in Sylius API
Variant.

Option

Every variant option is represented by Option instance and has the following properties:
Property
id
name
presentation
values
createdAt
updatedAt

Description
Unique id of the Option
Internal name
Name displayed to user
Option values
Date of creation
Date of the last update

Note: This model implements the OptionInterface. You will find more information about this interface in Sylius API
Option.

OptionTranslation

Every variant option has a corresponding translation stored as an OptionTranslation instance and has the following
properties:
Property
id
presentation

Description
Unique id of the translation
Translated option name

8.1. PHP E-Commerce Components

327

Sylius, Release

Note: This model implements the OptionTranslationInterface. You will find more information about this interface in
Sylius API OptionTranslation.

OptionValue

Every variant option value is represented by OptionValue instance and has the following properties:
Property
id
value
option

Description
Unique id of the OptionValue
Option internal value
An instance of Option

Note: This model implements the OptionValueInterface. You will find more information about this interface in Sylius
API OptionValue.

Interfaces
Model Interfaces

VariableInterface In order for the object class to manage variants and options it has to implement the
VariableInterface.
Note: You will find more information about this interface in Sylius API VariableInterface.

VariantInterface When an object class implements the VariantInterface it has a possibility to manage options.
Note: This interface extends the component_resource_model_softdeletable-interface and the TimestampableInterface. You will find more information about this interface in Sylius API VariantInterface.

OptionInterface In order for an object class to represent the option type it has to implement the
OptionInterface.
Note: This interface extends the TimestampableInterface and the OptionTranslationInterface. You will find more
information about this interface in Sylius API OptionInterface.

OptionTranslationInterface In order to store the translation of an Option an object class needs to imlement this
interface.
Note: You will find more information about this interface in Sylius API OptionTranslationInterface.

OptionValueInterface If you need to store a value of an Option you will have to create an object class that implements this interface.
Note: You will find more information about that interface in Sylius API OptionValueInterface.

328

Chapter 8. Components

Sylius, Release

Services Interfaces

SetBuilderInterface When you want a service to be able to Build a product set from one or more given sets it should
implement the SetBuilderInterface.
Note: You will find more information about that interface in Sylius API SetBuilderInterface.

VariantGeneratorInterface This interface is used to create all possible (non-existing) variations of a given object
based on its options.
Note: You will find more information about that interface in Sylius API VariantGeneratorInterface.
Addressing
Archetype
Attribute
Cart
Channel
Contact
Currency
Grid
Inventory
Locales
Mailer
Order
Originator
Payment
Pricing
Product
Promotion
RBAC
Registry
Report
Resource
Sequence
Shipping
Taxation
Taxonomy
Translation
User

8.1. PHP E-Commerce Components

329

Sylius, Release

Variation
Addressing
Archetype
Attribute
Cart
Channel
Contact
Currency
Grid
Inventory
Locales
Mailer
Order
Originator
Payment
Pricing
Product
Promotion
RBAC
Registry
Report
Resource
Sequence
Shipping
Taxation
Taxonomy
Translation
User
Variation

330

Chapter 8. Components

CHAPTER 9

Contributing

A guide to contribute to Sylius.

9.1 Contributing
Note: This section is based on the great Symfony2 documentation.

9.1.1 Contributing Code


Reporting a Bug
Whenever you find a bug in Sylius, we kindly ask you to report it. It helps us make a better e-commerce solution for
PHP.
Caution: If you think youve found a security issue, please use the special procedure instead.
Before submitting a bug:
Double-check the official documentation to see if youre not misusing the framework;
Ask for assistance on stackoverflow.com or on the #sylius IRC channel if youre not sure your issue is really a
bug.
If your problem definitely looks like a bug, report it using the official bug tracker and follow some basic rules:
Use the title field to clearly describe the issue;
Describe the steps needed to reproduce the bug with short code examples (providing a Behat scenario that
illustrates the bug is best);
Give as much detail as possible about your environment (OS, PHP version, Symfony version, Sylius version,
enabled extensions, ...);
(optional) Attach a patch.
Submitting a Patch
Patches are the best way to provide a bug fix or to propose enhancements to Sylius.

331

Sylius, Release

Step 1: Setup your Environment

Install the Software Stack Before working on Sylius, setup a Symfony2 friendly environment with the following
software:
Git;
PHP version 5.3.3 or above;
PHPUnit 3.6.4 or above;
MySQL.
Configure Git Set up your user information with your real name and a working email address:
$ git config --global user.name "Your Name"
$ git config --global user.email you@example.com

Tip: If you are new to Git, you are highly recommended to read the excellent and free ProGit book.
Tip: If your IDE creates configuration files inside the projects directory, you can use global .gitignore file (for
all projects) or .git/info/exclude file (per project) to ignore them. See Githubs documentation.
Tip: Windows users: when installing Git, the installer will ask what to do with line endings, and suggests replacing
all LF with CRLF. This is the wrong setting if you wish to contribute to Sylius. Selecting the as-is method is your
best choice, as Git will convert your line feeds to the ones in the repository. If you have already installed Git, you can
check the value of this setting by typing:
$ git config core.autocrlf

This will return either false, input or true; true and false being the wrong values. Change it to input by
typing:
$ git config --global core.autocrlf input

Replace global by local if you want to set it only for the active repository

Get the Sylius Source Code Get the Sylius source code:
Create a GitHub account and sign in;
Fork the Sylius repository (click on the Fork button);
After the forking action has completed, clone your fork locally (this will create a Sylius directory):
$ git clone git@github.com:USERNAME/Sylius.git

Add the upstream repository as a remote:


$ cd sylius
$ git remote add upstream git://github.com/Sylius/Sylius.git

Step 2: Work on your Patch

The License Before you start, you must know that all the patches you are going to submit must be released under
the MIT license, unless explicitly specified in your commits.
332

Chapter 9. Contributing

Sylius, Release

Create a Topic Branch Each time you want to work on a patch for a bug or on an enhancement, create a topic
branch:
$ git checkout -b BRANCH_NAME master

Tip: Use a descriptive name for your branch (issue_XXX where XXX is the GitHub issue number is a good
convention for bug fixes).
The above checkout commands automatically switch the code to the newly created branch (check the branch you are
working on with git branch).
Work on your Patch Work on the code as much as you want and commit as much as you want; but keep in mind
the following:
Practice BDD, which is the development methodology we use at Sylius;
Read about the Sylius conventions and follow the coding standards (use git diff --check to check for
trailing spaces also read the tip below);
Do atomic and logically separate commits (use the power of git rebase to have a clean and logical history);
Squash irrelevant commits that are just about fixing coding standards or fixing typos in your own code;
Never fix coding standards in some existing code as it makes the code review more difficult (submit CS fixes as
a separate patch);
Write good commit messages (see the tip below).
Tip: A good commit message is composed of a summary (the first line), optionally followed by a blank line and
a more detailed description. The summary should start with the Component you are working on in square brackets
([Cart], [Taxation], ...). Use a verb (fixed ..., added ..., ...) to start the summary and dont add a
period at the end.

Prepare your Patch for Submission When your patch is not about a bug fix (when you add a new feature or change
an existing one for instance), it must also include the following:
An explanation of the changes in the relevant CHANGELOG file(s) (the [BC BREAK] or the [DEPRECATION]
prefix must be used when relevant);
An explanation on how to upgrade an existing application in the relevant UPGRADE file(s) if the changes break
backward compatibility or if you deprecate something that will ultimately break backward compatibility.
Step 3: Submit your Patch

Whenever you feel that your patch is ready for submission, follow the following steps.
Rebase your Patch Before submitting your patch, update your branch (needed if it takes you a while to finish your
changes):
$
$
$
$
$

git
git
git
git
git

checkout master
fetch upstream
merge upstream/master
checkout BRANCH_NAME
rebase master

9.1. Contributing

333

Sylius, Release

When doing the rebase command, you might have to fix merge conflicts. git status will show you the unmerged files. Resolve all the conflicts, then continue the rebase:
$ git add ... # add resolved files
$ git rebase --continue

Push your branch remotely:


$ git push --force origin BRANCH_NAME

Make a Pull Request You can now make a pull request on the Sylius/Sylius GitHub repository.
To ease the core team work, always include the modified components in your pull request message, like in:
[Cart] Fixed something
[Taxation] [Addressing] Added something

The pull request description must include the following checklist at the top to ensure that contributions may be reviewed without needless feedback loops and that your contributions can be included into Sylius as quickly as possible:
|
|
|
|
|
|
|
|
|

Q
------------Bug fix?
New feature?
BC breaks?
Deprecations?
Fixed tickets
License
Doc PR

|
|
|
|
|
|
|
|
|

A
--[yes|no]
[yes|no]
[yes|no]
[yes|no]
[comma separated list of tickets fixed by the PR]
MIT
[The reference to the documentation PR if any]

An example submission could now look as follows:


|
|
|
|
|
|
|
|
|

Q
------------Bug fix?
New feature?
BC breaks?
Deprecations?
Fixed tickets
License
Doc PR

|
|
|
|
|
|
|
|
|

A
--no
no
no
no
#12, #43
MIT
Sylius/Sylius-Docs#123

The whole table must be included (do not remove lines that you think are not relevant). For simple typos, minor
changes in the PHPDocs, or changes in translation files, use the shorter version of the check-list:
|
|
|
|

Q
------------Fixed tickets
License

|
|
|
|

A
--[comma separated list of tickets fixed by the PR]
MIT

Some answers to the questions trigger some more requirements:


If you answer yes to Bug fix?, check if the bug is already listed in the Sylius issues and reference it/them in
Fixed tickets;
If you answer yes to New feature?, you must submit a pull request to the documentation and reference it under
the Doc PR section;
If you answer yes to BC breaks?, the patch must contain updates to the relevant CHANGELOG and UPGRADE
files;
334

Chapter 9. Contributing

Sylius, Release

If you answer yes to Deprecations?, the patch must contain updates to the relevant CHANGELOG and
UPGRADE files;
If some of the previous requirements are not met, create a todo-list and add relevant items:
- [ ] Fix the specs as they have not been updated yet
- [ ] Submit changes to the documentation
- [ ] Document the BC breaks

If the code is not finished yet because you dont have time to finish it or because you want early feedback on your
work, add an item to todo-list:
- [ ] Finish the feature
- [ ] Gather feedback for my changes

As long as you have items in the todo-list, please prefix the pull request title with [WIP].
In the pull request description, give as much details as possible about your changes (dont hesitate to give code
examples to illustrate your points). If your pull request is about adding a new feature or modifying an existing one,
explain the rationale for the changes. The pull request description helps the code review.
In addition to this code pull request, you must also send a pull request to the documentation repository to update the
documentation when appropriate.
Rework your Patch Based on the feedback on the pull request, you might need to rework your patch. Before
re-submitting the patch, rebase with upstream/master, dont merge; and force the push to the origin:
$ git rebase -f upstream/master
$ git push --force origin BRANCH_NAME

Note: When doing a push --force, always specify the branch name explicitly to avoid messing other branches
in the repo (--force tells Git that you really want to mess with things so do it carefully).
Often, Sylius team members will ask you to squash your commits. This means you will convert many commits to
one commit. To do this, use the rebase command:
$ git rebase -i upstream/master
$ git push --force origin BRANCH_NAME

After you type this command, an editor will popup showing a list of commits:
pick 1a31be6 first commit
pick 7fc64b4 second commit
pick 7d33018 third commit

To squash all commits into the first one, remove the word pick before the second and the last commits, and replace
it by the word squash or just s. When you save, Git will start rebasing, and if successful, will ask you to edit the
commit message, which by default is a listing of the commit messages of all the commits. When you are finished,
execute the push command.
Security Issues
This document explains how Sylius issues are handled by the Sylius core team.

9.1. Contributing

335

Sylius, Release

Reporting a Security Issue

If you think that you have found a security issue in Sylius, dont use the bug tracker and do not post it publicly. Instead,
all security issues must be sent to security [at] sylius.org. Emails sent to this address are forwarded to the Sylius core
team members.
Resolving Process

For each report, we first try to confirm the vulnerability. When it is confirmed, the team works on a solution following
these steps:
1. Send an acknowledgement to the reporter;
2. Work on a patch;
3. Write a security announcement for the official Sylius blog about the vulnerability. This post should contain the
following information:
a title that always include the Security release string;
a description of the vulnerability;
the affected versions;
the possible exploits;
how to patch/upgrade/workaround affected applications;
credits.
4. Send the patch and the announcement to the reporter for review;
5. Apply the patch to all maintained versions of Sylius;
6. Publish the post on the official Sylius blog;
7. Update the security advisory list (see below).
Note: Releases that include security issues should not be done on Saturday or Sunday, except if the vulnerability has
been publicly posted.
Note: While we are working on a patch, please do not reveal the issue publicly.

BDD Methodology
Note: This part of documentation is inspired by the official PHPSpec docs.
Sylius adopted the full-stack BDD methodology for its development processes.
According to Wikipedia:
BDD is a software development process based on test-driven development (TDD). Behavior-driven development combines the general techniques and principles of TDD with ideas from domain-driven design
and object-oriented analysis and design to provide software developers and business analysts with shared
tools and a shared process to collaborate on software development, with the aim of delivering software
that matters.

336

Chapter 9. Contributing

Sylius, Release

Setting up Behat & PHPSpec

To run the entire suite of features and specs, including the ones that depend on external dependencies, Sylius needs
to be able to autoload them. By default, they are autoloaded from vendor/ under the main root directory (see
autoload.php.dist).
To install them all, use Composer:
Step 1: Get Composer
$ curl -s http://getcomposer.org/installer | php

Make sure you download composer.phar in the same folder where the composer.json file is located.
Step 2: Install vendors
$ php composer.phar install

Note: Note that the script takes some time (several minutes) to finish.
Note:
If you dont have curl installed, you can also just download the installer file manually at
http://getcomposer.org/installer. Place this file into your project and then run:
$ php installer
$ php composer.phar install

Install Selenium2 Download Selenium server 2.38 here.


Create a VirtualHost Add this VirtualHost configuration:
<VirtualHost *:80>
ServerName sylius-test.local
RewriteEngine On
DocumentRoot /var/www/sylius/web
<Directory /var/www/sylius/web>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^(.*) %{DOCUMENT_ROOT}/app_test.php [QSA,L]
ErrorLog ${APACHE_LOG_DIR}/sylius-test-error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/sylius-test-access.log combined
</VirtualHost>

Update your /etc/hosts file to include the VirtualHost hostname:

9.1. Contributing

337

Sylius, Release

127.0.0.1

sylius-test.local

Additionally, copy behat.yml.dist to behat.yml, edit base_url parameter to match your host:
default:
...
extensions:
Behat\MinkExtension\Extension:
...
base_url: http://sylius-test.local/app_test.php/

Behat

We use Behat for StoryBDD and you should always write new scenarios when adding a feature, or update existing
stories to adapt Sylius to business requirements changes.
Sylius is an open source project, so the client is not clearly visible at first look. But they are here - the Sylius users.
We have our needs and Behat helps us understand and satisfy these needs.
Note: To be written.
You can launch Selenium by issuing the following command:
$ java -jar selenium-server-standalone-2.38.0.jar

Configure behat for Selenium:


default:
...
extensions:
Behat\MinkExtension\Extension:
default_session: selenium2
browser_name: firefox
base_url: http://sylius-test.local/app_test.php
selenium2:
capabilities: { "browser": "firefox", "version": "28"}

Run your scenario using the behat console:


$ bin/behat

PHPSpec

PHPSpec is a PHP toolset to drive emergent design by specification. It is not really a testing tool, but a design
instrument, which helps structuring the objects and how they work together.
Sylius approach is to always describe the behavior of the next object you are about to implement.
As an example, well write a service, which updates product prices based on an external API. To initialize a new spec,
use the desc command.
We just need to tell PHPSpec we will be working on the PriceUpdater class.
$ bin/phpspec desc "Sylius\Bundle\CoreBundle\Pricing\PriceUpdater"
Specification for PriceUpdater created in spec.

338

Chapter 9. Contributing

Sylius, Release

What have we just done? PHPSpec has created the spec for us. You can navigate to the spec folder and see the spec
there:
<?php
namespace spec\Sylius\Bundle\CoreBundle\Pricing;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class PriceUpdaterSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Bundle\CoreBundle\Pricing\PriceUpdater');
}
}

The object behavior is made of examples. Examples are encased in public methods, started with it_. or its_.
PHPSpec searches for such methods in your specification to run. Why underscores for example names?
just_because_its_much_easier_to_read than someLongCamelCasingLikeThat.
Now, lets write first example which will update the products price:
<?php
namespace spec\Sylius\Bundle\CoreBundle\Pricing;
use
use
use
use

Acme\ApiClient;
PhpSpec\ObjectBehavior;
Prophecy\Argument;
Sylius\Bundle\CoreBundle\Model\ProductInterface;

class PriceUpdaterSpec extends ObjectBehavior


{
function let(ApiClient $api)
{
$this->beConstructedWith($api);
}
function it_updates_product_price_through_api($api, ProductInterface $product)
{
$product->getSku()->shouldBeCalled()->willReturn('TES-12-A-1090');
$api->getCurrentProductPrice('TES-12-A-1090')->shouldBeCalled()->willReturn(1545);
$product->setPrice(1545)->shouldBeCalled();
$this->updatePrice($product);
}
}

The example looks clear and simple, the PriceUpdater service should obtain the SKU of the product, call the
external API and update products price accordingly.
Try running the example by using the following command:
$ bin/phpspec run
> spec\Sylius\Bundle\CoreBundle\Pricing\PriceUpdater

9.1. Contributing

339

Sylius, Release

it updates product price through api


Class PriceUpdater does not exists.
Do you want me to create it for you? [Y/n]

Once the class is created and you run the command again, PHPSpec will ask if it should create the method as well.
Start implementing the very initial version of the price updater.
<?php
namespace Sylius\Bundle\CoreBundle\Pricing;
use Sylius\Bundle\CoreBundle\Model\ProductInterface;
use Acme\ApiClient;
class PriceUpdater
{
private $api;
public function __construct(ApiClient $api)
{
$this->api = $api;
}
public function updatePrice(ProductInterface $product)
{
$price = $this->api->getCurrentProductPrice($product->getSku());
$product->setPrice($price);
}
}

Done! If you run PHPSpec again, you should see the following output:
$ bin/phpspec run
> spec\Sylius\Bundle\CoreBundle\Pricing\PriceUpdater
it updates product price through api
1 examples (1 passed)
223ms

This example is greatly simplified, in order to illustrate how we work. There should be few more examples, which
cover errors, API exceptions and other edge-cases.
Few tips & rules to follow when working with PHPSpec & Sylius:
RED is good, add or fix the code to make it green;
RED-GREEN-REFACTOR is our rule;
All specs must pass;
When writing examples, describe the behavior of the object in present tense;
Omit the public keyword;
Use underscores (_) in the examples;
Use type hinting to mock and stub classes;
If your specification is getting too complex, the design is wrong, try decoupling a bit more;

340

Chapter 9. Contributing

Sylius, Release

If you cannot describe something easily, probably you should not be doing it that way;
shouldBeCalled or willReturn, never together, except for builders;
Use constants in assumptions but strings in expected results;
Happy coding!
Coding Standards
When contributing code to Sylius, you must follow its coding standards.
Sylius follows the standards defined in the PSR-0, PSR-1 and PSR-2 documents.
Here is a short example containing most features described below:
<?php
/*
* This file is part of the Sylius package.
*
edrzejewski
* (c) Pawe J
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Acme;
/**
* Coding standards demonstration.
*/
class FooBar
{
const SOME_CONST = 42;
private $fooBar;
/**
* @param string $dummy Some argument description
*/
public function __construct($dummy)
{
$this->fooBar = $this->transformText($dummy);
}
/**
* @param string $dummy Some argument description
* @param array $options
*
* @return string|null Transformed input
*
* @throws \RuntimeException
*/
private function transformText($dummy, array $options = array())
{
$mergedOptions = array_merge(
array(
'some_default'
=> 'values',
'another_default' => 'more values',

9.1. Contributing

341

Sylius, Release

),
$options
);
if (true === $dummy) {
return;
}
if ('string' === $dummy) {
if ('values' === $mergedOptions['some_default']) {
return substr($dummy, 0, 5);
}
return ucwords($dummy);
}
throw new \RuntimeException(sprintf('Unrecognized dummy option "%s"', $dummy));
}
}

Structure

Add a single space after each comma delimiter;


Add a single space around operators (==, &&, ...);
Add a comma after each array item in a multi-line array, even after the last one;
Add a blank line before return statements, unless the return is alone inside a statement-group (like an if
statement);
Use braces to indicate control structure body regardless of the number of statements it contains;
Define one class per file - this does not apply to private helper classes that are not intended to be instantiated
from the outside and thus are not concerned by the PSR-0 standard;
Declare class properties before methods;
Declare public methods first, then protected ones and finally private ones;
Use parentheses when instantiating classes regardless of the number of arguments the constructor has;
Exception message strings should be concatenated using :phpfunction:sprintf.
Naming Conventions

Use camelCase, not underscores, for variable, function and method names, arguments;
Use underscores for option names and parameter names;
Use namespaces for all classes;
Prefix abstract classes with Abstract.
Suffix interfaces with Interface;
Suffix traits with Trait;
Suffix exceptions with Exception;
Use alphanumeric characters and underscores for file names;

342

Chapter 9. Contributing

Sylius, Release

Dont forget to look at the more verbose Conventions document for more subjective naming considerations.
Service Naming Conventions
A service name contains groups, separated by dots;
All Sylius services use sylius as first group;
Use lowercase letters for service and parameter names;
A group name uses the underscore notation;
Each service has a corresponding parameter containing the class name, following the service_name.class
convention.
Documentation

Add PHPDoc blocks for all classes, methods, and functions;


Omit the @return tag if the method does not return anything;
The @package and @subpackage annotations are not used.
License

Sylius is released under the MIT license, and the license block has to be present at the top of every PHP file,
before the namespace.
Conventions
This document describes coding standards and conventions used in the Sylius codebase to make it more consistent and
predictable.
Method Names

When an object has a main many relation with related things (objects, parameters, ...), the method names are
normalized:
get()
set()
has()
all()
replace()
remove()
clear()
isEmpty()
add()
register()
count()
9.1. Contributing

343

Sylius, Release

keys()
The usage of these methods are only allowed when it is clear that there is a main relation:
a CookieJar has many Cookie objects;
a Service Container has many services and many parameters (as services is the main relation, the naming
convention is used for this relation);
a Console Input has many arguments and many options. There is no main relation, and so the naming
convention does not apply.
For many relations where the convention does not apply, the following methods must be used instead (where XXX is
the name of the related thing):
Main Relation
get()
set()
n/a
has()
all()
replace()
remove()
clear()
isEmpty()
add()
register()
count()
keys()

Other Relations
getXXX()
setXXX()
replaceXXX()
hasXXX()
getXXXs()
setXXXs()
removeXXX()
clearXXX()
isEmptyXXX()
addXXX()
registerXXX()
countXXX()
n/a

Note: While setXXX and replaceXXX are very similar, there is one notable difference: setXXX may replace,
or add new elements to the relation. replaceXXX, on the other hand, cannot add new elements. If an unrecognized
key is passed to replaceXXX it must throw an exception.

Deprecations

Warning: Sylius is the pre-alpha development stage. We release minor version before every larger change, but be
prepared for BC breaks to happen until 1.0.0 release.
From time to time, some classes and/or methods are deprecated in the framework; that happens when a feature implementation cannot be changed because of backward compatibility issues, but we still want to propose a better
alternative. In that case, the old implementation can simply be deprecated.
A feature is marked as deprecated by adding a @deprecated phpdoc to relevant classes, methods, properties, ...:
/**
* @deprecated Deprecated since version 1.X, to be removed in 1.Y. Use XXX instead.
*/

The deprecation message should indicate the version when the class/method was deprecated, the version when it will
be removed, and whenever possible, how the feature was replaced.
A PHP E_USER_DEPRECATED error must also be triggered to help people with the migration starting one or two
minor versions before the version where the feature will be removed (depending on the criticality of the removal):

344

Chapter 9. Contributing

Sylius, Release

trigger_error(
'XXX() is deprecated since version 2.X and will be removed in 2.Y. Use XXX instead.',
E_USER_DEPRECATED
);

Git
This document explains some conventions and specificities in the way we manage the Sylius code with Git.
Pull Requests

Whenever a pull request is merged, all the information contained in the pull request is saved in the repository.
You can easily spot pull request merges as the commit message always follows this pattern:
merged branch USER_NAME/BRANCH_NAME (PR #1111)

The PR reference allows you to


https://github.com/Sylius/Sylius/pull/1111.
the reasoning behind the changes.

have a look at the original pull request on GitHub:


Often, this can help understand what the changes were about and

Sylius License
Sylius is released under the MIT license.
According to Wikipedia:
It is a permissive license, meaning that it permits reuse within proprietary software on the condition that
the license is distributed with that software. The license is also GPL-compatible, meaning that the GPL
permits combination and redistribution with software that uses the MIT License.
The License

Copyright (c) 2011-2014 Pawe Jedrzejewski


Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

9.1. Contributing

345

Sylius, Release

9.1.2 Community
Support
We have very friendly community which provides support for all Sylius users seeking help!
IRC Channels

There are 2 channels available on Freenode IRC network, where you can meet other Sylius developers, ask for help or
discuss ideas.
Users channel Channel #sylius is for Sylius users - a place where you should seek help and advices. Usually you
can meet there several people eager to provide your with community support and answer your questions.
We invite everyone interested in using Sylius to this room, we warmly welcome new people in our growing community!
Developers channel sylius-dev is the channel where youll most likely meet Sylius core team and contributors. It
is the right place to discuss about Pull Requests, ideas and architecture. It will be perfect starting point if you want to
contribute to the project or chat about concept youd like to introduce at Sylius.
Now the best part! You do not need to be a Symfony2 guru to help! There is plenty of work which can be done by
people starting with Symfony, and to us, every contribution is invaluable!
How to connect You should pick a nice username and connect to irc.freenode.org via XChat or any other
IRC client. You can also use web client.
StackOverflow.com

We encourage asking Sylius related questions on the stackoverflow.com platform. Be sure to tag them with sylius tag
- it will make it easier to find for people who can answer it.
To view all Sylius related questions - visit this link. You can also search for phrase.
Statistics
You can always check sylius.org/community to see an overview of our community at work!
Below, you can find more useful links:
Sylius Pull Requests - pull requests
Most recent patches - commits
GitHub issues - issues
Symfony2 bundles index - knpbundles.com

346

Chapter 9. Contributing

Sylius, Release

9.1.3 Contributing Documentation


Contributing to the Documentation
Documentation is as important as code. It follows the exact same principles: DRY, tests, ease of maintenance, extensibility, optimization, and refactoring just to name a few. And of course, documentation has bugs, typos, hard to read
tutorials, and more.
Contributing

Before contributing, you need to become familiar with the markup language used by the documentation.
The Sylius documentation is hosted on GitHub:
https://github.com/Sylius/Sylius-Docs

If you want to submit a patch, fork the official repository on GitHub and then clone your fork:
$ git clone git://github.com/YOURUSERNAME/Sylius-Docs.git

The master branch holds the documentation for the development branch of the code.
Create a dedicated branch for your changes (for organization):
$ git checkout -b improving_foo_and_bar

You can now make your changes directly to this branch and commit them. When youre done, push this branch to your
GitHub fork and initiate a pull request.
Creating a Pull Request Following the example, the pull request will default to be between your
improving_foo_and_bar branch and the Sylius-Docs master branch.
GitHub covers the topic of pull requests in detail.
Note: The Sylius documentation is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
You can also prefix the title of your pull request in a few cases:
[WIP] (Work in Progress) is used when you are not yet finished with your pull request, but you would like it to
be reviewed. The pull request wont be merged until you say it is ready.
[WCM] (Waiting Code Merge) is used when youre documenting a new feature or change that hasnt been
accepted yet into the core code. The pull request will not be merged until it is merged in the core code (or closed
if the change is rejected).
Pull Request Format Unless youre fixing some minor typos, the pull request description must include the following checklist to ensure that contributions may be reviewed without needless feedback loops and that your contributions
can be included into the documentation as quickly as possible:
|
|
|
|
|

Q
------------Doc fix?
New docs?
Fixed tickets

9.1. Contributing

|
|
|
|
|

A
--[yes|no]
[yes|no] (PR # on Sylius/Sylius if applicable)
[comma separated list of tickets fixed by the PR]

347

Sylius, Release

An example submission could now look as follows:


|
|
|
|
|

Q
------------Doc fix?
New docs?
Fixed tickets

|
|
|
|
|

A
--yes
yes (Sylius/Sylius#1250)
#1075

Tip: Online documentation is rebuilt on every code-push to github.

Documenting new Features or Behavior Changes

If youre documenting a brand new feature or a change thats been made in Sylius, you should precede your description
of the change with a .. versionadded:: 1.X tag and a short description:
.. versionadded:: 1.1
The ``getProductDiscount`` method was introduced in Sylius 1.1.

Standards

All documentation in the Sylius Documentation should follow the documentation standards.
Reporting an Issue

The most easy contribution you can make is reporting issues: a typo, a grammar mistake, a bug in a code example, a
missing explanation, and so on.
Steps:
Submit new issue in the GitHub tracker;
(optional) Submit a patch.
Translating

Read the dedicated document.


Documentation Format
The Sylius documentation uses reStructuredText as its markup language and Sphinx for building the output (HTML,
PDF, ...).
reStructuredText

reStructuredText is an easy-to-read, what-you-see-is-what-you-get plaintext markup syntax and parser system.


You can learn more about its syntax by reading existing Sylius documents or by reading the reStructuredText Primer
on the Sphinx website.
If you are familiar with Markdown, be careful as things are sometimes very similar but different:
Lists starts at the beginning of a line (no indentation is allowed);
348

Chapter 9. Contributing

Sylius, Release

Inline code blocks use double-ticks (like this).


Sphinx

Sphinx is a build system that adds some nice tools to create documentation from reStructuredText documents. As
such, it adds new directives and interpreted text roles to standard reST markup.
Syntax Highlighting All code examples uses PHP as the default highlighted language. You can change it with the
code-block directive:
.. code-block:: yaml
{ foo: bar, bar: { foo: bar, bar: baz } }

If your PHP code begins with <?php, then you need to use html+php as the highlighted pseudo-language:
.. code-block:: html+php
<?php echo $this->foobar(); ?>

Note: A list of supported languages is available on the Pygments website.

Configuration Blocks Whenever you show a configuration, you must use the configuration-block directive
to show the configuration in all supported configuration formats (PHP, YAML, and XML)
.. configuration-block::
.. code-block:: yaml
# Configuration in YAML
.. code-block:: xml
<!-- Configuration in XML //-->
.. code-block:: php
// Configuration in PHP

The previous reST snippet renders as follow:


YAML
# Configuration in YAML

XML
<!-- Configuration in XML //-->

PHP
// Configuration in PHP

The current list of supported formats are the following:

9.1. Contributing

349

Sylius, Release

Markup format
html
xml
php
yaml
jinja
html+jinja
html+php
ini
php-annotations

Displayed
HTML
XML
PHP
YAML
Twig
Twig
PHP
INI
Annotations

Adding Links To add links to other pages in the documents use the following syntax:
:doc:`/path/to/page`

Using the path and filename of the page without the extension, for example:
:doc:`/book/architecture`
:doc:`/bundles/SyliusAddressingBundle/installation`

The link text will be the main heading of the document linked to. You can also specify alternative text for the link:
:doc:`Simple CRUD </bundles/SyliusResourceBundle/installation>`

You can also add links to the API documentation:


:namespace:`Sylius\\Bundle\\CoreBundle`
:class:`Sylius\\Bundle\\CoreBundle\\Model\\Order`
:method:`Sylius\\Bundle\\AddressingBundle\\Matcher\\ZoneMatcher::match`

and to the PHP documentation:


:phpclass:`SimpleXMLElement`
:phpmethod:`DateTime::createFromFormat`
:phpfunction:`iterator_to_array`

Testing Documentation To test documentation before a commit:


Install Sphinx;
Run the Sphinx quick setup;
Install the Sphinx extensions (see below);
Run make html and view the generated HTML in the build directory.
Installing the Sphinx extensions
Download the extension from the source repository
Copy the sensio directory to the _exts folder under your source folder (where conf.py is located)
Add the following to the conf.py file:

350

Chapter 9. Contributing

Sylius, Release

# ...
sys.path.append(os.path.abspath('_exts'))
# adding PhpLexer
from sphinx.highlighting import lexers
from pygments.lexers.web import PhpLexer

# ...
# add the extensions to the list of extensions
extensions = [..., 'sensio.sphinx.refinclude', 'sensio.sphinx.configurationblock', 'sensio.sphinx.php
# enable highlighting for PHP code not between ``<?php ... ?>`` by default
lexers['php'] = PhpLexer(startinline=True)
lexers['php-annotations'] = PhpLexer(startinline=True)
lexers['php-standalone'] = PhpLexer(startinline=True)
lexers['php-symfony'] = PhpLexer(startinline=True)
# use PHP as the primary domain
primary_domain = 'php'
# set URL for API links
api_url = 'http://api.sylius.org/master/%s'

Documentation Standards
In order to help the reader as much as possible and to create code examples that look and feel familiar, you should
follow these standards.
Sphinx

The following characters are chosen for different heading levels: level 1 is =, level 2 -, level 3 ~, level 4 . and
level 5 ";
Each line should break approximately after the first word that crosses the 72nd character (so most lines end up
being 72-78 characters);
The :: shorthand is preferred over .. code-block::
documentation to see when you should use the shorthand);

php to begin a PHP code block (read the Sphinx

Inline hyperlinks are not used. Separate the link and their target definition, which you add on the bottom of the
page;
Inline markup should be closed on the same line as the open-string;
Example
Example
=======
When you are working on the docs, you should follow the
`Sylius Documentation`_ standards.
Level 2
------A PHP example would be::

9.1. Contributing

351

Sylius, Release

echo 'Hello World';


Level 3
~~~~~~~
.. code-block:: php
echo 'You cannot use the :: shortcut here';

.. _`Sylius Documentation`: http://docs.sylius.org/en/latest/contributing/documentation/standards.htm

Code Examples

The code follows the Sylius Coding Standards as well as the Twig Coding Standards;
To avoid horizontal scrolling on code blocks, we prefer to break a line correctly if it crosses the 85th character;
When you fold one or more lines of code, place ... in a comment at the point of the fold. These comments
are: // ... (php), # ... (yaml/bash), {# ... #} (twig), <!-- ... --> (xml/html), ; ... (ini),
... (text);
When you fold a part of a line, e.g. a variable value, put ... (without comment) at the place of the fold;
Description of the folded code: (optional) If you fold several lines: the description of the fold can be placed
after the ... If you fold only part of a line: the description can be placed before the line;
If useful to the reader, a PHP code example should start with the namespace declaration;
When referencing classes, be sure to show the use statements at the top of your code block. You dont need to
show all use statements in every example, just show what is actually being used in the code block;
If useful, a codeblock should begin with a comment containing the filename of the file in the code block.
Dont place a blank line after this comment, unless the next line is also a comment;
You should put a $ in front of every bash line.
Formats Configuration examples should show recommended formats using configuration blocks. The recommended
formats (and their orders) are:
Configuration (including services and routing): YAML
Validation: XML
Doctrine Mapping: XML
Example
// src/Foo/Bar.php
namespace Foo;
use Acme\Demo\Cat;
// ...
class Bar
{
// ...
public function foo($bar)

352

Chapter 9. Contributing

Sylius, Release

{
// set foo with a value of bar
$foo = ...;
$cat = new Cat($foo);
// ... check if $bar has the correct value
return $cat->baz($bar, ...);
}
}

Caution: In YAML you should put a space after { and before } (e.g. { _controller:
should not be done in Twig (e.g. {hello : value}).

...

}), but this

Language Standards

For sections, use the following capitalization rules: Capitalization of the first word, and all other words, except
for closed-class words:
The Vitamins are in my Fresh California Raisins
Do not use Serial (Oxford) Commas;
You should use a form of you instead of we (i.e. avoid the first person point of view: use the second instead);
When referencing a hypothetical person, such as a user with a session cookie, gender-neutral pronouns
(they/their/them) should be used. For example, instead of:
he or she, use they
him or her, use them
his or her, use their
his or hers, use theirs
himself or herself, use themselves
Sylius Documentation License
The Sylius documentation is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
You are free:
to Share to copy, distribute and transmit the work;
to Remix to adapt the work.
Under the following conditions:
Attribution You must attribute the work in the manner specified by the author or licensor (but not in any way
that suggests that they endorse you or your use of the work);
Share Alike If you alter, transform, or build upon this work, you may distribute the resulting work only under
the same or similar license to this one.
With the understanding that:
Waiver Any of the above conditions can be waived if you get permission from the copyright holder;

9.1. Contributing

353

Sylius, Release

Public Domain Where the work or any of its elements is in the public domain under applicable law, that
status is in no way affected by the license;
Other Rights In no way are any of the following rights affected by the license:
Your fair dealing or fair use rights, or other applicable copyright exceptions and limitations;
The authors moral rights;
Rights other persons may have either in the work itself or in how the work is used, such as publicity or
privacy rights.
Notice For any reuse or distribution, you must make clear to others the license terms of this work. The best
way to do this is with a link to this web page.
This is a human-readable summary of the Legal Code (the full license).
Code
Bugs
Patches
Security
Behavior Driven Development
Coding Standards
Code Conventions
Git
License
Documentation
Overview
Format
Documentation Standards
License
Community
Support
Statistics
Code
Bugs
Patches
Security
Behavior Driven Development
Coding Standards
Code Conventions
Git
License
Documentation

354

Chapter 9. Contributing

Sylius, Release

Overview
Format
Documentation Standards
License
Community
Support
Statistics

9.1. Contributing

355

Sylius, Release

356

Chapter 9. Contributing

Index

Addresses, 12
Architecture, 6
Authorization, 33

Users and Groups, 23

C
Channels, 10
Content, 27
Currencies, 25

E
E-Mails, 28
Environments, 4

I
Installation, 5, 83
Introduction, 3, 91
Introduction to Sylius REST API, 33
Inventory, 14

L
Locales, 26

O
Orders, 15

P
Payments, 18
Pricing, 21
Products, 11
Promotions, 22

S
Settings, 29
Shipments, 17
State Machine, 9

T
Taxation, 20

357

Anda mungkin juga menyukai