How to Set Up Controller Methods When Using Policies in Laravel

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).

update method in PostPolicy

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'. 🙂

Find out more: How to Change the Form Request Validation Redirect in Laravel

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 you have questions or thoughts, please leave a comment or send me a message using the contact page.

Don’t forget to share the post to help out others!

6 thoughts on “How to Set Up Controller Methods When Using Policies in Laravel”

  1. thank you so much. I faced the same problems when I read policies’ documentation.
    I guess using a model as parameters in method’s controller is possible with “Explicit binding”

  2. Thanks for the article.
    Where I am confused is….in some of my controllers I have customer method names like:
    public function axiosEdit()
    public function axiosView()

    and so on.
    Let’s say you wanted the users to only edit their own records on axiosEdit(), how would you do this with a policy please?

    I am lost on this.

    Thank you!

    • Unfortunately, I haven’t worked with Laravel anymore, so I’m almost completely rusted on the subject. You could try your luck on Stack Overflow.


Leave a Comment