Skip to content

Commit

Permalink
Merge pull request #5214 from TheMadeleine/docs/customization-guide
Browse files Browse the repository at this point in the history
[Documentation] Customization Guide
  • Loading branch information
Paweł Jędrzejewski committed Jun 27, 2016
2 parents f7bb196 + 8f46987 commit d8de7ed
Show file tree
Hide file tree
Showing 13 changed files with 741 additions and 73 deletions.
71 changes: 0 additions & 71 deletions docs/cookbook/extending-menu.rst

This file was deleted.

1 change: 0 additions & 1 deletion docs/cookbook/index.rst
Expand Up @@ -5,7 +5,6 @@ Cookbook
:hidden:

installation-commands
extending-menu
registry

.. include:: /cookbook/map.rst.inc
1 change: 0 additions & 1 deletion docs/cookbook/map.rst.inc
@@ -1,3 +1,2 @@
* :doc:`/cookbook/installation-commands`
* :doc:`/cookbook/extending-menu`
* :doc:`/cookbook/registry`
166 changes: 166 additions & 0 deletions docs/customization/controller.rst
@@ -0,0 +1,166 @@
Customizing Controllers
=======================

All **Sylius** resources are using the `` Sylius\Bundle\ResourceBundle\Controller\ResourceController`` as default, but some of them have been already extended in Bundles.
If you want to override some controller action, check which controller you should be extending.

.. note::
There are two types of controllers we can define in Sylius.
**Resource Controllers** - are basing only on one Entity, so they return only the resources they have in their name. For instance a ``ProductController`` should return only products.
**Standard Controllers** - non-resource; these may use many entities at once, they are useful on more general pages. We are extending these controllers only if the actions we want cannot be done through yaml configuration - like sending emails.

Why would you customize a Controller?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To add your custom actions you need to override controllers. You may bee needing to:

* add a generic action that will render a list of recommended products with a product on its show page,
* render a partial template that cannot be done via yaml resource action.

How to customize a Resource Controller?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Imagine that you would want to render a list of best selling products in a partial template that will be reusable anywhere.
Assuming that you already have a method on the ``ProductRepository`` - you can see such an example :doc:`here </customization/repository>`.
Having this method you may be rendering its result in a new action of the ``ProductController`` using a partial template.

See example below:

1. Create a new Controller class under the ``AppBundle/Controller`` namespace.

Remember that it has to extend a proper base class. How can you check that?

For the ``ProductController`` run:

.. code-block:: bash
$ php app/console debug:container sylius.controller.product
As a result you will get the ``Sylius\Bundle\CoreBundle\Controller\ProductController`` - this is the class that you need to be extending.

Now you have to create the controller that will have a generic action that is basically the ``showAction`` from the ``ResourceController`` extended by
getting a list of recommended product from your external api.

.. code-block:: php
<?php
namespace AppBundle\Controller;
use FOS\RestBundle\View\View;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Sylius\Bundle\CoreBundle\Controller\ProductController as BaseProductController;
use Sylius\Component\Resource\ResourceActions;
class ProductController extends BaseProductController
{
/**
* @param Request $request
*
* @return Response
*/
public function showAction(Request $request)
{
$configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($configuration, ResourceActions::SHOW);
$product = $this->findOr404($configuration);
$recommendationServiceApi = $this->get('app.recommendation_service_api');
$recommendedProducts = $recommendationServiceApi->getRecommendedProducts($product);
$this->eventDispatcher->dispatch(ResourceActions::SHOW, $configuration, $product);
$view = View::create($product);
if ($configuration->isHtmlRequest()) {
$view
->setTemplate($configuration->getTemplate(ResourceActions::SHOW . '.html'))
->setTemplateVar($this->metadata->getName())
->setData([
'configuration' => $configuration,
'metadata' => $this->metadata,
'resource' => $product,
'recommendedProducts' => $recommendedProducts,
$this->metadata->getName() => $product,
])
;
}
return $this->viewHandler->handle($configuration, $view);
}
}
2. In order to use your controller and its actions you need to configure it in the ``app/config/config.yml``.

.. code-block:: yaml
sylius_product:
resources:
product:
classes:
controller: AppBundle\Controller\ProductController
How to customize a Standard Controller?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Let's assume that you would like to send some kind of emails (which are not resources) after something has been purchased in your shop - to do this you should modify an ``afterPurchaseAction`` on the ``OrderController``.

1. Create a new Controller class under the ``AppBundle/Controller/Frontend`` namespace.

Run ``$ php app/console debug:container sylius.controller.frontend.order``.

Your class needs to be extending this base class.

.. code-block:: php
<?php
namespace AppBundle\Controller\Frontend;
use Sylius\Bundle\WebBundle\Controller\Frontend\Account\OrderController as BaseOrderController;
use Sylius\Bundle\PayumBundle\Request\GetStatus;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class OrderController extends BaseOrderController
{
/**
* @param Request $request
*
* @return Response
*/
public function afterPurchaseAction(Request $request)
{
$token = $this->getHttpRequestVerifier()->verify($request);
$this->getHttpRequestVerifier()->invalidate($token);
$status = new GetStatus($token);
$this->getPayum()->getGateway($token->getGatewayName())->execute($status);
$payment = $status->getFirstModel();
$order = $payment->getOrder();
$this->checkAccessToOrder($order);
$orderStateResolver = $this->get('sylius.order_processing.state_resolver');
$orderStateResolver->resolvePaymentState($order);
$orderStateResolver->resolveShippingState($order);
$this->getOrderManager()->flush();
$emailManager = $this->get('sylius.email_manager.order');
$emailManager->sendConfirmationEmail($order);
return $this->redirectToRoute('sylius_checkout_thank_you');
}
}
2. The next thing you have to do is to override the ``sylius.controller.frontend.order.class`` parameter in ``AppBundle/Resources/config/services.yml``.

.. code-block:: yaml
parameters:
sylius.controller.frontend.order.class: AppBundle\Controller\Frontend\OrderController
From now on your ``afterPurchaseAction`` of the ``OrderController`` will also send emails in addition to its default behaviour.
122 changes: 122 additions & 0 deletions docs/customization/form.rst
@@ -0,0 +1,122 @@
Customizing Forms
=================

The forms in Sylius are placed in the ``Sylius\Bundle\*BundleName*\Form\Type`` namespaces.

.. warning::
Many forms in Sylius are **extended in the CoreBundle**.
If the form you are willing to override exists in the ``CoreBundle`` your should be extending the one from Core, not the base form from the bundle.

Why would you customize a Form?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

There are plenty of reasons to modify forms that have already been defined in Sylius.
Your business needs may sometimes slightly differ from our internal assumptions.

You can:

* add completely **new fields**, if you need another phone number for your customers,
* **modify** existing fields, make them required, change their HTML class, change labels etc.,
* **remove** fields that are not used.

How to customize a Form?
~~~~~~~~~~~~~~~~~~~~~~~~

If you want to modify the form for the ``Address`` in your system there are a few steps that you should take.
Assuming that you would like to (for example):

* Add a ``contactHours`` field,
* Remove the ``company`` field,
* Change the label for the ``lastName`` from ``sylius.form.address.last_name`` to ``app.form.address.surname``

These will be the steps that you will have to take to achieve that:

1. If your are planning to add new fields remember that beforehand they need to be added on the model that the form type is based on.

In case of our example if you need to have the ``contactHours`` on the model and the entity mapping for the ``Address`` resource.
To get to know how to prepare that go :doc:`there </customization/model>`.

2. Write your own form type class that will be extending the default one. Place it in your ``AppBundle\Form\Type`` directory.

Your new class has to extend a proper base class. How can you check that?

For the ``AddressType`` run:

.. code-block:: bash
$ php app/console debug:container sylius.form.type.address
As a result you will get the ``Sylius\Bundle\AddressingBundle\Form\Type\AddressType`` - this is the class that you need to be extending.

.. code-block:: php
<?php
namespace Acme\Bundle\ShopBundle\Form\Type;
use Sylius\Bundle\AddressingBundle\Form\Type\AddressType as BaseAddressType;
use Symfony\Component\Form\FormBuilderInterface;
class AddressType extends BaseAddressType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Add default fields from the `BaseAddressType` that you are extending.
parent::buildForm($builder, $options);
// Adding new fields works just like in the parent form.
$builder->add('contactHours', 'text', [
'required' => false,
'label' => 'app.form.address.contact_hours',
]);
// To remove a field from a form simply call ->remove(`fieldName`).
$builder->remove('company');
// You can change the label by adding again the same field with a changed `label` parameter.
$builder->add('lastName', 'text', [
'label' => 'app.form.address.surname',
]);
}
}
3. Define your newly created class in the ``app/config/config.yml``.

.. code-block:: yaml
sylius_addressing:
resources:
address:
classes:
form:
default: AppBundle\Form\Type\AddressType
.. note::
Of course remember that you need to render the new fields you have created,
and remove the rendering of the fields that you have removed **in your views**.

In **Twig** for example you can render your modified form in such a way:

.. code-block:: html

<div id="addressForm">
{{ form_row(form.firstName) }}
{{ form_row(form.lastName) }}
{{ form_row(form.city) }}
{{ form_row(form.street) }}
{{ form_row(form.postcode) }}
{{ form_row(form.countryCode) }}
{{ form_row(form.provinceCode) }}
{{ form_row(form.phoneNumber) }}
{{ form_row(form.contactHours) }}
</div>

What happens while overriding Forms?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* Parameter ``sylius.form.type.address.class`` contains the ``AppBundle\Form\Type\AddressType``.
* ``sylius.form.type.address`` form type service uses your custom class.
* ``sylius_address`` form type uses your new form everywhere.
15 changes: 15 additions & 0 deletions docs/customization/index.rst
@@ -0,0 +1,15 @@
The Customization Guide
=======================

.. toctree::
:hidden:

model
form
repository
controller
validation
menu
template

.. include:: /customization/map.rst.inc
7 changes: 7 additions & 0 deletions docs/customization/map.rst.inc
@@ -0,0 +1,7 @@
* :doc:`/customization/model`
* :doc:`/customization/form`
* :doc:`/customization/repository`
* :doc:`/customization/controller`
* :doc:`/customization/validation`
* :doc:`/customization/menu`
* :doc:`/customization/template`

0 comments on commit d8de7ed

Please sign in to comment.