How to fix call to undefined function can() in Laravel Spatie

A common error encountered when implementing the Laravel Spatie package is to ‘fix call to undefined function can().

Solutions for undefined function can()

The most common reason for this error is when we forget to execute Authorizable on the User Model.

Illuminate\Contracts\Auth\Access\Authorizable
and then used the trait: 
Laravel\Lumen\Auth\Authorizable

Solution 01

Let’s say if we want to register permissions for users and check if a logged-in user has any specific permission. For example, user 10, has permission to edit an article. To check permission we use Auth::user()->can(‘edit articles’)

Suppose you want to register some permissions for multiple users and need to confirm that a specific logged-in user has permission applied. For example, user Anna has permission to update a record. To check this, we normally use Auth::user()->can(‘update articles’).

The code below shows the PermissionMiddleware, where we check a user’s permission. All permissions should be defined in this user model.

class PermissionMiddleware
{
    public function handle($request, Closure $next, $permission)
    {
        if (app('auth')->guest()) {
            throw UnauthorizedException::notLoggedIn();
        }

        $permissions = is_array($permission)
            ? $permission
            : explode('|', $permission);

        foreach ($permissions as $permission) {
            if (app('auth')->user()->can($permission)) {
                return $next($request);
            }
        }

        throw UnauthorizedException::forPermissions($permissions);
    }
}

You can add Authorizable to this model to solve the undefined function can() issue.

use Illuminate\Contracts\Auth\Access\Authorizable;
use Laravel\Lumen\Auth\Authorizable;

Solution 02

The solution above works while using a User model. However, while using a custom user model, we should create a UserView Model as shown below.

<?php

namespace App\Models;

use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Foundation\Auth\Access\Authorizable;             // <-- add import
use Spatie\Permission\Traits\HasRoles;
use Laravel\Sanctum\HasApiTokens; 

class UserView extends Model implements AuthenticatableContract,
                                        AuthorizableContract    // <-- add interface
{
    use Authenticatable;
    use Authorizable;                                           // <-- add trait
    use HasFactory;
    use HasRoles;
    use HasApiTokens;

    // ... other code ...
}

Here you can explicitly call the Authorizable interface and trait. Note the extracted code lines as shown below using the Authorizable interface.

\Illuminate\Contracts\Auth\Access\Authorizable
\Illuminate\Contracts\Auth\Access\Gate\Authorizable

Leave a Comment