laravellaravel-8laravel-authenticationlaravel-authorizationlaravel-gate

Gate Define not working for other users except whose role_id is 1


The Below code in middleware is working fine when user has role_id 1, When I did the dd on role->permissions I get then the response is array.

But on this middleware line

if (in_array($role->permissions, $permission)) {

I get this error for all other users whose role_id is different

in_array(): Argument #2 ($haystack) must be of type array, string given where array was passed

My Roles Model has

protected $casts = [
        'permissions' => 'array',
    ];

My User Model has

protected function role()
    {
        return $this->hasOne(Roles::class, 'id', 'role_id');
    }

My web Middlewaregroup has

\App\Http\Middleware\RolePermissionCheck::class,

My Meddlware has

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;


class RolePermissionCheck
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (!empty(Auth::user()->role_id)) {
            $role = Auth::user()->role;

            Gate::before(
                function () {
                    if (Auth::user()->role_id === 1) {
                        return true;
                    }
                }
            );
            // dd($role->permissions);
            foreach ($role->permissions as $permission) {
                Gate::define(
                    $permission,
                    function ($role) use ($permission) {
                        if (in_array($role->permissions, $permission)) {
                            return true;
                        }
                    }
                );
            }
        }

        return $next($request);
    }
}

Solution

  • Do not mark my answer as correct as user Autista_z told you the fix, I am going to share something to have a better code, really simple and "Laravel way" stuff to do.

    As user Autista_z said: "the problem will be in foreach loop for Gate defining. In first iteration of loop (based on your sample array) you would have $action = 0 and $roles = 'admin_role_manage'. So the name of gate would be 0. So of course, then @can('admin_role_manage') is false".


    So, You are setting up or getting a lot of stuff that is not needed, or worded other way, you can have a clearer code (at least for your Middleware class).

    If you know (maybe you don't), you can cast models properties to a type you want, so instead of doing json_decode($user_role->permissions) you can simply do foreach ($user_role->permissions as $role), but before you do so you need to cast it.

    So your model would have a property $casts as this:

    protected $casts = [
        'permissions' => 'array',
    ];
    

    This will allow you to do $model->permissions = ['role_1', 'role_2', ...]; without the need of doing $model->permissions = json_encode(['role_1', 'role_2', ...]); (and also this is the Laravel way of handling this).

    So your middleware would end like:

    namespace App\Http\Middleware;
    
    use App\Models\User;
    use App\Models\Roles;
    use Closure;
    use Illuminate\Support\Facades\Auth;
    use Illuminate\Support\Facades\Gate;
    
    class RolePermissionCheck
    {
        public function handle($request, Closure $next)
        {
            if ($role = Auth::user()->role)) {
                Gate::before(
                    function (User $user) {
                        if ($user->role_id === '1') {
                            return true;
                        }
                    }
                );
    
                foreach ($role->permissions as $permission) {
                    Gate::define(
                        $permission,
                        function ($role) use ($permission) {
                            if (in_array($permission, $role->permissions)) {
                                return true;
                            }
                        }
                    );
                }
            }
    
            return $next($request);
        }
    }
    

    See that I have changed the wording of $role->permissions as $roles to $role->permissions as $permission, your role could be "Writer" and your permissions are newsletter_manage, brand_logos, quote_manage, and more. So this are not roles but permissions.

    Also have in mind that I think you can even have better code for the foreach/define part.