如何设计数据库以存储“默认”项目
Posted
技术标签:
【中文标题】如何设计数据库以存储“默认”项目【英文标题】:How to design database in order to store "default" items 【发布时间】:2020-01-08 14:54:56 【问题描述】:我正在 Laravel 中开发一个应用程序。我有名为 Account 和 ShippingAddress 的模型。一个 Account 可能有一个或多个 ShippingAddress,但一个 ShippingAddress 只能有一个 Account。所以我使用的是一对多关系。
现在我想实现一个功能,将 ShippingAddress 标记为“默认”,并且每个“帐户”只能有一个“默认送货地址”。我已经确定了两种可能的解决方案:
-
在 ShippingAddress 表中添加一个“is_default”列。
创建一个“default_shipping_address”数据透视表,我将在其中存储“account_id”“和 shipping_address_id”。
哪一个是最适合您的解决方案,为什么?如果是第二个:如何通过数据透视表在 Laravel 中实现一对一关系?
【问题讨论】:
【参考方案1】:恕我直言,考虑到一个 ShippingAddress
只属于一个 Account
,恕我直言,使用数据透视表似乎有点多余,第一个解决方案对我来说看起来更干净。
顺便说一句,您可以通过这种方式或类似的方式设计Models
:
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');
这样你可以eager load'shipping_addresses'
或者只有'default_shipping_address'
当你检索Account
模型时,例如:
$account = Account::with('shipping_addresses')->find($id);
$account = Account::with('default_shipping_address')->find($id);
显然你不需要eager load
这两个关系,因为shipping_addresses
已经包含了默认送货地址。
您只需通过验证 (unique rule) 或数据库 constraints 或两者都检查代码中是否只有 一个 default shipping address
。
【讨论】:
【参考方案2】:我个人的观点是,在单个收藏地址的情况下,使用数据透视表虽然从形式上看可能更正确,但只会增加查询的复杂性。相反,通过在地址中添加一个标志列来确定它何时是收藏夹,这将使查询更容易。
现在,如果你想使用数据透视表,你应该使用如下函数在 Account 模型中建立关系:
public function defaultShippingAddress()
return $this->hasOneThrough('App\Models\AccountShippingAddress', 'App\Models\ShippingAddress');
在这里您可以阅读有关它的整个文档https://laravel.com/docs/5.8/eloquent-relationships#has-one-through
问候。
【讨论】:
我想探索这个答案,但我不太明白。我将按如下方式设计我的数据库:accounts [id, ...]
、shipping_addresses [id, account_id, ...]
、default_shipping_address [account_id, shipping_address_id]
。现在我创建了一个扩展Illuminate\Database\Eloquent\Relations\Pivot
的DefaultShippingAddress
,因为通过阅读文档,这听起来是正确的做法。然后我通过反转参数来实现您建议的方法。但是如果我尝试执行它返回一个QueryException
的方法:它无法检索shipping_addresses.id
。我做错了什么?
让我尝试实现您的模型,我会回来提供更好的解释。【参考方案3】:
我会选择选项 1,因为它更简单且没有缺点。为了确保每个Account
只有一个默认ShippingAddress
,您需要监听saving
事件并进行如下验证:
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
【讨论】:
以上是关于如何设计数据库以存储“默认”项目的主要内容,如果未能解决你的问题,请参考以下文章