If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
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.
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 | |
} |
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.
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.
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.
// 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
}
}