Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ALT's avatar
Level 1

Laravel Policy Issue

Hi all, Im quite new to laravel and Im building an API and have hit a road block with policies. Here is my challenge:

I have a api to delete a proposal and I need the owner of proposals to be the only ones that can delete it. After some research I found policies and I have implemented it as suggested by the docs but I keep getting this error:

Too few arguments to function App\Policies\ProposalPolicy::delete(), 1 passed in /..../vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php on line 798 and exactly 2 expected", "exception": "ArgumentCountError"

Now, this makes sense because the delete function has a signature with 2 parameters:

public function delete(User $user, Proposal $shareProposal)
{
        //
}

however my understanding is that the current Auth user is inferred and passed along side the second parameter; but this doesnt work.

Im hooking this up in the api.php class like this:

Route::delete('proposal/delete/{id}', [ProposalController::class, 'destroy'])->can('delete', 'App\Models\Proposal');

As it seemed only one param was added in the ->can(...) call, I decided to add an array like this:

Route::delete('proposal/delete/{id}', [ShareProposalController::class, 'destroy'])->can('delete', ['App\Models\Userr', 'App\Models\ShareProposal']);

this did not work too. I also tried the different variations with the ->middelware(...) call, but didnt work either.

I also understand this call can be made from the controller with the ..authorise(...) call but could someone please help explain how this is expected to work in the routes as this is the desire location for this setup.

Thank you.

0 likes
5 replies
s4muel's avatar
Route::delete('proposal/delete/{id}', [ProposalController::class, 'destroy'])->can('delete', 'App\Models\Proposal');

i would rename the {id} to {proposal} (just a cosmetic tweak, don't forget to rename the parameter in the controller also, so the route model binding can work correctly)), and then use:

Route::delete('proposal/delete/{proposal}', [ProposalController::class, 'destroy'])->can('delete', 'proposal`);

the proposal in the parameter matches the bound model and passes it correctly to the policy (along with the authenticated user (first policy parameter))

if you use App\Models\Proposal, it doesnt know the model instance, just the model class. it is used in policies which doesnt require models (create/store) https://laravel.com/docs/10.x/authorization#methods-without-models

ALT's avatar
Level 1

Thanks S4muel, that was helpful. I will give this a go later as my work VPN isnt letting me connect to my hosting sever. Just to understand more, is the {proposal} expected to match the name of the Table or the Model? Also if the Model name was GivenProposal and the table name is given_proposals should the route be:

Route::delete('proposal/delete/{given_proposals}', [ProposalController::class, 'destroy'])->can('delete', 'given_proposals`);

or

Route::delete('proposal/delete/{givenProposal}', [ProposalController::class, 'destroy'])->can('delete', 'givenProposal`);

Finally, the controller param should be

public function destroy(GivenProposal $givenProposal) { }

Right?

s4muel's avatar
s4muel
Best Answer
Level 50

@ALT neither (not a table, not a model). it needs to match the variable name in the controller method.

Route:

Route::delete('proposal/delete/{parameter}', [ProposalController::class, 'destroy']) // <- here i named the parameter "parameter"
    ->can('delete', 'parameter'); // <- match also here, so it is passed automatically, read more below

read more here: https://laravel.com/docs/10.x/authorization#via-middleware

Controller:

public function destroy(GivenProposal $parameter) // <- match `parameter` here so the "route model binding" works
{
	//
}

read more here: https://laravel.com/docs/10.x/routing#route-model-binding

Policy:

public function delete(User $user, GivenProposal $parameter) // <- i think this doesnt have to be called $parameter, because it is resolved from service container
{
    //
}

note: laravel framework might do some magic to work with combination of snakecase and camelcase (as it usually does in other places), not tested though, but you can play around.

1 like
ALT's avatar
Level 1

Thanks @s4muel. I have applied your suggested changes and they work, thanks again.

1 like
s4muel's avatar

@ALT you're welcome. just mark the correct answer to close this thread;)

Please or to participate in this conversation.