A common error encountered when implementing the Laravel Spatie package. Using it in the initial tries, you might come up with a call to undefined function can() error.
Solutions for undefined function can()
The most common reason for this error is, we forget to implement Authorizable on User Model.
The common reason behind this error is when you forget to implement Authorizable on a User Model.
Illuminate\Contracts\Auth\Access\Authorizable
and then used the trait:
Laravel\Lumen\Auth\Authorizable
Solution 01
Lets say, we want to register permissions for users and want to check if logged in user has specific permission. For example, user 10, has permissions 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 the a specific logged in user has a permission applied. For example, user Alice, has the 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 will 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 will explicitly call the Authorizable interface and trait. Notice the extracted code lines as shown below.
\Illuminate\Contracts\Auth\Access\Authorizable
\Illuminate\Contracts\Auth\Access\Gate\Authorizable