Skip to content

Resource setup

Lex de Willigen edited this page Mar 12, 2021 · 5 revisions

This package gives you precise control over which abilities should be checked when hydrating your API resources. Some extra methods have to be added to your models and API resources. In your models, add the HasAbilities trait.

use AgilePixels\ResourceAbilities\HasAbilities;

class Post extends Model
{
    use HasAbilities;
}

In your API resources, add the AgilePixels\ResourceAbilities\JsonResource\ProcessesAbilities trait. Please do note the JsonResource namespace.

use AgilePixels\ResourceAbilities\JsonResource\ProcessesAbilities;

class PostResource extends JsonResource
{
    use ProcessesAbilities;
}

Resource Collections

While resources transform a single model into an array, resource collections transform a collection of models into an array. However, it is not absolutely necessary to define a resource collection class for each one of your models since all resources provide a collection method to generate an "ad-hoc" resource collection on the fly. However, if you need to customize the meta data returned with the collection, it is necessary to define your own resource collection. Therefore this package also supports the use of ResourceCollection classes. Add the AgilePixels\ResourceAbilities\ResourceCollection\ProcessesAbilities trait to your class.

Note the ResourceCollection namespace! Also, please be aware that you MUST extend our AgilePixels\ResourceAbilities\ResourceCollection class. This class makes sure nested ResourceCollections are also wrapped. If nested resource collections wouldn't be wrapped, the collection actions couldn't be added to the response.

use AgilePixels\ResourceAbilities\ResourceCollection\ProcessesAbilities;
use AgilePixels\ResourceAbilities\ResourceCollection;

class PostCollection extends ResourceCollection
{
    use ProcessesAbilities;
}

Defining abilities

Laravel provides two primary ways of authorizing actions: gates and policies. Therefore, the available gates and policies have to be defined first in the API resource.

Policies

Policies, like controllers, group logic around a particular model or resource. Therefore, we prefer to add abilities to our API resources using policies. To check the current model against an entire policy, provide a policy in the abilities() method. This package will scan the provided PostPolicy class for available actions and add all available actions from the policy to the API resource.

public function toArray($request): array
{
    return [
        'abilities' => $this->abilities(PostPolicy::class)
    ];
}

Gates

To add an ability using gate check to your API resource, provide a gate in the abilities() method. The given action will be checked using the model that is available in the resource. As an example, in a PostResource, the ability view will be checked using the action view and the available Post model. If you were to check this action by hand, the result would look like Gate::check('view', $post).

public function toArray($request): array
{
    return [
        'abilities' => $this->abilities('view')
    ];
}

Adding multiple gate/policy abilities

It may be the case that you want to add a policy but also some other gates. This may be achieved by calling the add() method after the abilities() method.

public function toArray($request): array
{
    return [
        'abilities' => $this->abilities('view')
                            ->add('update')
                            ->add('delete')
    ];
}

Collections

Some action checks are model specific. For instance, the actions view, update or delete require a specific model to determine whether the user is granted that ability or not. However, some actions don't need a model, such as viewAny or create. In the Laravel docs, these actions are referred to as methods without models. You might need these abilities when a collection is empty. That's why it doesn't make sense to include these abilities on model level, but instead add them to the collection meta. Like when working with resources, these "collection abilities" can be added using both policies and gates. To do so use the collectionAbilities() method in your JsonResource class.

Collection policy abilities

Since the collection has no "resource" available to authorize against, you have to provide the model class name to the collectionAbilities() method.

public static function collection($resource): AnonymousResourceCollection
{
    return parent::collection($resource)->additional([
        'abilities' => self::collectionAbilities($resource, PostPolicy::class, Post::class)
    ]);
}

Collection gate abilities

The most used way to add abilities will probably be using a policy as described above. However, you can also check a single action for a given model class using a gate check.

public static function collection($resource): AnonymousResourceCollection
{
    return parent::collection($resource)->additional([
        'abilities' => self::collectionAbilities($resource, 'viewAny', Post::class)
    ]);
}

Adding multiple collection gate/policy abilities

Like adding multiple abilities for a model, it may also be the case that you want the same behaviour on collection level. This may be achieved by calling the add() method after the collectionAbilities() method.

public static function collection($resource): AnonymousResourceCollection
{
    return parent::collection($resource)->additional([
        'abilities' => self::collectionAbilities($resource, 'viewAny', Post::class)
                            ->add('create)
    ]);
}

Dedicated resource collection classes

If you're using a dedicated resource collection class, you don't have to use the static collectionAbilities() method. You can use the abilities() in always the same way as you would in a normal JsonResource. However, the tiny difference is that you MUST provide the model class as second argument.

public function toArray($request): array
{
    return [
        'data' => $this->collection,
        'abilities' => $this->abilities(PostPolicy::class, Post::class)
    ];
}