I’m developing an application where a User can be of type Admin, Client, Supplier or Manager. Each type (except Admin) has it’s own model with respective data, and related with a user_id. For example:
users
- id
- name
- email
- password
- type
clients
- id
- user_id
- segment
- last_buy
suppliers
- id
- user_id
- product_type
- corporate_name
managers
- id
- user_id
- managed_area
I want to have, in my User
model, a profile
attribute that loads user’s data from it’s other model (Client, Supplier or Manager), based on existing type
attribute.
Before, I’ve used the $appends
property and getProfileAttribute()
method approach to achieve the result. But now, I’m trying to optimize my application using eager loading. So I’m trying to load the profile this way:
public function profile(){
if($this->type == "client"){
return $this->hasOne(Client::class);
} else if($this->type == "supplier"){
return $this->hasOne(Supplier::class);
} else if($this->type == "manager"){
return $this->hasOne(Manager::class);
}
return null;
}
But isn’t working. Every query ->with(["profile"])
returns the profile
attribute with null value. I’ve dd
ed the $this->type
and noticed is always returning null too. I don’t understood why, but this is the cause why it can’t conditionally check User’s type.
So how can I achieve the expected result?
2
Answers
You must specify a foreign key and local key for each hasOne because you are not using ‘conventional’ column names.
e.g.:
As @mrhn commented above, the reason for your code not working is when queries are executed before model load. You can consider to apply One To One (Polymorphic) in your code: https://laravel.com/docs/10.x/eloquent-relationships#one-to-one-polymorphic-relations
Your tables would be:
Your migration:
Your relationship in user model: