-
-
Notifications
You must be signed in to change notification settings - Fork 1
Home
If you build a web application with a separate frontend and backend, all kinds of information has to be transferred between
these two parts. Part of this are your routes, but how do you share the authorized actions in the most convenient way?
One quick note: from now on, we'll call these authorized actions "abilities". To share abilities, we use API resources.
That way we can see the abilities the current user has for that resource.
Now we hear you think, can't I just add a Gate::check()
to my API resource and then I have accomplished sharing
abilities? That is certainly true, but you will run into some problems. We've tried this approach in a big project.
This is what one of our resources looked like (stripped version):
class BoardResource extends Resource
{
/**
* Transform the resource into an array.
*
* @param Request $request
* @return array
*/
public function toArray($request)
{
return [
// Some attributes
'meta' => [
'abilities' => [
BoardPolicy::UPDATE => Gate::check(BoardPolicy::UPDATE, $this->resource),
BoardPolicy::DELETE => Gate::check(BoardPolicy::DELETE, $this->resource),
],
],
];
}
}
This approach worked perfectly fine. However, the biggest issue we ran into was the fact that the update
and delete
actions were always checked for every resource that was loaded. Our BoardPolicy
performed some database queries to
check whether the current user is authorized to perform the action, which resulted in A LOT of queries. So, we reduced
the amount of queries by making use of eager loading.
That improved our issue quite significantly, however, it still felt like we were loading an unnecessary amount of data
since there were quite some pages that didn't require the update
or delete
abilities at all. We've therefore
engineered this package which gives you precise control over which actions should be checked on a page load.
Let's take a look at a new API resource using this package:
class PostResource extends JsonResource
{
use ProcessesAbilities;
public function toArray($request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
'published_at' => $this->published_at,
'abilities' => $this->abilities(PostPolicy::class)
];
}
}
Our package will scan the PostPolicy
for available actions and check whether the currently authenticated user is
authorized to perform a certain action against the given resource. As a result, the json output will look like this:
{
"data": {
"id": 10,
"title": "Corporis eum adipisci et cum nostrum.",
"slug": "corporis-eum-adipisci-et-cum-nostrum",
"published_at": "2020-06-06T10:49:01.000000Z",
"abilities": {
"view": true,
"update": false,
"delete": true
}
}
}