The words "parent" and "child" may be a little confusing. They are not the synonyms of "general, whole" and "specific, part".
Please take a look at the figure in the guide.
Access Check
The key concept in the rbac hierarchy is arrows and their directions.
An arrow relates 2 items. An item on the starting point is called "child" and one on the ending point is called "parent". And all the items and the arrows in the hierarchy must construct an one-way network.
On one end of the network are located permissions that permit the controller actions, and on the other end are individual users.
Checking access right is performed by trying to find a route from the action to the user following the arrows.
- Can John "updatePost"?
"updatePost" has 2 parents: "admin" and "updateOwnPost". But the route via "admin" is not possible for John, because John is not a parent of "admin". So we have to take another route, i.e., "updatePost" => "updateOwnPost" => "author" => "John".
There’s a rule called “AuthorRule” attached to “updateOwnPost”. It checks whether the user is the creator of the post. If he/she is, then he/she can proceed to the next item and finally get the right to update the post.
- Can Jane "updatePost"?
Yes, always, because there’s a route from “updatePost” to “Jane” via “admin”. The other route via “updateOwnPost” may or may not be available for her, depending on whether she is the creator of the post or not. It’s just the same as John. But she can update any post of any author because she has a route via “admin”.
- How do we call "can" method?
We use this:
if (\Yii::$app->user->can('updatePost', ['post' => $post])) {
// update post
}
We start from "updatePost", not from "updateOwnPost". Otherwise we could not provide a route via "admin" for Jane.
And we pass the additional parameter that will be used by "AuthorRule". It is not used in "updatePost", but must be there from the beginning for the sake of "updateOwnPost".
By this way we could simplify the code for access checking.
- What if "updateOwnPost" and "updatePost" have no parent-child relation?
Since we have no route between "updatePost" and "updateOwnPost", we have to check 2 of them independently:
if (\Yii::$app->user->can('updatePost') ||
\Yii::$app->user->can('updateOwnPost', ['post' => $post])) {
// update post
}
This will work as expected, but is not as effective as 3).
- What if "updateOwnPost" is a child of "updatePost"?
Now the arrow between "updatePost" and "updateOwnPost" has the opposite direction.
if (\Yii::$app->user->can('updateOwnPost', ['post' => $post])) {
// update post
}
But the code above works only for John. It won’t give Jane an admin right to update any post.
Be aware that you have to pass "AuthorRule" in order to proceed from "updateOwnPost" to "updatePost". It means that only the authors can proceed to "updatePost".
So in this case you’ll end up writing the same code as 4).
Now, I hope you’ll agree that “updateOwnPost” must be a parent of “updatePost”. 