Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Dynamic Roles

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

Denying access is great... but we still have a User class that gives every user the same, hardcoded role: ROLE_USER:

... lines 1 - 12
class User implements UserInterface
{
... lines 15 - 46
public function getRoles()
{
return ['ROLE_USER'];
}
... lines 51 - 93
}

And maybe that's enough for you. But, if you do need the ability to assign different permissions to different users, then we've gotta go a little further.

Let's say that in our system, we're going to give different users different roles. How do we do that? Simple! Just create a private $roles property that's an array. Give it the @ORM\Column annotation and set its type to json_array:

... lines 1 - 12
class User implements UserInterface
{
... lines 15 - 40
/**
* @ORM\Column(type="json_array")
*/
private $roles = [];
... lines 45 - 110
}

Tip

json_array type is deprecated since Doctrine 2.6, you should use json instead.

This is really cool because the $roles property will hold an array of roles, but when we save, Doctrine will automatically json_encode that array and store it in a single field. When we query, it'll json_decode that back to the array. What this means is that we can store an array inside a single column, without ever worrying about the JSON encode stuff.

Returning the Dynamic Roles

In getRoles(), we can get dynamic. First, set $roles = $this->roles:

... lines 1 - 12
class User implements UserInterface
{
... lines 15 - 51
public function getRoles()
{
$roles = $this->roles;
... lines 55 - 61
}
... lines 63 - 110
}

Second, there's just one rule that we need to follow about roles: every user must have at least one role. Otherwise, weird stuff happens.

That's no problem - just make sure that everyone at least has ROLE_USER by saying: if (!in_array('ROLE_USER', $roles)), then add that to $roles. Finally, return $roles:

... lines 1 - 12
class User implements UserInterface
{
... lines 15 - 51
public function getRoles()
{
$roles = $this->roles;
// give everyone ROLE_USER!
if (!in_array('ROLE_USER', $roles)) {
$roles[] = 'ROLE_USER';
}
return $roles;
}
... lines 63 - 110
}

Oh, and don't forget to add a setRoles() method!

... lines 1 - 12
class User implements UserInterface
{
... lines 15 - 63
public function setRoles(array $roles)
{
$this->roles = $roles;
}
... lines 68 - 110
}

Migration & Fixtures

Generate the migration for the new field:

./bin/console doctrine:migrations:diff

We should double-check that migration, but let's just run it:

./bin/console doctrine:migrations:migrate

Finally, give some roles to our fixture users! For now, we'll give everyone the same role: ROLE_ADMIN:

... lines 1 - 22
AppBundle\Entity\User:
user_{1..10}:
... lines 25 - 26
roles: ['ROLE_ADMIN']

Reload the fixtures!

./bin/console doctrine:fixtures:load

Ok, let's go see if we have access! Ah, we got logged out! Don't panic: that's because our user - identified by its id - was just deleted from the database. Just log back in.

So nice - it sends us back to the original URL, we have two roles and we have access. Oh, and in a few minutes - we'll talk about another tool to really make your system flexible: role hierarchy.

So, how do I Set the Roles?

But now, you might be asking me?

How would I actually change the roles of a user?

I'm not sure though... because I can't actually hear you. But if you are asking me this, here's what I would say:

$roles is just a field on your User, and so you'll edit it like any other field: via a form. This will probably live in some "user admin area", and you'll use the ChoiceType field to allow the admin to choose the roles for every user:

class EditUserFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder)
    {
        $builder
            ->add('roles', ChoiceType::class, [
                'multiple' => true,
                'expanded' => true, // render check-boxes
                'choices' => [
                    'Admin' => 'ROLE_ADMIN',
                    'Manager' => 'ROLE_MANAGER',
                    // ...
                ],
            ])
            // other fields...
        ;
    }
}

If you have trouble, let me know.

What about Groups?

Oh, and I think I just heard one of you ask me:

What about groups? Can you create something where Users belong to Groups, and those groups have roles?

Totally! And FOSUserBundle has code for this - so check it out. But really, it's nothing crazy: Symfony just calls getRoles(), and you can create that array however you want: like by looping over a relation:

class User extends UserInterface
{
    public function getRoles()
    {
        $roles = [];
        
        // loop over some ManyToMany relation to a Group entity
        foreach ($this->groups as $group) {
            $roles = array_merge($roles, $group->getRoles());
        }
        
        return $roles;
    }
}

Or just giving people roles at random.

Leave a comment!

64
Login or Register to join the conversation
Default user avatar
Default user avatar gstanto | posted 4 years ago

What I had to do to make this work on Symfony 4:

When running migration initially I received this error:

SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'JSON NOT NULL COMMENT '(DC2Type:json_array)'' at line 1

I tried to manually tweak the migration as Karol suggested below, but did not work for me.

I checked Mysql Version (As installed with setting from Ansible tutorial - recommended, btw)
Ver 14.14 Distrib 5.5.59, for debian-linux-gnu (x86_64) using readline 6.3

Then needed to tweak doctrine.yaml


doctrine:
dbal:
# configure these for your database server
driver: 'pdo_mysql'
server_version: '5.5' <-----(preset was 5.7, which has true json column types)


Then it worked fine. Maybe that will save someone the hour I spent figuring it out.

1 Reply

Hey gstanto ,

Thanks for sharing it with others!

Cheers!

Reply
Default user avatar
Default user avatar Steven Carré | posted 3 years ago

Hi,
thanks for this very instructive article.
I have a question: the User entity has a setRoles() method so say if a user has several roles and I just want to remove one, I have to get the roles first and then reset them all after removing the one from the roles array(). Is there a more convenient way to achieve this?
Thank you

Reply

Yo Steven Carré!

> if a user has several roles and I just want to remove one, I have to get the roles first and then reset them all after removing the one from the roles array(). Is there a more convenient way to achieve this?

The way the code is written out-of-the-box, there is not. You have a few options:

1) Say ok! No big deal - I'll just keep my code that does all this work :).
2) Create a new method on User called (for example) removeRole(string $roleName) and move the logic there for finding the role. Basically the same thing... but the code is more centralized.
3) If you want to, you can actually have an entire UserRole entity that you could relate to User and store roles there. Each UserRole would have an id, so you would know exactly which to delete. This might be overkill... to solve the problem ;).

Cheers!

Reply

Hey Ryan,
When loading my fixtures after I added the roles field and migrated then I get this lovely error:
Message: "Warning: Invalid argument supplied for foreach()" ["error" => Symfony\Component\Debug\Exception\ContextErrorExcepti
on { …},"command" => "doctrine:fixtures:load","message" => "Warning: Invalid argument supplied for foreach()"] []. As a result of this nothing gets loaded into my database.

I am using Symfony 3.4

Reply

Hey DaveKnifeman!

Ah man! Let's see if we can debug this. First, if you add a -vvv flag at the end of your command (so bin/console doctrine:fixtures:load -vvv) you will get a stacktrace on your error, which is usually very helpful to see exactly where the error is coming from!

Second, make sure that when you call setRoles() that you're passing it an *array*. I think that you might be setting this to a string. And then inside our setRoles() method in User, we are trying to foreach over that.

Let me know if that helps!

Cheers!

Reply
Marcelo F. Avatar
Marcelo F. Avatar Marcelo F. | posted 4 years ago

Hello ! I
need to sign in with some ROLE, such as ROLE_SELLER_USER, after that, I
need to select a seller from a list and load roles from a Many to Many
table to continue on the next page.
Can you help me ?

Reply

Hey Marcelo,

Do you mean you need to have a user that may impersonate other users? e.g. you have a ROLE_SELLER_ADMIN that can log in as any ROLE_SELLER_USER and do some things on their behalf?

Then this Symfony docs might be useful for you: https://symfony.com/doc/cur...
We also say about impersonation here: https://knpuniversity.com/s...

I hope it helps!

Cheers!

Reply
Marcelo F. Avatar
Marcelo F. Avatar Marcelo F. | victor | posted 4 years ago

Hey Man !

Finally after a little study I was able to solve this problem by creating a Voter with the extractRoles method.

Thanks for help.

Reply

Hey Marcelo,

Glad you got it working!

Cheers!

Reply
Default user avatar

Just a small note: The "json_array" type is deprecated since Doctrine 2.6. You should use "json" instead.

Source: http://docs.doctrine-projec...

EDIT: I actually just tried to use "json" and it does not even work yet, it says the type is not recognized. I don't know :l

Reply

Hey Johan,

Nice notice, thanks! Actually, we require only the latest 2.5 version of doctrine ORM due to the next line in composer.json: "doctrine/orm": "^2.5". So Composer won't use 2.6 for this project even when it'll be available.

But I think we could add a note about it, I'll add it.

Cheers!

UPD: Note added in https://github.com/knpunive...

1 Reply
Default user avatar
Default user avatar Richard Perez | posted 4 years ago

Ryan, what would be the different doing the @Route(/admin) at the top of the controller vs using the firewall? And if in this case there are no different, what is the advantage of using on or other.

Thansk

Reply

Hey Richard,

That's different things. The "@Route" annotation at the top of any controller class is just a prefix for all routes, which this controller has. It means, that if you have:


/**
* @Route("/admin")
*/
class GenusController
{
/**
* @Route("/genus")
*/
public function indexAction()
{
// code here...
}
}

so the `indexAction()` URL will be the "/admin/genus" (i.e. with "/admin" prefix) but not just the "/genus". That's it!

Firewall in turn allows you to configure who has access to a page.

Cheers!

Reply
Default user avatar
Default user avatar Richard Perez | victor | posted 4 years ago

Hi Victor, sorry my bad, What I want to say, was, what is the different about using @Security(/admin) vs using in the firewall ^/admin.

Reply

Hey Richard!

Ah, that makes more sense :). Or, more specifically - you mean @Security("ROLE_ADMIN").

It's more or less perfectly equivalent. The ^/admin in the firewall causes some code to run that compares the URL and if matches, checks for ROLE_ADMIN. If you use @Security, it does the same thing - except instead of checking if the URL matches, it checks to see if that controller class is being executed on this request.

So it comes down to a matter or preference: do you like protecting by URL better? Or do you like to secure specific controller classes. It's nice to have both options :).

Cheers!

Reply
Default user avatar
Default user avatar Richard Perez | weaverryan | posted 4 years ago

Thanks guys, is clear now :)

Regards,

Reply
Default user avatar
Default user avatar 3amprogrammer | posted 4 years ago

Hmm. I was used to roles and permissions structure. I mean, used to have a roles table, permissions table, pivot tables between them, user could have roles but also a independent permissions and so on. I was using https://github.com/romanbic... with Laravel.

How this relates to Symfony? Do groups act as roles and roles become permissions in Symfony world? If a role represents a set of permissions, hard coded in my application (according to: http://stackoverflow.com/qu... ) how am I supposed to associate permissions with roles?

Reply
Default user avatar
Default user avatar julien moulis | posted 4 years ago

Hi, I would like to make the user select only one role. But with the choiceType with multiple false, it gives a string and then a nice error appears, as expected. Do you have a hint? Thx...
By the way as you can guess I'm a newbie

Reply

Hey Julien,

There's a lot of ways to do it. If you're good at JavaScript, you could write a simple JS script which will allow you to select only one checkbox, i.e. reset all checkboxes except the latest. But probably there's another better solution: since UserInterface enforce you to have setRoles()/getRoles(), you can add a new "virtual" ChoiceType form field which name is "role", make it "mapped => false" and add User::setRole() method which will get a role string value, add it to an empty array and set this array to the $this->roles property overwriting the current role. The 3rd is more complex I think, but should work well too - choiceType with multiple false and create a Data Transformer for this field, which will convert single role string to an array and back: array with a single role to the single role string.

Cheers!

Reply
Default user avatar
Default user avatar julien moulis | victor | posted 4 years ago

Hi Victor, tanks for the reply. Until I get your answer I used jquery. But I would like to use the virtual choice type method. But I can it make it... :-(
The roles column in the database is empty.
Where do I have to create the method? In the User entity or in the form?

Reply

Hey Julien,

Let me a bit improve and probably simplify my original idea: if you want to be able to set only one single role for your users, then you don't need User::$roles property at all, you just need the User::$role field. So your User entity may looks like:


User
{
// other properties...

/**
* @ORM\Column(type="string")
*/
private $role;

public function getRole()
{
return $this->role;
}

public function setRole($role)
{
$this->role = $role;
}

// You have to keep this method due to the UserInterface
public function getRoles()
{
return [$this->role];
}

public function setRoles(array $roles)
{
throw new \Exception('You probably never need to call it, but UserInterface enforce you to have this method')
}

// other methods...
}

How does it looks like? I.e. you replace User::$roles property with User::$role one. And then in your form just add a normal field for this $role property, so you event don't need "mapped => false" now.

P.S. Sorry for misleading you in my first reply.

Cheers!

Reply
Default user avatar
Default user avatar julien moulis | victor | posted 4 years ago

Thank you so much. I realized that the pb was not (only) a lack of understanding of symphony, but OOP in general... I have to work more and more. By the way your courses are so great... I spent hours and hours on reading, searching and following different courses... But yours are so well executed and make me feel less dumb :-)!!!

Reply

Hey Julien,

Thank you for the kind words! Btw, we have a separate track for OOP if you're interested in it:
https://knpuniversity.com/t...

Cheers!

Reply
Default user avatar
Default user avatar Daan Hage | posted 4 years ago

He Ryan/Viktor,

A question regarding fixtures. ./bin/console doctrine:fixtures:load gives a fatal once i already have content, because of the foreign key constraints. This happens when it wants to truncate each table.

Is there a way to either:
1) set the order in which tables are truncated?
2) or to execute a query before it runs the purge()-command?

I know i can delete and recreate the entire database, but i find that a pretty lame workaround.

Kind Regards!

Reply

Hey Daan!

This "shouldn't" happen, because the command calculates the correct order that the tables should be truncated. But, it *can* happen if you have some circular relationships (e.g. two tables - or maybe a bigger loop - that each have foreign keys that point back to each other). One way to fix that - if it's appropriate for your app - is to add some @JoinColumn(onDelete="CASCADE") (or SET NULL) functionality, so that when items in one of those relationships is deleted, it cascades in the database.

But, if that's not really appropriate for your app (i.e. CASCADE would be potentially dangerous for that relationship in production), then you *will* need a workaround, as you suggested. There are no hook points that I know of, so I would recommend just making a simple shell script that runs all the commands for you (database:drop, database:create, migrations:migrate, fixtures:load).

Cheers!

Reply
Default user avatar

Aaah to bad. Thanks for the reply. I'll make a bash script then :)

Reply
Default user avatar
Default user avatar Yang Liu | posted 4 years ago

Hi Ryan,

In my old blog system implemented with plain php, I had 3 tables for posts, comments and users. In post/comment-table theres a authorId column which points to an user. and in the user-table, infos like password and roles are stored. Now I want to rebuild this with symfony. So I would have post/comment entities, in post there will be a private $comment with OneToMany relation to the comment-entity and so on...
my question ist now about user, is it possible to make an "class User implement UserInterface", so I have access to all the authentication stuff, and at the same time, have ManyToOne-relation in post/comment entities point to the user? something like

/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
*/
private $user;

Btw. I recall in the doctrine relationship tutorial, you said something about theres only ManyToOne or ManyToMany relationship. So I wouln't need to add extra properties like $comments or $posts in the user class?

Reply

Hey Yang,

Let me help you. Yes, it's definitely possible! The UserInterface just enforce you to have authentication-related fields on an entity, that's it. So you can add whatever you need fields to the User entity besides UserInterface required fields, even some relation properties like ManyToMany, etc.

Yes, you're right! You wouldn't need to add these extra properties, but you could if it'll be useful for you, i.e. to build some complex join queries or just for convenience to get a collection of related entities.

Cheers!

Reply
Default user avatar
Default user avatar Danijela | posted 4 years ago

Hey! :) I created EditUserForm, finally. I had a little problem with roles: sometimes roles were saved as json array ["ROLE_USER","ROLE_ADMIN"], sometimes as json object {"1":"ROLE_USER"}.
I solved it with next line, is there some better way? Thanks!

$user->setRoles(array_values($form->get("roles")->getData()));

Reply

Hey Danijela

I'm not sure why that's happening, but can you double check that you chose "json_array" as the type of your $roles property?
Another thing you can do is passing the User instance as a second parameter to your FormType (when creating it), so it automatically updates the properties.


// UserController.php
public function editAction(Request $request, User $user) {
...
$editForm = $this->createForm(UserEditType::class, $user)
...
}

Cheers!

Reply
Default user avatar
Default user avatar Danijela | MolloKhan | posted 4 years ago | edited

Hey MolloKhan

I choose json_array type, and also set second parameter for createForm(..). Maybe I missed something else, I will check it all again.

I found the similar issue here, maybe it is smilar issue, i dont know.. :)

Thank you for response :)

Reply

Or maybe is something related to your environment. Which OS, and database driver are you using?

Reply
Default user avatar

I had the same problem and ended up swapping json_array for simple_array which saved everything as a comma delimited list.

Reply

Oh, that's interesting. I've never have that problem - Maybe it's because of the MySql version. On which version are you running?

Reply
Default user avatar

Just a quick one, if I add different users which have different accounts with their details and their favorites etc.. How would you protect each new users account. I would say dynamically but I am not sure how, maybe with their ID I suppose?

Reply

Hi James!

I'm not 100% sure I'm going to answer what you're actually asking, but let me give it a shot - then you can tell me that I didn't fully understand your question if I miss :).

Giving each user a different role is cool, but it's still a "global" property - it helps to answer questions like "should the user have access to the blog admin area in general". But, roles can't be used for security that's based on an *object* - e.g. perhaps a user should have access to only the blog posts that *they* author. So, they will have access to edit *some* blog posts, but not others. Is this kind of what you're asking?

In short, there are 2 ways to solve this:

1) Manually (I don't often do this, but you'll see how simple it is). For example, suppose there is some url like /users/{id}, but I should only be able to see *my* page. Then, you might do this in the controller:


public function showAction($id)
{
$user = // query for the User

if ($user != $this->getUser)) {
throw $this->createAccessDeniedException('this is not your account!');
}
}

So, you can simply put whatever security logic you need in your controller and then deny access like this.

2) A better solution is to use voters: https://knpuniversity.com/s.... These are very similar to doing what I just did above, but you centralize your security logic so that you can more easily re-use it.

Does this help? Or did I totally answer the wrong question? :)

Cheers!

Reply
Default user avatar

Voters is it ! Thanks again! But if I still want admin to have access, how do I add the admin to the if statement? And is there some king of super admin for developers like you talked about in one of you tuto?

Thanks

Reply

Hey James,

In if statement you could check whether the current user has a role ROLE_ADMIN or the current user is owner (like in Ryan's example) - then allow access:


public function showAction($id)
{
$user = // query for the User

$this->denyAccessUnlessGranted('ROLE_ADMIN');

if ($user != $this->getUser()) {
throw $this->createAccessDeniedException('this is not your account!');
}
}

Cheers!

Reply
Default user avatar
Default user avatar Karol Goraus | posted 4 years ago

Hi everyone!

I am following your course but I use Postgres instead of MySql. I encountered a problem with the "roles" column.
Migration generated by console creates json column (with NOT NULL), as expected. Running the migrate command generates an error that column roles cannot be null
I tried to add columnDefinition='default=\'[\']::JSON' in "@ORM\Column" annotation but that generates "ALTER..." SQL without column type ((...) ADD roles default '[]'::JSON).
I ended up removing the columnDefinition part (so I had User.php like the one in this course) and modifying the migration file: $this->addSql('ALTER TABLE "user" ADD roles JSON NOT NULL DEFAULT \'[]\'::JSON');

I hope that someone can benefit from that, should one encounter such problem or maybe you have some other solution for that problem.

Cheers!

Reply

Hey Karol Goraus

Thanks for sharing your problem and solution :)
But instead of setting a default value at the DataBase level, you can do it in your entity code


// User.php
class User {
....
// Set default to an empty array
private $roles = [];
....
}

I believe it should give you the same result

Cheers!

Reply
Default user avatar
Default user avatar mounir elhilali | posted 4 years ago

Hi Ryan/Victor,

I get an empty array in roles database column! help plz

Reply

Hey Mounir,

Check your setter/getter methods - maybe you just made a logic typo there. Also, try to debug roles before doing persist() and flush() with:


dump($user->getRoles());
die;
$em->persist($user);
$em->flush();

Or if you set roles only in data fixtures, try to dump the result array which your getter returns:


public function getRoles()
{
$roles = [];

// loop over some ManyToMany relation to a Group entity
foreach ($this->groups as $group) {
$roles = array_merge($roles, $group->getRoles());
}

dump($roles); die;
return $roles;
}

Btw, do you use MySQL database?

Cheers!

Reply
Default user avatar
Default user avatar mounir elhilali | victor | posted 4 years ago

Sorry, i have just seen your reply.

Yes i do use MySQL dababase.

My getRoles() look like this:

public function getRoles()
{
$roles = $this->roles;

// give everyone ROLE_USER!
if ( !in_array('ROLE_USER', $roles) ) {
$roles[] = 'ROLE_USER';
}

return $roles;
}

I have no groups property in User class.

when i dump $roles in getRoles() i get ['ROLE_USER']
and when i dump $user->getRoles() in registerAction(), before persist method, i get ['ROLE_USER'].

But dumping $user in registerAction(), before persist method returns roles with empty array.

Thanks Victor!

Reply

Hey Mounir,

Great! This's exactly the correct behavior. So ROLE_USER in this case is dynamicly added to all your users at runtime, even if they don't have any roles. It allows you do not worry about assigning default ROLE_USER to all your users. But if you add a different role to a user - it will be stored into the DB! So check if it works correctly by adding a new ROLE_ADMIN for the admin user, then persist() and flush() - you should see the only ROLE_ADMIN in database as we expected.

Cheers!

Reply
Default user avatar
Default user avatar mounir elhilali | victor | posted 4 years ago

Thanks guys, is clear now :)

Reply
Dennis E. Avatar
Dennis E. Avatar Dennis E. | posted 4 years ago

Hello KNP,

I've got a weird thing with the $roles json_array. I save the user to the database with this code:

$user = new User();
$user->setEmail('RyanIsAwesome@gmail.com');
$user->setPlainPassword('SuperCoolPassword');
$user->setRoles('ROLE_ADMIN');

$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();

All goes well, and the user is saved into the database.

Now when I login, It's not returning the $roles as an array, so it breaks on this line
if(!in_array('ROLE_USER', $roles)) {

Now I fixed it with this:

$roles = (array)$this->roles;

But why isn't it an array?

Greetz, Dennis

Reply

Hey Dennis,

I suppose it's because in your code you're passing role as a string to setRoles() method, i.e. $user->setRoles('ROLE_ADMIN'). Check your setRoles() setter, in our project it has "setRoles(array $roles)" signature, which means your should pass an *array* of roles like: $user->setRoles(['ROLE_ADMIN']). If you still have some problems with it - please, show your setRoles() method declaration.

Cheers!

Reply
Dennis E. Avatar
Dennis E. Avatar Dennis E. | victor | posted 4 years ago | edited

Hi victor,

I did not have that indeed.
I passed a string to the setRoles method. Now I've changed that, and adjusted the setRoles function to setRoles(array $roles)!

Thanks!

Reply
Default user avatar
Default user avatar Tomáš Hermánek | posted 4 years ago

Hi, I am thinking about best practices for following scenario: I have two roles ROLE_USER, ROLE_ADMIN. Also, I have object (i.e. Article) which can be in two states. draft, assigned. And also can be assigned to user. A want to list all articles but user with ROLE_USER can see only articles in draft state and assigned articles. User with role ROLE_ADMIN can see all articles. And question is. Is better practice to create controller with one action which handles each roles (based on role it picks proper repository method) or I should use two dedicated actions .... separated for each ROLE?
Thank you for your response.

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}