ella-stinnes's avatar

Policy for belongsToMany Pivot Table

  • projects
  • project_user
  • users

I want to create an authorization policy for the 'project_user' pivot table to define who is able to assign projects to a given user.

At the moment, I don't have a ProjectUser model class. Will I need to create this for the ProjectUserPolicy?

1 like
9 replies
vincent15000's avatar

It depends on the business logic.

Can you explain the rule that explains if a user can assign projects to other users ?

It is inside a team with a team leader ?

It this authorized only for the manager ?

... ?

ella-stinnes's avatar
  • Full Admin - Can link any user to projects that are associated with the users' company.

  • Company Admin - Can only view users associated with their own company. They can therefore only link these users to projects associated with their company.

Jsanwo64's avatar
Jsanwo64
Best Answer
Level 11

You don’t need a ProjectUser model just to authorize “assigning a project to a user”.

In Laravel, the “pivot” is usually not treated as a first-class resource for authorization; instead you authorize the action on one of the real models (most commonly User or Project) and pass the other model(s) as arguments.

  1. Define a custom ability on UserPolicy
// app/Policies/UserPolicy.php

public function assignProject(User $actor, User $target, Project $project): bool
{
    // 1) Full Admin: can link users to projects within the user's company
    if ($actor->isFullAdmin()) {
        return $target->company_id === $project->company_id;
    }

    // 2) Company Admin: only within their own company
    if ($actor->isCompanyAdmin()) {
        return $actor->company_id === $target->company_id
            && $actor->company_id === $project->company_id;
    }

    return false;
}
  1. Use it in controller (best place, because you have all objects)
public function update(User $user, Request $request)
{
    $project = Project::findOrFail($request->project_id);

    $this->authorize('assignProject', [$user, $project]);

    $user->projects()->syncWithoutDetaching([$project->id]);
}

Route middleware: possible, but you’ll still need the objects

Route ->can() works well when your policy method can be resolved from route params. Since the ability needs User + Project, your route must provide both:

Route::post('/users/{user}/projects/{project}', [UserProjectController::class, 'store'])
    ->can('assignProject', ['user', 'project']);

This calls UserPolicy@assignProject(auth()->user(), $user, $project).

If your “edit” page doesn’t include {project} in the URL, then ->can() can only authorize a broader ability like “canManageUserProjects” (no specific project yet).
3 likes
ella-stinnes's avatar

My solution of a policy for a pivot table wasn't mentioned in the docs, so your explanation was what I was looking for and makes sense - thank you!

2 likes

Please or to participate in this conversation.