Add a custom route outside the CMS

In this cookbook entry we want to add a custom route to display the details related to a generic product on a web page and render them on the page, using a template of the current theme used by RedKite CMS for your website.

The web page layout will be one of the templates that comes with the BootbusinessThemeBundle and the data fetched from the database, will be displayed into some template’s blocks.

In addition, we will fetch this information from a database, using Doctrine ORM, to demonstrate how RedKite CMS, which is powered by Propel ORM, simply sits beside your website and allows you to choose the tools you feel most comfortable with, without forcing to use something you don’t want to.

Note

This tutorial grabs some code from the Symfony2 Doctrine chapter and implements some of the entities proposed there.

You still need a configured Doctrine connection and a database which contains the product table as defined by the Entity we are going to create.

Add a new route

This tutorial assumes that the configured deploy bundle is AcmeWebSiteBundle.

Be sure your deploy bundles routing.yml file is imported in the apps routing.yml configuration, otherwise import it as follows:

# app/config/routing.yml
_WebSiteBundle:
    resource: "@AcmeWebSiteBundle/Resources/config/routing.yml"

Add or open the routing.yml file under the Resources/config folder of your deploy bundle and add the following route:

# src/Acme/WebSiteBundle/Resources/config/routing.yml
_product:
    pattern: /{_locale}/product/{id}
    defaults: { _controller: AcmeWebSiteBundle:WebSite:product }

Add the entity that manages the product

Add the Entity folder to your deploy bundle, and create inside it the Product.php file, then add the following code:

// src/Acme/WebSiteBundle/Entity/Product.php
namespace Acme\WebSiteBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\Table(name="product")
*/
class Product
{
    /**
    * @ORM\Id
    * @ORM\Column(type="integer")
    * @ORM\GeneratedValue(strategy="AUTO")
    */
    protected $id;
    /**
    * @ORM\Column(type="string", length=100)
    */

    protected $name;
    /**
    * @ORM\Column(type="decimal", scale=2)
    */
    protected $price;

    /**
    * @ORM\Column(type="text")
    */
    protected $description;
}

Next, run the following Terminal command to generate the getters and setters:

php app/console doctrine:generate:entities Acme/WebSiteBundle/Entity/Product

Add the action

Open the WebSiteController.php file and add a new action as set out below:

// src/Acme/WebSiteBundle/Controller/WebSiteController.php
    [...]
    use RedKiteLabs\ThemeEngineBundle\Core\Rendering\Controller\FrontendController;

class WebSiteController extends FrontendController
{
    public function productAction($id)
    {
        $product = $this->getDoctrine()
            ->getRepository('AcmeWebSiteBundle:Product')
            ->find($id);

        if (!$product) {
            throw $this->createNotFoundException('No product found for id '.$id);
        }

        return $this->render('AcmeWebSiteBundle:WebSite:product.html.twig', array(
            'product' => $product,
            'base_template' => $this->container->getParameter('red_kite_labs_theme_engine.base_template'),
        ));
    }
}

Add the template

Create a new WebSite folder under the bundle’s views folder and add a new product.html.twig template under it. Open that template and enter the following code:

// src/Acme/WebSiteBundle/Resources/views/WebSite/product.html.twig
{% extends 'AcmeWebSiteBundle:' ~ environment_folder ~ ':' ~ app.request.get('_locale') ~ '/base/empty.html.twig' %}

{% block page_title %}
    <h3>{{ product.getName }}</h3>
{% endblock %}

{% block content %}
<table>
  <tr>
    <td>Description</td>
    <td>{{ product.getDescription }}</td>
  </tr>
  <tr>
    <td>Price</td>
    <td>{{ product.getPrice}}</td>
  </tr>
</table>
{% endblock %}

The template extends the empty.html.twig base template generated by RedKite CMS, and overrides two of its blocks: the page_title block, where the product name is displayed, and the content block which contains the product details.

You may have noticed that the extends instruction contains the environment_folder variable which handles the current environment. In fact, RedKite CMS can deploy the website for the stage and/or production environment. In this way the template is “environment agnostic”.

The environment_folder has not been declared yet: let’s do that now.

Open the controller and change it as follows:

// src/Acme/WebSiteBundle/Controller/WebSiteController.php
    [...]
    use RedKiteLabs\ThemeEngineBundle\Core\Rendering\Controller\FrontendController;

class WebSiteController extends FrontendController
{
    public function productAction($id)
    {
        $product = $this->getDoctrine()
            ->getRepository('AcmeWebSiteBundle:Product')
            ->find($id);

        if (!$product) {
            throw $this->createNotFoundException('No product found for id '.$id);
        }

        return $this->render('AcmeWebSiteBundle:WebSite:product.html.twig', array(
            'product' => $product,
            'base_template' => $this->container->getParameter('red_kite_labs_theme_engine.base_template'),
            'environment_folder' => $this->getEnvironmentFolder(),
        ));
    }

    protected function getEnvironmentFolder()
    {
        return strpos($this->container->getParameter('kernel.environment'), 'stage') === false ? $this->container->getParameter('red_kite_labs_theme_engine.deploy.templates_folder') : $this->container->getParameter('red_kite_labs_theme_engine.deploy.stage_templates_folder');
    }
}

The new getEnvironmentFolder protected method has been added and called in the template parameters definition.

Dispatch the events

In the chapter How to change a content at runtime you learned how to replace the content on a slot, implementing a listener.

FrontendController dispatches the events these listeners respond, just before returning the Response.

When you create a custom controller, you should always dispatch these events to avoid bad surprises when you use some listeners.

To achieve this task, you just need to call the dispatchEvents method, which dispatches the events and returns back the modified Response or the same when any listener exists.

Here is the code:

// src/Acme/WebSiteBundle/Controller/WebSiteController.php
class WebSiteController extends FrontendController
{
    public function productAction($id)
    {
        [...]

        $response = $this->render('AcmeWebSiteBundle:WebSite:product.html.twig', array(
            'product' => $product,
            'base_template' => $this->container->getParameter('red_kite_labs_theme_engine.base_template'),
            'environment_folder' => $this->getEnvironmentFolder(),
        ));

        return $this->dispatchEvents($this->container->get('request'), $response);
    }
}

Deploy your website

Since now, if you have not deployed the website yet, the base templates have not been created. So now we are going to do that.

To deploy the website for the stage environment simply open the toolbar and click the Deploy stage button.

Note

Deploying for the stage environment to develop this new page keeps you safe from adding something under development, whilst in production.

When RedKite CMS completes the deploying operation, open a new tab in your browser and enter in the stage dev environment: http://localhost/stage_dev.php/en/product/1

Note

Obviously you will need to have at least one record in your table to see the page correctly rendered, otherwise you get an exception.

Conclusion

The result does not look impressive, but this is not the purpose of this tutorial.

You have now learned how to add a custom route to a website powered by RedKite CMS and how to manage data from a database, using Doctrine ORM instead of Propel.

Found a typo? Is something not correct in this documentation? Just fork and edit it!

Fork me on GitHub