Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

mihailovskim's avatar

How to extend/overwrite the @can and @canany blade directives

My whole permissions control is built by using the spatie/laravel-permission, so now I am at the point with so many users and it's really hard to update the permissions for each user when it's required adding more permissions based on a company.

I have implemented functionality to assign roles and permissions to a company - and not just the user. So, my question is: What is the best way to rewrite the @can and @canany directives to check for both permissions sets, the ones assigned directly to the logged in user, and also for the ones assigned to the user company (by roles or permissions). So to check if the user or user company has the required permission.

0 likes
4 replies
LaryAI's avatar
Level 58

To extend the @can and @canany Blade directives to check both user and company permissions, you can create custom Blade directives. Here's a step-by-step solution:

  1. Create a Custom Gate: First, define a custom gate that checks both user and company permissions. You can do this in the AuthServiceProvider.

  2. Register the Custom Blade Directives: Use the Blade::directive method to create custom directives.

  3. Use the Custom Directives in Blade: Replace the default @can and @canany with your custom directives.

Here's how you can implement this:

Step 1: Define a Custom Gate

In your AuthServiceProvider, define a custom gate that checks both user and company permissions.

use Illuminate\Support\Facades\Gate;
use App\Models\User;

public function boot()
{
    $this->registerPolicies();

    Gate::define('access-permission', function (User $user, $permission) {
        // Check if the user has the permission
        if ($user->can($permission)) {
            return true;
        }

        // Check if the user's company has the permission
        $company = $user->company; // Assuming you have a relationship set up
        if ($company && $company->hasPermissionTo($permission)) {
            return true;
        }

        return false;
    });
}

Step 2: Register Custom Blade Directives

In a service provider, register custom Blade directives.

use Illuminate\Support\Facades\Blade;

public function boot()
{
    Blade::directive('canCompany', function ($permission) {
        return "<?php if(auth()->check() && auth()->user()->can('access-permission', {$permission})): ?>";
    });

    Blade::directive('endcanCompany', function () {
        return "<?php endif; ?>";
    });

    Blade::directive('canAnyCompany', function ($permissions) {
        return "<?php if(auth()->check() && collect({$permissions})->contains(fn($permission) => auth()->user()->can('access-permission', $permission))): ?>";
    });

    Blade::directive('endcanAnyCompany', function () {
        return "<?php endif; ?>";
    });
}

Step 3: Use the Custom Directives in Blade

Now, you can use these custom directives in your Blade templates.

@canCompany('edit articles')
    <!-- The user or their company can edit articles -->
@endcanCompany

@canAnyCompany(['edit articles', 'delete articles'])
    <!-- The user or their company can either edit or delete articles -->
@endcanAnyCompany

This approach allows you to extend the functionality of the @can and @canany directives to include company-level permissions, making it easier to manage permissions across both users and their associated companies.

mihailovskim's avatar

@LaryAI The idea is more to overwrite the @can and @canany and not creating a new one. If I create new one @canCompany etc, I will have to do a lot of refactoring.

jlrdw's avatar

@mihailovskim A little more code involved, but you could load a server fetched partial in an object depending on the permission. I would suggest doing the who can or cannot at controller level and keep permissions out of views. Just my opinion.

mihailovskim's avatar
mihailovskim
OP
Best Answer
Level 1

Bellow is the piece of code I have added to the AuthServiceProvider and it works as expected.

app/Providers/AuthServiceProvider.php

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

    if ($user->hasRole(config('access.users.admin_role'))) {
        return true;
    }

    if ($user->hasPermissionTo($ability)) {
        return true;
    }

    if ($user->company) {
        if ($user->company->hasPermissionTo($ability)) {
            return true;
        }
    }

    return null;
});

Please or to participate in this conversation.