A symfony1.4 to Symfony2 migration: Why you should learn Symfony2

A symfony1.4 to Symfony2 migration: Why you should learn Symfony2

Since a few months all the PHP developer’s community is quite in turmoil about everything that’s going on. PHP 5.3 is becoming very standard, Zend is in heavy development for his new version and Symfony2 is on the point to release his final version.

What’s happening?

Before going further, I must make a quick overview of what’s happening in the world of PHP developers.
The news coming frameworks are reaching a new step in professionalization of PHP developments. And this one is very huge! The provision of modern design patterns such as SOC (separation of concerns), DI (Dependency Injection) or Annotations (even if it not really a design pattern) pulled the Frameworks to new high quality standards. Note that Java and Microsoft worlds joined this state years ago, PHP is just catching up.

And this catching up is really fast! Essentially thanks to GitHub which allows the lead development team to be easily helped by thousands of voluntary developers all around the world. Really, GitHub community amazes me every day.

Consequently, new generation PHP Frameworks relegate past frameworks to an old way of developing. “Yea my Framework does MVC! So don’t I have some kind of a modern architecture?” Of course not! I am quite bored of this excuse which leads to poor development quality. Take for example the Symfony1.4 Framework. It uses obviously MVC, but outside of the views and controllers all the rest stays messy.

What I realized last months is that PHP development became a language where you can really develop an expertise based on framework but also on architecture and best practices. And it feels right. You cannot tell anymore that you master PHP just because you understand the language and a Framework.

Fact: PHP is becoming a very professional and valuable technology, just next to Java and .NET.

PHP and some"modern" Frameworks
PHP and some"modern" Frameworks

Learn a new Framework

What I did in the beginning of 2011 is the migration a full featured website from symfony1.4 to Symfony2.

First of all, I must say that symfony1.4 is a tremendous Framework. It is reliable and well-conceived. I learned a lot using it. If you like it and are using it on current projects, you should definitely keep it.
But when I saw Symfony2 at the end of 2010, I couldn’t help myself but to be thrilled about it. Symfony1.4 is nice, but Symfony2 is so much better!

You have several advantages to learn and use Symfony2. You will learn and use new philosophies and best practices that are very valuable. Functionalities and performances are better. Team development is enforced with a much decoupled framework. And you can easily leverage the GitHub community which tests and improve the Framework every day or provided Bundles for every need.

You should learn Symfony2, but the road is not easy.

Symfony1.4 to Symfony2

Yes, the architecture is completely different. And despite the fact that the name stays the same, you will have to recode everything again. This is why we advise to keep current symfony1.4 project the way they are.

Of course, you will get some kind of a step by step tutorial to migrate the code. But at the end you will recode everything.

The general architecture

The main difference is that this time you won’t code into the framework (remember the model directory for example). You will have to create a Bundle.
A Bundle is some kind of a library that has the ability to be plugged into Symfony2. Bundles are very independent one from another and are at the core of your application. Note that Symfony2 is itself built on Bundles. Also, old plugins are now Bundles.

A Bundle could be your model, your frontend, a web service, a template engine, an ORM, a search engine, an integration of TinyMCE… so Bundle is a very large notion in the Symfony2 framework. The main interest it that they can be very easily plugged, updated and configured.

It took me several months to fully understand what the implications of using Bundles are. But once you’ll get it, you won’t want to leave it.
Also Symfony2 relies on the concept of dependency injection. It is very important to understand why it is build this way, what the container is and what it implies. When you’ll know how it works, the Framework will become very easier to understand.

Doctrine2

Doctrine2 works differently.

The first thing you’ll notice is that Doctrine does not rely on code generation. Now you will have to code your objects yourself. Hopefully the command can help you to build your first objects, but you should rapidly code your files directly.

Consequently, your objects are not using any base class. They are lighter but they don’t have any access to the ORM, and should not contain the saving logic anymore.
You might also use annotation, which are very powerful and have the merit to explicitly set relationships.
What I like with Doctrine2 is that your model is now very light and explicit. You won’t have the WTF effect any more when a relation or column is not loaded/stored correctly because of a wrong “schema.yml” file.

The Controllers

Controllers may contain the same kind of logic; the way they work is different. Now you have to consume services. And so you will learn the notion of services and begin to use one of the core items of the Symfony2 architecture: the container. No more static classes, you will rely on real instances that are parameterized via all the dependency injection and services logic.

Really, services, dependency injection and the container are very nice things. They allow your code to be more decoupled and more robust.

The views

Twig is one of the most powerful stuffs in Symfony2. You may don’t want to use it, but I strongly encourage you to do so.

The syntax is less verbose, and you can have a lot of nice functionalities. Auto escaping, easy access to variables, template inheritance, easy test structures (which can be boring in php, such as testing the existence of a value, or test even/odd occurrences in a loop), filters (truncate, text replacement, capitalization)… and so on.

Twig is a language dedicated to designers, so that you won’t have to teach them PHP.

Also Twig will impose you to have only view logic in your view. And that is great! You designer can concentrate on creating a nice interface without messing with your business logic.

The routing

Really, the routing looks like to be the least changed component. It became a little simpler than before, can be used with annotations and has some nice new functionalities such as the param converter.

Forms

Forms completely changed. It is a brand new form Framework which works like a mix of MVVM and Converter design patterns (very common in .NET). You will have to go through all your forms in terms of composition, validation, and process.

As far as I can see, the new Form framework is not so easy to master. It is also one of the last stable components in Symfony2. As long as I can see, when it comes to functionalities, there is no major improvement.

But when you get how to use them, they become very powerful and fast to use. Especially thanks to the validation that can use annotations, and the very clean process of form construction.

Security

The security component is certainly the hardest to master. The first time I had to use it, it was very painful. You will have to understand many new concepts, such as the provider, the firewalls, the encoders…
But you will have a very powerful tool to really control every aspect of your web application security and authorization mechanisms.

I18n

The translation of your views is very clean and perfectly integrated in the twig syntax. The forms won’t get you any trouble.

But there is no out of the box process for the translation of your doctrine object. You will have to install the doctrine extensions, which are kind of official and not very hard plug, in order to support this feature.

On the bright side you can now have your translations in xliff files, but also php, yaml, and more.

Extending the framework

This last point is certainly one of the most important. Because complete parts of the framework are Bundles, it is very easy to build new bundle and makes them do whatever you want! Also it allows you to override very easily the way the framework behaves.

Because Bundles are kind of standalone, you can share them and plug them very easily. You can already find very popular bundles with very powerful stuffs in it. I am thinking about the KnpUserBundle, the SonataAdminBundle, the StofExtensionBundle or the ImagineBundle.

What will happen?

The first thing you might want to do is to download the Symfony2 package, configure your web server, and see what comes out. But it is not so easy. You first have to choose a distribution, get the “vendors”, maybe by using git…

Just after that you will go through the doc, and see that you have so many things to read, with some kind of an arbitrary order. Also they are a lot of cookbooks that contains so many features to discover. You will just want to know everything in order to make the right decisions. But you will still want to code.

You will be overwhelmed by the Framework and feel kind of lost. Almost I did.

You will “loose” time reading doc, learning new ways of doing stuffs that were working perfectly on your last framework. You will be stuck again common issues, and some other tricky functionalities will work very smoothly. At last you will learn to watch Github regularly to follow the development of some bundles, read the doc hosted there, and so on. You might not want to do that at the beginning (it is not what you came for)… but you will do.

The truth is that your first Bundle (application) will be crappy. Sure it will work, but you’ll be frustrated to miss some new features along the way. So you might refactor several times before getting all the potential of Symfony2.

During my migration I did not just recode everything. I had to refactor my forms two times, my security and the import of the assets one time. My controllers were permanently recoded to follow these changes. And so on.

The same thing happened to me when I discovered Symfony1.4. But, as always, you will feel some kind of betrayed by the Framework. You will have to overcome many frustrations and disappointments. And it might last for some months, but you had the strength to learn symfony1.4 so the learning of Symfony2 will be incredibly fast!

After all you will be a kid again, playing with big toys. You will surely miss your old ones, miss some functionality. But when you will know how to use them, they will become very pleasant tools.

The change curve
The change curve

What you’ll miss

Symfony2 is not so extraordinarily good. It is still very young, and it is thus missing some functionalities that symfony1.4 has.

You can forget about the admin generator for the moment. Symfony2 should get one, but not now. You can still compensate by using the SonataAdminBundle or the WhiteOctoberAdminBundle.

The Framework is too much flexible at first. You can do almost any file architecture. You can use xml, yaml, php or annotation as configuration. It seems nice, but sometimes it is kind of confusing to have a permissive Framework, especially after using symfony1.4.

No Jobeet. No Jobeet. No Jobeet. Sadly it is not coming soon. I have seen some projects to migrate the Jobeet project to Symfony2, but it is not very conclusive yet.

The magic. You will have to code your assessors yourself (or use a helper command), you will have to explicitly route your controllers and templates…

What you’ll like

The performance! Nothing more to say.

The new debug toolbar which is more powerful.

The documentation. It has no Jobeet, but it becomes more complete every day and you won’t have a specific documentation for the 1.0 to 1.4/Doctrine/Propel versions of the Framework. It wasn’t the case a month ago, but now the Symlfony2 documentation is more complete that the Symfony1.4 one. Thanks to the documentation team and the Github contributors that do a tremendous work.

Everything is explicit. “No more magic” can look like a nice easy thing to say, but is a very apparent and useful. Now the templates are called because you wrote it, your assessors are explicitly written, logic is not hidden into base classes that belong or are generated by the Framework, your configuration is loaded because you referenced it… No more “finger crossed” effect.

The community. The Symfony community has never been so active. You have lot developers ready to help you whatever your problem is. Moreover your will find a lot of nice Bundles on Github provided by the community.

You will learn a lot! You will use valuable practices and standards. You will build powerful, robust and nice web applications. And finally you will become a better developer than ever.

Some extras

Of course Symfony2 comes with some killers features. Let’s talk about some of these.

The management of assets is incredibly neat. The pre-packaged AsseticBundle can handle in a very elegant way all your assets. They can be images, css or js. But it can also compile on the fly coffee script, sass, lesscss or more, minifies tem, combine them and cache them!

You can do more by using the ImagineBundle that will handle your images. It will generate thumbnails, apply filters or watermark on the fly depending on what you ask in your templates. And it is cached.

Yes, my designer is very happy!

The cache has never been so powerful. Symfony2 uses the http headers (what a weird nice idea) coupled with a reversed proxy to reduce drastically what your application need to compute. Also it integrates in an almost transparent way the handling of ESI cache (if you don’t know it, you should at least look at this: http://en.wikipedia.org/wiki/Edge_Side_Includes).

Functional and unit tests are completely integrated into phpunit so that you can leverage a true PHP unit test framework.

Because everything is decoupled in bundles and only accessed via the container, your classes are loaded just when you use them. And it results in a very lightweight Framework compared to what symfony1.4 was.

The management of updates. It was not so easy at the beginning, but the core development team came with a script that can update your entire framework and dependency bundle just by reading a “deps.ini” file. Because your code is in a completely isolated Bundle you won’t have to do more than update the “deps.ini” file and run a simple command (which wasn’t really the case with symfony1.4 where your code were placed everywhere in the Framework).

What did I get?

I already told some of the reason why you should learn Symfony2. But there is more to say.

Again, Symfony2 will teach you many new nice things. You will learn. You will be a better developer than ever. And you will be more valuable.

Also your application will become much more powerful, fast and reliable. And that not nothing! I never felt so confident about my development and my websites.

At last you might enjoy it! Many new features of Symfony2 allow you to do so powerful stuffs without all the boring code. I am thinking about ESI, Assets management, Twig, Annotations, Bundles…

At the beginning you will hate learning Symfony2, but at last you won’t regret it.

Advertisements

Comment déployer une application symfony2 sur OVH

Comment déployer une application symfony2 sur OVH

Exceptionally, this article is in French. I’ll talk about how to deploy a symfony2 application on an OVH server, which is a french webhost.

J’ai récemment déployé mon nouveau projet symfony2 sur mon serveur mutualisé OVH. Et c’est après quelques petites astuces que j’ai pu faire marcher tout cela sans heurt.

La première chose à savoir sur un serveur OVH, c’est comment changer la version de php. En effet la version par défaut est la 4.4.9 au moment où j’écris ces lignes, alors que symfony 2 requiert au mois la version 5.3.2.

Pour avoir la dernière version de php en ligne de commande : [cci]php.TEST.5[/cci]

Et pour activer la dernière version de PHP pour apache, mettez en tête du .htaccess :

[cc]
SetEnv REGISTER_GLOBALS 0
SetEnv MAGIC_QUOTES 0
SetEnv ZEND_OPTIMIZER 1
SetEnv PHP_VER 5_TEST
[/cc]

Comme vous pouvez le voir, j’ai rajouté quelques lignes pour avoir une configuration plus propre.

Ensuite, si comme moi vous développez en environnement Windows, vous allez avoir des erreurs signifiant que symfony n’arrive pas à charger certaines classes. Cela vient probablement du fait que vous avez oublié de respecter la case dans le nommage de vos répertoire ou bien dans le fichier [cci]/app/autoload.php[/cci].

Finalement, lors de l’utilisation de la commande [cci]assets :install web[/cci] pour le déploiement de vos ressources (images, swf…), la commande risque de vous signifier que le répertoire [cci]web[/cci] n’existe pas. Pour cela vous devez pointer vers le répertoire avec un chemin relatif au répertoire [cci]/app/[/cci]. Ce qui nous donne : [cci]assets :install ../web[/cci].

Et voilà, normalement tout fonctionne. En cas de soucis, n’oubliez pas de vérifier la configuration de la connexion à votre base de données dans le fichier [cci]/app/config/parameters.ini[/cci] et d’effacer le répertoire [cci]/app/cache/prod/[/cci].

Note : A propos de php, il est toujours possible d’avoir la liste des versions disponibles en faisant [cci]ls -l /usr/local/bin/php*[/cci] depuis la ligne de commande.

EDIT: la dernière note est devenue obsolète. De plus php.TEST.5 n’est plus utilisable, on peut maintenant utiliser php.ORIG.5.3.2.

Symfony2 : More about the ExtraToolsBundle

Symfony2 : More about the ExtraToolsBundle

Following some of my previous posts A translation message extractor command and Create your own constraint validator in symfony2 : A Doctrine unique validator, these two handful functionalities are available on Github in the ExtraToolBundle.

Please, fork me 😉

Also, both functionalities have been strongly improved since I have blogged about them.

The Doctrine UniqueValidator, is now much more configurable and have been updated to support the new namespace annotation injection. And, thanks to Matt Agar the trans:udpate command is now much more robust and supports export to xliff and php translation files. He did a very nice job and it was very nice to have another developer to challenge and contribute to my implementation.

You can find the full details of the new tools on the ExtraToolsBundle’s github.

Symfony2: A translation message extractor command

Symfony2: A translation message extractor command

One of the very painful taks you may face using symfony2 is the extration of all you translation message from your twig templates. This is much more annoying knowing that symfony1.4 did the job for you with a simple command, which does not exist in symfony2.

Today I will give give you a command for symfony2 that checks all your twig messages, combine them with your already existing messages in your yaml translations files and save the new ones. It is a recent work for me and it just works with twig/yml files.

The Command
I embedded it in a Bundle on github: https://github.com/michelsalib/ExtraToolsBundle.

You just need to get it, register the namespace and the bundle.

The name of the command is [cci]bcc:trans:update locale bundleName[/cci], where the [cci]locale[/cci] is the targeted locale (en, fr, es…) and the [cci]bundleName[/cci] is the name of the targeted bundle. You have several options:
– –dump-messages to display your final messages
– –force to update/write your translation files, it also perform a backup of the old ones
– –prefix=”…” if you want to change the prefix use for your new messages, by default [cci]__ [/cci] is used

Some example:
– To extract the messages of your bundle and display them in the console:
[cci]bcc:trans:update –dump-messages fr MyBundle[/cci]

– You can save them:
[cci]bcc:trans:update –force fr MyBundle[/cci]

– In another language:
[cci]bcc:trans:update –force en MyBundle[/cci]

– Or if you want to chaneg the prefix used to generate the new messages:
[cci]bcc:trans:update –force –prefix=’myprefix’ en MyBundle[/cci]

Behind the scene
The trick behind the code is how to properly crawl your twig templates.

First to get them:
[cc lang=”php”]
// get bundle directory
$foundBundle = $this->getApplication()->getKernel()->getBundle(‘MyBundle’);
// get twig templates
$finder = new Finder();
$files = $finder->files()->name(‘*.html.twig’)->in($foundBundle->getPath() . ‘/Resources/views/’);
// iterate over the files
foreach ($files as $file) {
$path = $file->getPathname();
// parse the files
}
[/cc]

Here you just need to use the kernel to retrieve the bundle data from a simple bundle name. When you have the bundle path, you can use the finder to get all the file ending by [cci].html.twig[/cci] in the relative [cci]/Resources/views/[/cci] directory.

You can now parse the twig files:
[cc lang=”php”]
$tree = $twig->parse($twig->tokenize(file_get_contents($path)));
[/cc]

Then you get a tree composed of [cci]Twig_Node[/cci]. The rest is just an algorithmic problem using recursion and type checking to find [cci]SymfonyBridgeTwigNodeTransNode[/cci] (for [cci]{% trans %}…{% end trans %} syntax[/cci]) and [cci]Twig_Node_Print[/cci] that contains trans filter (for [cci]{{ … | trans }}[/cci] syntax).

When you finally have all your messages, you might want to save them into yaml. For that you have two very simple static functions:
[cc lang=”php”]
// get a yaml file into a php array
$array = SymfonyComponentYamlYaml::load($path);
// transform an array into a yaml string
$yml = SymfonyComponentYamlYaml::dump($array);
[/cc]

Wrap up
I hope this command will help you building better symfony2 app, by automatizing some work. Don’t hesitate to report me bugs, suggestions or to fork me on github!

Programmatically authenticate the user in symfony2

Programmatically authenticate the user in symfony2

I’ll just share here a quick snippet that serves to pragmatically authenticate the user in Symfony2.

It is very useful, especially after checking an email account using a token for example. In this case you might don’t want to ask again the password to your user and do the authentication for him.

What you need to do is to manually create the AuthenticationToken and give it to the security context:

// create the authentication token
$token = new UsernamePasswordToken(
	$user,
	null,
	'main',
	$user->getRoles());
// give it to the security context
$this->container->get('security.context')->setToken($token);

Note that the third parameter of the token constructor is the name of the security provider associated with your user found in your app/config/security.yaml file.

Create your own constraint validator in symfony2 : A Doctrine unique validator

Create your own constraint validator in symfony2 : A Doctrine unique validator

The new symfony2 form framework is definitely one of my favorites. It is quite simple, and very powerful and straightforward. Moreover it is very extensible.

One of the most common tasks you need to perform when validating a form is to check the unity of a field in your database. Unfortunately, there is no build-in validator coupled with doctrine. Here is my implementation.

Create the unique validator

The first thing to do when implementing a new validator is to create the constraint class:
[cc lang=”php”]
namespace MyAppMyBundleValidator;

use SymfonyComponentValidatorConstraint;

class Unique extends Constraint
{
public $message = ‘This value is already used’;
public $entity;
public $property;

public function validatedBy()
{
return ‘validator.unique’;
}

public function requiredOptions()
{
return array(‘entity’, ‘property’);
}

public function targets()
{
return self::PROPERTY_CONSTRAINT;
}
}
[/cc]

As you can see, this definition is pretty simple. We define the error message and two more required options which are the Entity that doctrine with check and its property. The validatedBy() method returns the service’s name that is in charge to validate the constraint (we will declare it just after). Finally, the targets() function limits the use of the constraint only to class’ property.

Now that our constraint is set, let’s create the constraint’s validator:
[cc lang=”php”]
namespace MyAppMyBundleValidator;

use DoctrineORMEntityManager;
use SymfonyComponentValidatorConstraint;
use SymfonyComponentValidatorConstraintValidator;

class UniqueValidator extends ConstraintValidator
{
private $entityManager;

public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}

public function isValid($value, Constraint $constraint) {
// try to get one entity that matches the constraint
$user = $this->entityManager->getRepository($constraint->entity)
->findOneBy(array($constraint->property => $value));
// if there is already an entity
if($user != null){
// the constraint does not pass
$this->setMessage($constraint->message);
return false;
}
// the constraint passes
return true;
}
}
[/cc]

The main function here is isValid(). It checks the validity of the constraint and consequently returns true/false and may add some error message. Here our job is to check is the value is already used in the database. The simplest way to do that is to try to get one matching entity. If Doctrine finds an entity the constraints does not pass, if there is no entity, the value can be used and the constraint passes.

One thing to know is that the Unique constraint instance is pass to the isValid() function, so that we can get the entity et property values.

Finally, our UniqueValidator has a dependency to the Doctrine manager. We will need to declare it in our services:

[cc lang=”yaml”]
# MyApp/MyBundle/Resources/config/services.yml
parameters:
my.validator.unique.class: MyAppMyBundleValidatorUniqueValidator

services:
my.validator.unique:
class: %my.validator.unique.class%
arguments: [@doctrine.orm.entity_manager]
tags:
– { name: validator.constraint_validator, alias: validator.unique }
[/cc]

We defined here the way to instantiate our UniqueValidator with its dependencies (arguments line). The tag line is very important. The name specifies that it is load as a constraint validator, and the alias name is used to link the constraint validator to its constraint (remember the Unique::validatedBy() function).
One last thing is to load our services.yml file. We can do it in the dependency injection configuration :
[cc lang=”php”]
load(‘services.yml’);
}

public function getAlias() {
return ‘my’;
}
}
[/cc]

The load() function is launched at the configuration of the container. Here we simple retrieve the services.yml file and load it to the current configuration.

That’s it you know have a working constraint that you can use to check the unity of a field at form’s submission. Don’t forget to set the entity and property values.

Support annotation

Actually I prefer setting my constraints using annotations. To do so, we will need some more work.

Let’s say we want to use our constraint like that:
[cc lang=”php”]
/**
* @orm:Entity
*/
class User{
/**
* @orm:Column(length=255, unique=”TRUE”)
* @myvalidation:Unique(entity=”MyBundle:User”, property=”username”)
*/
protected $username;

//…
}
[/cc]

It won’t work right away. This is because the myvalidation is a shortcut to a namespace (just like validation or orm). Because we did not declare this shortcut, symfony cannot find the Unique class and simply ignores it.

To declare the myvalidation shortcut you can do it directly in the app/config/config.yml. I chose to do it in the bundle because I don’t like to play so much with the app configuration. One of the benefits of bundle is that they decoupled the application, so we don’t want our general configuration to become dependent to our bundle.

To declare the shortcut in our bundle we can do it in the dependency injection configuration:
[cc lang=”php”]
load(‘services.yml’);

// get the existing registered namespaces for validator annotations
$namespaces = $container->getParameter(‘validator.annotations.namespaces’);
// add our namespace under the alias myvalidation
$namespaces[‘myvalidation’] = ‘MyApp\MyBundle\Validator\’;
// save it
$container->setParameter(‘validator.annotations.namespaces’, $namespaces);
}

public function getAlias() {
return ‘my’;
}
}
[/cc]

What we need to do is to retrieve the namespace aliases of the validators and then add our alias. Don’t forget the trailing backslashes.

And voila! Our Unique constraint works also with annotations.

I hope that this tutorial will help you to build new powerful constraints and share them to the community.

Advance customization of the 403 error page in Symfony2

Advance customization of the 403 error page in Symfony2

If you currently are testing the brand new Symfony2 framework, you might have tried to customize the 403 page. Actually, you have two documented ways to do so, first by overriding the default exception template, second by listening to the onCoreException event and filtering the right exception.

Unfortunately, neither of these two methods satisfied me. The simple customization of the template was not enough for my needs and the addition of an ExceptionListener impose a “maybe” to much complex filtering in order to retrieve just the 403 exception.

Note: Actually, my needs are similar to those I exposed in this blog post on symfony 1.4 : Symfony 1.4 vs. sfGuardUserPlugin : understand credentials and permissions.

What I want to do is to get the unsatisfied requirements and display them to the user.

First secure an action
In order to secure the access to my action with specific requirements, I use the @Secure annotation on my controller’s method. The annotation is provided by the JMSSecurityExtraBundle included in the symfony2 standard edition:

[php]
// /src/MyVendor/MyBundle/Controller/MyController.php

/**
* …
* @extra:Secure(roles="ROLE_MEMBER")
*/
public function newAction()
{
// …
}
[/php]

Here in order to access my action, the user will need to have the role named ROLE_MEMBER. If not, the security layer will throw an Exception resulting in a 403 response.

Change the unauthorized action
You can change the action that will display the 403 page in the security configuration:

[yml]
# /app/config/security.yml
security:
access_denied_url: /unauthorized
[/yml]

Now when dealing with an unauthorized Exception, the security layer will call the action associated to this URL. You need to specify the action that matches this URL in your routing configuration:

[yml]
# /src/MyVendor/MyBundle/Resources/config/routing.yml
unauthorized:
pattern: /unauthorized
defaults: { _controller: FrontendBundle:Default:unauthorized }
[/yml]

Now, the action responsible for rendering a 403 page is the following:

[php]
// /src/MyVendor/MyBundle/Controller/DefaultController.php

class DefaultController extends Controller
{
// …

/**
* @extra:Template()
*/
public function unauthorizedAction()
{
return array();
}
}
[/php]

And the associated template:

[html]
{# /src/MyVendor/MyBundle/Resources/views/Default/unauthorized.html.twig #}

403 error: Unauthorized

[/html]

Display the unmatched requirements
Now, the last things I want to do are to retrieve the required roles of the original request and display them to the user, so that he knows why his request did not succeed.
The tricky now is to obtain the original request, deduce the controller, load its annotations and thus deduce his requirements. Here is what I did:

[php]
// /src/MyVendor/MyBundle/Controller/DefaultController.php

public function unauthorizedAction()
{
// get the previous requests
$requests = $this->container->getCurrentScopedStack(‘request’);

// get the previous controller
$controllerResolver = new SymfonyComponentHttpKernelControllerControllerResolver();
list($controller, $method) = $controllerResolver->getController($requests[‘request’][‘request’]);
// isolate the method
$method = new ReflectionMethod($controller, $method);
// load the metas
$reader = new JMSSecurityExtraBundleMappingDriverAnnotationReader();
$converter = new JMSSecurityExtraBundleMappingDriverAnnotationConverter();
$annotations = $reader->getMethodAnnotations($method);
$metadata = $converter->convertMethodAnnotations($method, $annotations)->getAsArray();
// isolate the required roles
$requiredRoles = $metadata[‘roles’];

return array(‘requiredRoles’ => $requiredRoles);
}
[/php]

The first thing I do is to obtain from the container the request stack that contains the original request. From the request I use the ControllerResolver in order to retrieve the initial controller and then isolate the method. With this method, I can use the AnnotationReader and AnnotationConverter from the JMSSecurityExtraBundle and get back the required roles.
Despite the fact that the method is pretty straightforward, it is not as elegant as I wanted it to be (in the whole process, the controller resolving and annotation extraction will be done twice). But it does the job.

The final thing to do is the show the required roles to the user in the template:

[html]
{# /src/MyVendor/MyBundle/Resources/views/Default/unauthorized.html.twig #}

Required roles:

{% for role in requiredRoles %}
{{ role }}

{% endfor %}

[/html]