vincent15000's avatar

Is it possible to apply a policy to a relationship ?

Hello,

In a model, I have some relationships.

I'd like to filter a relationship result according to some rules in the corresponding policy.

Example :

public function users()
{
	return hasMany(User::class);
}

...

$events = Event::with('users')->get();

My question is right here : is it possible to apply a policy to retrieve only some users and not all users binded to the event ? Given that the policy contains some authorizations to allow viewing only some users.

Thanks for your answer.

V

0 likes
7 replies
LaryAI's avatar
Level 58

Yes, it is possible to apply a policy to a relationship. You can use the whereHas method to filter the relationship results based on the policy. Here's an example:

$events = Event::whereHas('users', function ($query) {
    $query->where(function ($query) {
        $query->where('id', 1)
            ->orWhere('id', 2);
    });
})->get();

In this example, we're using the whereHas method to filter the users relationship based on the policy. The policy is defined as a closure that takes a query builder instance as its argument. We're using the where method to add a condition to the query builder that filters the users based on their ID. In this case, we're only retrieving users with an ID of 1 or 2.

You can replace the closure with a call to a policy method that returns a query builder instance. Here's an example:

$events = Event::whereHas('users', function ($query) {
    $query->where(app(UserPolicy::class)->viewAny(Auth::user()));
})->get();

In this example, we're using the where method to add a condition to the query builder that filters the users based on the policy. We're calling the viewAny method on the UserPolicy class and passing in the authenticated user as an argument. The viewAny method returns a query builder instance that filters the users based on the policy.

1 like
vincent15000's avatar

@LaryAI A relationship has to be inside a model, but I don't want to define a relationship which could depend on an authenticated user.

What could be the best way to filter a relationship outside the model but in a transparent manner in the controller. For example with a scope ? Another way ?

jlrdw's avatar

@vincent15000 click the ** My Participation** link, notice no id is shown. Jeffrey is using the auth id to only display your Participation post.

That is how.

Authentication and authorization work together.

2 likes
vincent15000's avatar

@jlrdw I have clicked on ** My Participation ** link, but I don't understand why you ask me to do that.

jlrdw's avatar

@vincent15000 the point is Jeffrey did not use any kind of ID, he uses the authenticated users ID in the query, thus protecting the query string from being tampered with.

2 likes
vincent15000's avatar

@jlrdw Oh you mean I could do something similar for my problem ? My problem is a bit more complex.

Roles => superadmin, admin, user

If the superadmin is connected, he can see all datas.

If the admin is connected, he can only see his own datas and the users datas.

If a user is connected, he can only see his own datas.

I'm searching a way to have the simplest query to do retrieve only the authorized datas.

I specify that it's an app with Laravel v7.24. I can't rewrite all controllers so I have necessarily to keep the existing global code even if it's a very bad code.

1 like
jlrdw's avatar

@vincent15000 That is exactly what I mean, the query is written just like you described.

        $petsearch = $petsearch . "%";
        $query->where('petname', 'like', $petsearch);
        if (ChkAuth::userRole('admin') === false) {    /////Do your check here
            $userid = Auth::user()->id;
            $query->where('owner_id', '=', $userid);  ///// Only if not admin
        }
        $results = $query->orderBy('petname', 'asc')->paginate(5);   //// Can see all  if admin
        return $results;


Just example.

2 likes

Please or to participate in this conversation.