Where is the model in Symfony2? Entities, Repositories and Services

Without any doubt, the MVC Design Pattern fits perfectly on website development. It is all about the controller deciding where to go according to the Request, the model fetching data and the view rendering HTML / json / xml / whatever sending the Response.

Of course, this can get complicated sometimes (for instance when the data is accessed through a WebService or using HMVC like Kohana does) but at the end it is quite more or less the same. Most PHP (and other languages) frameworks implement the MVC pattern its own way with different flavours, and sometimes this can get confusing.

Symfony2 is not strictly and MVC framework. It is a Request / Response framework. It gives you several tools to handle the request (HTTP classes, Validator Service, Routing Component, etc…) and the response (Twig, Assetic, HTTP Cache, etc…) but the model is just up to you. As a matter of fact, when you create a Bundle folder structure, there is no Model folder. So, how can we proceed?

Another nice tool that comes with a Symfony standard setup is Doctrine2. This big library is both a Database Abstraction Layer (with Doctrine DBAL) and also an ORM on top of the DBAL. The good thing about Symfony2 is that everything can be decoupled, so you can use any system you like. But of course, Doctrine2 is just set up for you and the only thing to be done is to configure the database access, which is much easier than figuring out how to setup any other library. So we will talk about Doctrine2 as a Model Library.

Doctrine Entities

A Doctrine Entity is basically a class that maps a database table to a PHP Object. You can also define how Entities / Tables are related each other (One-To-One, One-To-Many, Many-To-Many), define database restriction rules (Foreign Keys, Indexes, nullable fields, numbers precision) and also define some slight business validation rules through annotations (for instance an Email, an URL, a Credit Card, …).

We can also define some smart getters / setters. One easy example that we use at Ulabox is in the Customer Entity name getter. Firstname and Lastname are not mandatory, so it can happen that we only have the Customer Email as a best Customer naming. So, our Customer -> getName function tries to find a Firstname – Lastname pair but if both fields are null we just return the customer e-mail.

Doctrine Repositories

We use repositories as the place to define methods that perform complex queries using joins and small business logics. For instance, if we have the Group – User relationship, and we list the Users we can get the User Group by performing $user->getGroup() but if we look at the Symfony debug bar we will see that the amount of queries performed grows dramatically. It is much better to create a Users Repository, with a getUsersAndGroups method that retrieves all data with just a single query.

Services creation

One of the most interesting features of Symfony2 is how easy it is to create a Service that can be accessed / injected all over the application extremely easy.

Services can be used to serve many purposes like: code advanced business logic that does not fit in Entities / Repositories, access external or 3rd party APIs, split application into many small pieces of code that perform a task, … so to sum up they help you build a service layer that helps you decouple your code and also makes it easier to mock and unit-test them.

To illustrate this, I will show some code we use in Symfony-Barcelona web application to read data from Sensio Connect JSON Api.

– config.yml

  sensiogroup: symfony-barcelona

– services.yml

            class: SFBCN\WebsiteBundle\Service\SensioConnectService
                - [setGroupName, [%sensiogroup%]]

With just these 2 small YML code we are defining a constant meaning the Sensio Connect group name and a service called sensio_connect, that is coded in SFBCN\WebsiteBundle\Service\SensioConnectService and even more interesting we are telling Symfony2 to execute setGroupName method injecting sensiogroup constant (previously defined) when the service is first requested. Isn’t it really cool? This can be seen as the evolution of the good old XXXModel::getInstance()->init() but far more powerful.

And how can this be coded?

– The service

// Service Code
namespace SFBCN\WebsiteBundle\Service;
class SensioConnectService
    protected $groupName;
     * Sets configured RSS Feeds (@see services.yml)
     * @param string $name
    public function setGroupName($name)
        $this->groupName = $name;
     * Gets injected RSS Feeds
     * @return array
    public function getGroupName()
        return $this->groupName;
     * Gets Symfony-Barcelona info from Sensio Connect. Info is stored in APC during an hour to increase speed
     * @return array
    public function getGroupInfo()
        $sfConnect = json_decode(file_get_contents('https://connect.sensiolabs.com/club/' . $this->getGroupName() . '.json'), true);
        return $sfConnect;

And how can use our service from a controller or wherever?

– Controller using the service

namespace SFBCN\WebsiteBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Response;
class DefaultController extends Controller
     * Shows Sensio Connect Content
     * @return \Symfony\Component\HttpFoundation\Response
     * @Route("/sensio-connect", name="sensio_connect")
    public function sensioReadAction()
        $sfConnect = $this->get('sensio_connect')->getGroupInfo();
        return new Response(var_export($sfConnect, true));

Of course this should be sent to Twig to give a nice response but I think we can see the whole point of it.

You can see the full application code on Github or visit us at www.Symfony-Barcelona.es if you want to have a look at how we use this information 🙂

Of course this is an easy example, more complex services example, testing and business logic creating coming in the following posts!

You may also like...