Today, I started practicing with Authorization in Laravel using Gates and Policies.
Gates are easier, but Policies not so much, at least for me. And the documentation doesn’t really clear things up either.
I got stuck for several hours, and I really thought it will take days again.
The part where I (and many others from what I’ve seen on forums) got stuck is where I had to authorize the actions in the Controllers.
But, luckily, I managed to figure it out, and I’m going to share what I learned with you.
Set Up The Controller Methods for Policies
I’m not going through the whole creating policies process, just the confusing part with the Controllers. I assume you already properly created a Policy and its logic.
As an example, I’ll use a PostController
and its edit()
method.
Usually, the edit()
method would look something like this:
public function edit($id)
{
$posts = Post::findOrFail($id);
return view('posts.edit', ['posts'=>$posts]);
}
This is if you add your logic in Controllers.
Now, this is where it gets confusing for many people.
If you check Laravel’s Policy documentation, you’ll see an example like this one when using Policies via Controllers:
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// The current user can update the blog post...
}
You’ll notice that there’s no $id
parameter in the method anymore.
That’s because, in order for the Policy to work, you need to replace it with the object and its instance - Post $post
.
But how does it fetch the specific post for editing if there’s no id anymore?
Well, since you’ll be asking for the object directly in the method, as a parameter, there’s no need to use something like $posts = Post::findOrFail($id)
to check/find it again.
Laravel will check if the object exists and it will trigger the function.
So, the edit()
method I’ve used in my example should look like this now:
public function edit(Post $post)
{
$this->authorize('update', $post);
return view('posts.edit', ['posts'=>$post]);
}
As you can see, there’s no need to use findOrFail($id)
anymore.
If you’d still use findOrFail($id)
and $id
as a parameter, you’ll get an error like:
Too few arguments to function App\Http\Controllers\PostController::edit(), 1 passed and exactly 2 expected
Or, if you’d remove the $id
as a parameter, but pass $post
to findOrFail()
, as in findOrFail($post)
, it will return a collection instead of an object, because findOrFail()
normally needs an integer.
Ask for the Object in Every Method that Uses a Policy
Not all Controller methods require an $id
as a parameter, but you still need to ask for the object if you’re using a Policy for it.
For example, the index()
method:
public function index(Post $post)
{
$this->authorize('view', $post);
return view('posts.index', ['posts'=>$post]);
}
Careful When Calling the Policy Methods
Some Policy methods are named differently than the ones in the Controller.
For example, 'update'
from $this->authorize()
calls the method in the Policy (i.e. PostPolicy) which handles both edit()
and update()
in the Controller (i.e. PostController).
The index()
method from the Controller is handled by the view()
method in the Policy.
So, make sure you don’t add 'edit'
or 'index'
in $this->authorize()
by mistake just because you’re in the edit()
or index()
method in the Controller.
It happened to me with 'edit'
. 😃
That’s a Wrap
I hope I was comprehensive enough and you managed to understand how things stand with Policies via Controllers in Laravel.
If some info is outdated or incorrect, or you have anything to add, say or ask, please contact me via Twitter or email.
Looking for a modern & optimized presentation website?
Hire me on Fiverr