Leverage Symfony2 extra features to simplify controller actions

This post presents two simple yet underused Symfony features that make controller action code shorter and simpler to understand.

This post presents two simple yet underused Symfony2 features that make actions code shorter and simpler to understand:

  • The @Template annotation
  • Action parameters conversion (optionally used with the @ParamConverter annotation).

We will see step by step how to simplify a generated CRUD controller to leverage these features.

Doctrine filters will be introduced in an upcoming blog post to make things even easier and more reliable in some use cases.

Symfony2 comes with the SensioGeneratorBundle bundle, adding utility commands for generating pages based on a Doctrine schema. A CRUD (Create, Remove, Update, Delete) can be generated from a given Doctrine entity. This will generate a simple structure composed of a controller with all 4 actions + their associated routes + templates + a form type associated to the entity.

This generated structure will be the base of the simplification process introduced in this post.

Generation of a CRUD

Let’s say that a Post Doctrine entity has already been defined in an AcmeBlogBundle.

Generate the associated CRUD:

app/console generate:doctrine:crud --entity=AcmeBlogBundle:Post --with-write

More information on this in the documentation

Use of @Template

Note that the @Template() annotation is automatically generated by the SensioGeneratorBundle with the annotation format. However, the @Template() annotation can be used for your other actions.

Considering the following action:

/**
 * ...
 */
public function showAction($id)
{
    // ...

    return $this->render('AcmeBlogBundle:Post:show.html.twig', array(
        'entity' => $entity,
    ));
}

Using the @Template() annotation, it will become:

/**
 * ...
 * @Template()
 */
public function showAction($id)
{
    // ...

    return array(
        'entity' => $entity,
    );
}

This is much more convenient as:

  • The bundle name and the template name don’t need to be explicitly given. No need to change them should the action or bundle be renamed.
  • No more direct call to $this->render(), making things easier to read.

Don’t forget to add the following use declaration at the beginning of the controller:

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;

The @Template() annotation can take some parameters to define the rendered template name. More information on http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/view.html

The @ParamConverter annotation

The @ParamConverter annotation is a real time saver. Most of the time, it does not even need to be explicitly added as an annotation and will work automatically out of the box.

Before using the ParamConverter feature:

/**
 * Finds and displays a Post entity.
 *
 * @Template()
 */
public function showAction($id)
{
    $em = $this->getDoctrine()->getManager();

    $entity = $em->getRepository('AcmeBlogBundle:Post')->find($id);

    if (!$entity) {
        throw $this->createNotFoundException('Unable to find Post entity.');
    }

    return array(
        'entity' => $entity,
    );
}

After:

/**
 * Finds and displays a Post entity.
 *
 * @Template()
 */
public function showAction(Post $post)
{
    return array(
        'entity' => $post,
    );
}

Isn’t that simpler?

This is really useful as:

  • “Not found 404″ pages are automatically handled: no more need to check the entity existence and throw a new “not found” exception.
  • There’s no more dependance to Doctrine in the controller code
  • As a result, code is much easier to read

The ParamConverter simply uses the route parameter (generally the id, but it could be a slug of any kind) and searches for an existing entity using its primary key.

Would you want to use an any other entity property for searching the entity in the database, this can be done with parameters of the @ParamConverter annotation.

For more information, have a look at the documentation.