【问题标题】:How to design database in order to store "default" items如何设计数据库以存储“默认”项目
【发布时间】:2020-01-08 14:54:56
【问题描述】:

我正在 Laravel 中开发一个应用程序。我有名为 Account 和 ShippingAddress 的模型。一个 Account 可能有一个或多个 ShippingAddress,但一个 ShippingAddress 只能有一个 Account。所以我使用的是一对多关系。

现在我想实现一个功能,将 ShippingAddress 标记为“默认”,并且每个“帐户”只能有一个“默认送货地址”。我已经确定了两种可能的解决方案:

  1. 在 ShippingAddress 表中添加一个“is_default”列。
  2. 创建一个“default_shipping_address”数据透视表,我将在其中存储“account_id”“和 shipping_address_id”。

哪一个是最适合您的解决方案,为什么?如果是第二个:如何通过数据透视表在 Laravel 中实现一对一关系?

【问题讨论】:

    标签: database laravel laravel-5 database-design


    【解决方案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\PivotDefaultShippingAddress,因为通过阅读文档,这听起来是正确的做法。然后我通过反转参数来实现您建议的方法。但是如果我尝试执行它返回一个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
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-17
        • 2011-01-24
        • 2018-02-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多