6
votes

Introduction:

I have these Tables with their Models:

  • addresses table --> Address model
  • users table --> User model
  • companies table --> Company model
  • properties table --> Property model

Rules:

  • each User can have one address only.
  • each Company can have one address only.
  • each Property can have one address only.

Two possible relationships planning could be done here:

  • User hasOne Address and Address belongsTo User [i.e. one-to-one relationship] but the disadvantage is that the foreign key here is located in addresses table i.e. user_id field and by repeating the same relationship with other models [Company and Property] we will get another two foreign keys in the addresses table which are [company_id and property_id], then we will end up with 3 foreign keys for 3 Models But only one will be filled by each row record leaving always two foreign keys fields left empty

    I feel this is an overhead charge to addresses table.

OR the other way around:

  • Address hasOne User and User belongsTo Address [one-to-one relationship also]. This has the advantage that it keeps the foreign key here in the related Model i.e. [User, Company and Property] this way there is no empty fields of foreign keys --- But still this is against the real life Modelling logic which dictates that User hasOne Address more than Address hasOne User.

My Question:

Is there any other relationship that can bind these 4 Models in one relationship similar to Polymorphic Relations but without using MorphMany but instead using MorphOne which strangely enough, I can not find it in the Documentation of Laravel although the method itself exists in Eloquent Relations.

Is this MorphOne polymorphic relation possible and how it is composed?

Existing code:

//Address model
public function user(){
    return $this->hasOne(User::class);
}
public function company(){
    return $this->hasOne(Company::class);
}
public function property(){
    return $this->hasOne(Property::class);
}

//User model
public function address(){
    return $this->belongsTo(Address::class);
}

//Company model
public function address(){
    return $this->belongsTo(Address::class);
}
//Property model
public function address(){
    return $this->belongsTo(Address::class);
}
1
How about you save an id and type fields in address table. Here the id would be the id from user, company or property table and the type would store the name (user/company/property) to which this id belongs. How you can achieve that using eloquent I'm not sureGanymede
You can save the type field as enumGanymede
I agree with you as this looks to be the same as MorphOne Relationship that I am searching for. But I would be using Eloquent for that instead of doing it manually - this is to take advantage of the build-in Eloquent relationship instead of myself building setter and getters manually.WillKoz
I am mainly concerned about building the relationship than storing the data itself as I can limit data storage values and types through the AddressController directly in Laravel. What I am after is the Eloquent proper relationship between Address and the other 3 models User, Company and Property to use it in the further project code.WillKoz

1 Answers

9
votes

Here is the MorphOne relationship between Address on one side and User, Company and Property on the other-side:

class Address extends Model
    public function addressable(){
        return $this->morphTo();
    }       
}

class User extends Model
    public function address(){
        return $this->morphOne(Address::class,'addressable'); //note this is not MorphMany()
    }       
}

class Company extends Model
    public function address(){
        return $this->morphOne(Address::class,'addressable');
    }       
}

class Property extends Model
    public function address(){
        return $this->morphOne(Address::class,'addressable');
    }       
}

then in the UserController something like that:

$user = User::find(1);

$addressData = [
    'street_name' => '5, Golf street',
    'country_id' => 100, //etc..
];

//to add new address record related to user:
$user->address()->create($addressData);
dd($user->address);

//to update existing address record related to user:
$user->address->update($addressData);
//dd($user->address);