phplaravelcontrollerresourcespolicies

Laravel, having a custom controller method with custom policy method?


I have a resource controller and want to add an extra custom policy method for destroyMany In which I would check if the user is admin before deleting many.

The default methods work fine

Controller Method Policy Method
index viewAny
show view
create create
store create
edit update
update update
destroy delete
destroyMany destroyMany

Controller destroyMany method is called, the policy isn't Or should I stick to Gates for this extra method? The docs say I can have any name for the methods and policies, How can both be linked?

destroyMany->destroyMany or destroyMany->deleteMany would be a good setup.

And would be a great addition to my resource controller (where it should reside)

class ResourceController extends Controller
{
    public function __construct()
    {
      $this->middleware('auth:api');
      $this->authorizeResource(Resource::class, 'resource');
    }

    public function index()
    {

        return ResourceCollection::collection(Resource::all());
    }

    public function destroyMany(Request $request)
    {
        // gets called but needs a policy which isn't called
    }
}

policy

class ResourcePolicy
{
    use HandlesAuthorization;

    /**
     * Create a new policy instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    public function viewAny(User $user)
    {
        // works
        return $user->hasAnyRoles(['admin', 'superAdmin']);
    }

    public function delete(User $user, Resource $resource)
    {
       // works
        return $user->hasAnyRoles(['admin', 'superAdmin']);
    }

    public function deleteMany(User $user, Resource $resource)
    {
        // not called because the controller method needs to be hooked up, like the other methods
       
    }
}

Solution

  • To get the addition policy method to work you will need to update the resourceAbilityMap for the controller. Adding the following to your controller should do the trick:

    protected function resourceAbilityMap()
    {
        return array_merge(parent::resourceAbilityMap(), [
            'destroyMany' => 'deleteMany'
        ]);
    } 
    

    Also, if you don't return anything from your deleteMany policy method it will result in a 403.

    If your route/controller method isn't receiving an instance of the model then you will also need to update the array returned from the resourceMethodsWithoutModels method:

    protected function resourceMethodsWithoutModels()
    {
        return array_merge(parent::resourceMethodsWithoutModels(), ['destroyMany']);
    }