I'm developing an app in Laravel. I've models called Account and ShippingAddress. An Account may have one or more ShippingAddress, but a ShippingAddress can only have one Account. So that I'm using a one-to-many relation.
Now I want to implement a functionality that marks a ShippingAddress as "default", and there's can be only one "default Shipping Address" per "Account". I've identified two possible solutions:
Which one is the best solution up to you and why? In case of the second one: how can I implement a one-to-one relation in Laravel through a pivot table?
IMHO using a pivot table seems a bit redundant considering that one ShippingAddress
belongs to only one Account
, the first solution looks cleaner to me.
BTW you can design the Models
in this way or something like:
class Account extends Model
{
public function shipping_addresses()
{
return $this->hasMany('App\ShippingAddress');
}
public function default_shipping_address()
{
return $this->hasOne('App\ShippingAddress')->where('is_default', true);
}
}
class ShippingAddress extends Model
{
public function account()
{
return $this->belongsTo('App\Account');
}
}
In this way you can eager load 'shipping_addresses'
or only 'default_shipping_address'
when you retrieve the Account
model, eg:
$account = Account::with('shipping_addresses')->find($id);
$account = Account::with('default_shipping_address')->find($id);
Obviously you should not need to eager load
both the relationships togheter because shipping_addresses
already include the defaut shipping address .
You have only to check that there is only one default shipping address
in your code either with validation ( unique rule ) or database constraints or both.
I would go with option 1 because it is simpler and has no downsides. In order to make sure you have only one default ShippingAddress
per Account
you listen for the saving
event and do some validation like:
class ShippingAddress extends Model
{
// ... other code
public function boot()
{
parent::boot();
static::saving(function (ShippingAddress $shippingAddress) {
if (! $shippingAddress->is_default) {
return;
}
$defaultAlreadyExists = static::where([
['account_id', '=', $shippingAddress->account_id],
['id', '!=', $shippingAddress->id],
['is_default', '=', true],
])->exists();
if ($defaultAlreadyExists) {
throw new YourCustomException("Account with id {$shippingAddress->account_id} already has a default shipping address");
}
});
}
// ... other code
}
my personal point of view, is that in the case of a single favorite address, using a pivot table, although it may be more correct from the formal, would only be adding a complication to the query. Instead by adding a flag column to the address to determine when it is favorite, it would make the query easier.
Now, if you want to use a pivot table, you should establish a relationship in Account model using a function such as:
public function defaultShippingAddress()
{
return $this->hasOneThrough('App\Models\AccountShippingAddress', 'App\Models\ShippingAddress');
}
Here you can read the whole documentation about it https://laravel.com/docs/5.8/eloquent-relationships#has-one-through
Regards.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.