Laravel: Scoping queries in the URL

The session won’t help you make things easier

Italo Baeza Cabrera
4 min readOct 10, 2022
Photo by Jaimie Harmsen on Unsplash

One of the projects I worked on had small but important problematic: users should be able to manage multiple subscriptions through my on-premises Subscriptions library.

This app was built around a simple idea: an User should be able to have multiple Subscriptions, be able to change the one to manage, and scope all queries to the related Subscription.

My library already solves the problem of “multiple subscriptions”, but no the scoping. Like everything in the world of software, you have multiple ways to solve a problem. I just started with the bad one.

Who need sessions anyway?

The first idea was to let the user pick which Subscription, and save that value into the session. Then I would take out the Subscription ID from the session itself, and scope each query dynamically.

It was a total mess because I had to do it everywhere I needed to scope something by the Subscription. In other words, I had to edit all queries.

Take for example this simple “show all products for the subscription” controller.

Going around with the same idea, I thought it would be convenient to create a Global Scope for each request, but since the scopes are static, doing so would break the application when using Laravel Octane.

Trying to tie anything from an ephemeral state (the HTTP Request) into/from a global state (session/cache/database/storage), or even outside controllers, it’s a very bad idea. I had to scrap that approach, and then I figured out that the simpler the solution, the better.

Named parameters is all you need

I needed to know which Subscription was active across all requests. Session was the first thing I thought to solve the problem, but as you saw, it wasn’t the best solution.

Then, I decided on using the Subscription as a named parameter at the root of the Route declaration. This would make the Subscription somewhat transparent to the user.

http://myapp.com/subscription/27d...0fa/products

After the user picked up the subscription to work on in at login time, he would be redirected to the subscription respective URL subscription/{subscription}/ .

All the controllers would receive the instance of the Subscription Model we bound to the route, and authorize it.

One neat thing about this approach was its simplicity, since instead of dealing with the session, I only had to check the user wasn’t checking a Subscription that wasn’t subscribed to.

Soon enough I figured out that Authorizing each controller to check what the user could see (or manage) would became tedious.

For that cumbersome logic, I remembered the authorization middleware, which will magically execute the Authorization for the named parameter we give. As you can guess, this is done before hitting the controller at all.

That way we can skip authorizing something on multiple controllers, leaning their code. We can also use this middleware inside a group, so we can save declaring it in every route declaration.

The only “let down” from using the middleware are the controllers. If the signature of the Controller doesn’t type-hints the Subscription as parameter, the binding won’t resolve, and the gate will receive a string instead.

To solve that, we can change the signature of the Gate to find the model if the parameter is just a string.

In any case, the last thing looks like a stretch if you consider you shouldn’t need to authorize something you won’t use, but just beware of the caveats.

Now you know why most web apps decide to make heavy use of the URL: is straight forward, globally available for the app, and is just easier to manage than anything else.

While you’re it, you may check my on-premises Subscription package if you need to deal with Plans, Subscriptions and what not, and you don’t have a month to implement it.

--

--

Italo Baeza Cabrera
Italo Baeza Cabrera

Written by Italo Baeza Cabrera

Graphic Designer graduate. Full Stack Web Developer. Retired Tech & Gaming Editor. https://italobc.com

No responses yet