I have the following tables:
| id | email | password | active | user_profile_type | user_profile_id | remember_token | created_at | updated_at | deleted_at |
|----|----------------------------|----------|--------|------------------------|-----------------|----------------|---------------------|---------------------|------------|
| 1 | macey.[email protected] | xxx | 1 | standard_user_profiles | 1 | 2BNbGXSnE7 | 2018-07-08 15:20:00 | 2018-07-08 15:20:00 | NULL |
| 2 | [email protected] | xxx | 0 | premium_user_profiles | 1 | GY3EHWm0Js | 2018-07-08 15:20:01 | 2018-07-08 15:20:01 | NULL |
| 3 | quinten.[email protected] | xxx | 0 | standard_user_profiles | 2 | Lw46mB2Wek | 2018-07-08 15:20:01 | 2018-07-08 15:20:01 | NULL |
| id | language_id | academic_prefix | first_name | last_name | academic_suffix | sex | created_at | updated_at | deleted_at |
|----|-------------|-----------------|------------|-------------|-----------------|--------|---------------------|---------------------|------------|
| 1 | 139 | NULL | Albin | Stoltenberg | NULL | male | 2018-07-08 15:20:00 | 2018-07-08 15:20:00 | NULL |
| 2 | 63 | NULL | Jermey | Konopelski | NULL | male | 2018-07-08 15:20:00 | 2018-07-08 15:20:00 | NULL |
| 3 | 156 | NULL | Demario | Ruecker | NULL | female | 2018-07-08 15:20:00 | 2018-07-08 15:20:00 | NULL |
I have to following models:
class User extends Authenticatable
{
use Notifiable, SoftDeletes;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'email',
'password',
'active',
'user_profile_id'.
'user_profile_type',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
'user_profile_type',
'user_profile_id',
];
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = [
'created_at',
'updated_at',
'deleted_at',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'active' => 'boolean',
];
/**
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function profile()
{
return $this->morphTo('user_profile');
}
}
class StandardUserProfile extends AbstractUserProfile
{
use SoftDeletes;
protected $fillable = [
'language_id',
'first_name',
'last_name',
'academic_prefix',
'academic_suffix',
'sex',
];
protected $dates = [
'created_at',
'updated_at',
'deleted_at',
];
/**
* @return \Illuminate\Database\Eloquent\Relations\MorphOne
*/
public function user()
{
return $this->morphOne(User::class, 'user_profile', 'user_profile_type', 'user_profile_id');
}
}
Relation::morphMap(
[
PremiumUserProfile::class,
StandardUserProfile::class,
]
);
User::find(1)->profileRelationship is accessable correctly here.
>>> User::find(1)->profile
=> App\Models\UserProfiles\StandardUserProfile {#3170
id: 1,
language_id: 139,
academic_prefix: null,
first_name: "Albin",
last_name: "Stoltenberg",
academic_suffix: null,
sex: "male",
created_at: "2018-07-08 15:20:00",
updated_at: "2018-07-08 15:20:00",
deleted_at: null,
}
User::with('profile')->find(1)This kind of loading is not possible, because related model is loaded in the wrong property user_profile, property profile returns null
>>> User::with('profile')->find(1)
=> App\Models\User {#3154
id: 1,
email: "[email protected]",
active: 1,
created_at: "2018-07-08 15:20:00",
updated_at: "2018-07-08 15:20:00",
deleted_at: null,
profile: null,
user_profile: App\Models\UserProfiles\StandardUserProfile {#3181
id: 1,
language_id: 139,
academic_prefix: null,
first_name: "Albin",
last_name: "Stoltenberg",
academic_suffix: null,
sex: "male",
created_at: "2018-07-08 15:20:00",
updated_at: "2018-07-08 15:20:00",
deleted_at: null,
},
}
It does work, if I change columns and everything else to profile_type and profile_id.
However, I didn't dive deeper into framework code, but this looks like a bug by loading polymorphic relations.
@themsaid Can you take a look at this?
You explicitly test the relation name in DatabaseEloquentModelTest::testMorphToCreatesProperRelation() (morphToStubWithName and morphToStubWithNameAndKeys). Aren't these tests incorrect?
@staudenmeir I think, my specific case is missing in these tests.
Testing my case could look like the following snippet.
Because the relationship is called via name in morphTo() and have nothing to do with the keys some_name in the tables.
// additional test scenario in testMorphToCreatesProperRelation()
// $this->morphTo('name');
$relation5 = $model->morphToStubWithOtherName();
$this->assertEquals('some_name_id', $relation5->getForeignKey());
$this->assertEquals('some_name_type', $relation5->getMorphType());
$this->assertEquals('name', $relation5->getRelation());
public function morphToStubWithOtherName()
{
return $this->morphTo('name');
}
I hope my thesis are correct.
Your relationship works if you specify both column names and don't specify a $name:
public function profile() {
return $this->morphTo(null, 'user_profile_type', 'user_profile_id');
}
I would have expected $this->morphTo('user_profile') to produce the same result. But apparently, that's a misconception.
Nevertheless, when using a custom relationship name, the "profile" => null entry shouldn't be there.
@staudenmeir wow, that works! 馃憤
I can see the misconception, but this looks like many dependencies in the framework.
Can you confirm your service provider, please? You specify the classes, but don't actually alias them up?
Also, why is your relationship method called profile and the morph is called user_profile?
@deleugpn you mean the morphMap() in the ServiceProvider? Yes, it is included.
The idea was to have another "Profile"-Model relation to other Models except "User"-Model, nothing to do with the current structure. So maybe in future, we have "Customer"-Models, they may can also have "Profile"-Models, but not the same as "User"-Models. So then, I would call the relation "profile" again and get a kind of "BusinessCustomerProfile"-Model. In case of that, I can diverse the morph-fields in the database better, when naming "user_profile" and "customer_profile".
@deleugpn was referring to the missing keys/aliases in your morphMap().
Is defined now, but also the same logic as @staudenmeir has evaluated.
Most helpful comment
Your relationship works if you specify both column names and don't specify a
$name:I would have expected
$this->morphTo('user_profile')to produce the same result. But apparently, that's a misconception.Nevertheless, when using a custom relationship name, the
"profile" => nullentry shouldn't be there.