Injecting a Relationship

Published 1 week ago by ts

Hi all,

So in a controller I'm accessing the establishment property of the authenticated user, which returns an Eloquent belongsTo relationship.

I'm accessing it in several places, currently like so:

Auth::user()->establishment->...

Which I feel is bad. I'd really like to inject the establishment into the controller __construct method so I can access it as $this->establishment, but when I do, it returns a new instance of the establishment, not the user's establishment.

I've tried declaring a singleton in a service container and setting it to Auth::user()->establishment, but Auth::check() is returning false because (I'm guessing) the user hasn't been resolved?

I feel like I'm close to a solution. If there are any Laracasts or articles that might shed some light, that'd be ace.

Cheers, Tom

Best Answer (As Selected By ts)
bobbybouwmann

Well, it's not possible to do that in a controller because, like you said, the user hasn't been resolved in the service provider. It's alright to do multiple calls for this, however make sure you eager load the relationship so you prevent multiple of the same queries.

However there are alternatives for this. You can for example eager load the relationship by default, by specifing it in the model. This way you can always access the establishment from any User object.

// User.php

protected $with = [
    'establishment',
];

Another solution is using route model binding! However that also means that you always need to provide the id of the establishment in the url of every route where you want to access this data. This might not be the best solution.

A final solution is building a helper method. You can for example make a method like this

/**
 * Get the establishment from the user. If we pass a user we get it from that user.
 * If there is no user we simply use the current authenticated user.
 *
 * @param User|null $user
 * @return Establishment|null
 */
public function getEstablishmentFromUser(User $user = null) {
    if ($user === null) {
        auth()->user()->establishment;
    }   

    return $user->establishment;
}

You can even put this is some helper file which you can include using composer. Let me know if you need another example for this :)

bobbybouwmann

Well, it's not possible to do that in a controller because, like you said, the user hasn't been resolved in the service provider. It's alright to do multiple calls for this, however make sure you eager load the relationship so you prevent multiple of the same queries.

However there are alternatives for this. You can for example eager load the relationship by default, by specifing it in the model. This way you can always access the establishment from any User object.

// User.php

protected $with = [
    'establishment',
];

Another solution is using route model binding! However that also means that you always need to provide the id of the establishment in the url of every route where you want to access this data. This might not be the best solution.

A final solution is building a helper method. You can for example make a method like this

/**
 * Get the establishment from the user. If we pass a user we get it from that user.
 * If there is no user we simply use the current authenticated user.
 *
 * @param User|null $user
 * @return Establishment|null
 */
public function getEstablishmentFromUser(User $user = null) {
    if ($user === null) {
        auth()->user()->establishment;
    }   

    return $user->establishment;
}

You can even put this is some helper file which you can include using composer. Let me know if you need another example for this :)

ts
ts
1 week ago (14,145 XP)

@bobbybouwmann that's excellent, thanks so much.

I think the "best" way in this instance would be to use route model binding. The routes don't currently include the establishment, so I'll rewrite them to do so.

Thanks again!

bobbybouwmann

@ts Well route model makes the call for the establishment go away for you, however Laravel still needs to do a query and that will all happen underwater. But it's fine to add it ;)

ts
ts
1 week ago (14,145 XP)

@bobbybouwmann yeah, I've got my User model to use route model binding. I didn't think establishments would be necessary because a user belongsTo an establishment, so retrieving the user's establishment would be predictable. However, to futureproof (haha) the app, I'm going to change the relationship to belongsToMany - in future a user may be responsible for more than one establishment.

Please sign in or create an account to participate in this conversation.