★ When to use Gate::after in Laravel
Tags: laravel, php
Published: 2020-01-02 23:22:02

---
title: ★ When to use Gate::after in Laravel
published: true
date: 2019-04-24 13:46:23 UTC
tags: laravel, php
canonical_url: https://freek.dev/1325-when-to-use-gateafter-in-laravel
---

In a Laravel app [policies](https://laravel.com/docs/master/authorization) are a great way to organize authorization logic that revolves around models.

For the longest time, I've been using `Gate::before` to allow superadmins to do anything they want. While working on a new app, it finally clicked how [`Gate::after`](https://laravel.com/docs/master/authorization#gates) can be useful too. I'd like to share that knowledge in this blog post.

This post assumes that you understand and have experience with Laravel's authorization features. If not, [read the relevant documentation](https://laravel.com/docs/master/authorization) first.

## Explaining Gate::before #

The new app [our team](https://spatie.be) is building that was mentioned in the intro, is called [attended.io](https://github.com/spatie/attended.io). When finished, it will allow event organizers to register their events, slots, and speakers. Attendees can leave feedback on the slots of the events and its speakers.

Let's take a look at a part of the policy that handles whether or not a user is allowed to administer an event.

```php
class EventPolicy
{
   use HandlesAuthorization;

   public function administer(User $user, Event $event)
   {
      return $user->organizes($event);
   }
}
```

With this policy you can make the auth check like this:

```php
$user->can('administer', $event); // returns a boolean
```

This will return `true` for users that organize the event, and `false` for all others.

In our system, we also have superadmins that are allowed to do anything. They do not organize the event, but they still should be allowed to administer it. This can be solved by adding a `Gate::before`:

```php
// somewhere in a service provider

Gate::before(function ($user, $ability) {
    if ($user->isSuperAdmin()) {
        return true;
    }
});
```

The closure passed to `Gate::before` will be used before the `EventPolicy` (and all other policies) get called. If you're a superadmin you can now administer the event even though you're not organizing that event. Life is good!

Notice that if you're not a superadmin we don't return `false` but `null` (by not returning anything). This is done on purpose. If we were to return `false` here, the policies classes wouldn't be checked. Only superadmins would be able to administer events.

## Explaining Gate::after #

In our attended.io app, a slot can be reviewed by users that are logged in. A slot can only be reviewed once per user. A user may not review a slot before the slot has started.

Here's the policy class:

```php
class SlotPolicy
{
    use HandlesAuthorization;

    public function review(User $user, Slot $slot)
    {
        if ($user->hasReviewed($slot)) {
            return false;
        }

        if ($slot->starts_at->isFuture()) {
            return false;
        }
        
        return true;
    }
}
```

For non-superadmins this works fine. All our rules are respected. But the `Gate::before` - mentioned in the previous section - grants our superadmins way too much power. Superadmins can now review slots multiple times, and they can review a slot before it starts. Let's fix that!

Instead of using `Gate::before`, let's use `Gate::after`.

```php
// somewhere in a service provider

Gate::after(function ($user, $ability) {
   return $user->isSuperAdmin();
});
```

With this `Gate::after` in place instead of `Gate::before` the policies will get called first, even for superadmins. All users, including superadmins, are now unable to review slots that haven't started yet. Great!

If you have a keen eye, you probably noticed that for the `review` check on `SlotPolicy` the `Gate::after` doesn't get called because `SlotPolicy` already returns `true`. And that's true, for that particular check will work without the `Gate::after`.

But let's take a look again at the `EventPolicy` from the previous section.

```php
class EventPolicy
{
   use HandlesAuthorization;

   public function administer(User $user, Event $event)
   {
      return $user->organizes($event);
   }
}
```

As it stands, superadmins are not allowed to administer events because `administer` returns a boolean. Let's fix that.

```php
class EventPolicy
{
  use HandlesAuthorization;

  public function administer(User $user, Event $event)
  {
     if ($user->organizes($event)) {
        return true;
     }
  }
}
```

Now we don't return anything for regular users that don't organize the event. In this case our `Gate::after` callback will get called and it will return `true` for superadmins, `false` for all other users.