【问题标题】:Eager load a multiple indexes relationship with Laravel 5使用 Laravel 5 急切加载多索引关系
【发布时间】:2017-11-26 00:02:38
【问题描述】:

我目前正在做一个项目,我必须使用一个 10 年历史的数据库,该数据库的结构很糟糕。 无论如何,我有两张桌子:一张用于产品,一张用于颜色。每个产品都由参考标识,每种颜色都由其自己的参考 + 其产品的参考标识。 例如,我有一个产品编号为 123。该产品有 3 种颜色:123-01、123-02 和 123-03。这两个值分为两列:nocol(产品参考)和 nopat(颜色参考)。 我不能为每种颜色设置一个唯一的 ID,因为数据库每晚都会被删除,所以 ID 总是在变化(我知道这很奇怪)。

目前我有三个模型:Product、Color 和 CartItem。 My Color 模型与 Product 模型具有一对一的关系。我的 CartItem 与我的 Color 模型具有一对一的关系,其定义如下:

public function color() {
  return $this->hasOne('App\Color', 'nocol', 'color_ref')->where('nopat', $this->product_ref);
}

这很好,因为我可以使用简单的$cartItem->color 从 CartItem 中检索颜色。问题是它不适用于急切加载。当我尝试使用以下内容检索我的 CartItems 时:

CartItem::with('product', 'color')->get();

当我转储结果时,我最终得到了一个空关系。

当索引是两个单独的列时,有没有其他方法可以写出这种关系?


编辑 07-10

根据要求,这是一个非常简单的数据库视图。正如我所说,我必须使用无法修改的旧数据库。解决我的问题的最简单方法是在我的颜色表中添加一个唯一 ID,但我无法做到这一点。

我的产品模型有一个唯一的 ID pat_nopat。一种产品可以有多种颜色。我的颜色模型有两列 col_nopatcol_nocolcol_nopat 列引用产品 ID。这两列一起是 Color 模型的唯一 ID。

我的 OrderItem 模型引用了 col_nopatcol_nocol 的颜色,它们被称为 product_refcolor_ref

当我显示特定客户的订单历史时,我还通过 OrderItem 模型显示有关颜色的一些信息。问题是它执行了太多查询,因为我找不到一种方法来预先加载 OrderItem 和 Color 之间的关系。


编辑 07-11

我尝试使用 join 子句而不是 where 子句来更改我的关系。急切加载有效,但结果错误。

如果我有两个具有相同颜色参考但产品参考不同的 CartItem,我得到的结果是错误的。 假设我有两个这样的 CartItem:

product_ref | color_ref
----------- | ---------
1234        | 01
5678        | 01

我通过 where 关系得到了正确的结果,除了我不能急切加载。

但是,连接关系对两行都返回 1234-01,因为它获取每个颜色引用的第一个结果。 如果我查看调试栏中的查询,我会得到以下信息:

select * from `colors` inner join `cart_items` on `colors`.`col_nopat` = `cart_items`.`product_ref` where `colors`.`col_nocol` in ('01')
select * from `products` where `products`.`pat_nopat` in ('1234', '5678')

【问题讨论】:

  • 你能提供一个简单的 ERD,列上有 cmets 吗?
  • 我确实更新了问题。
  • 知道了这一点,您可能想使用LaravelTreats 来使用复合PK。

标签: php laravel eloquent


【解决方案1】:

加盟怎么样? (顺便问一下,这不是belongsTo关系而不是hasOne吗?)

public function color()
{
    return $this->belongsTo(Color::class, 'color_ref')
        ->join('order_items', 'colors.col_nopat', '=', 'order_items.product_ref');
}

在您进行编辑后,我发现此解决方案存在问题。在 Eloquent 中处理复合主键总是很困难,就像(某种程度上)这种情况。

您可以稍微作弊并急切加载所有匹配的颜色,然后按product_ref 过滤。这样您就可以避免为每个 CartItem 模型触发单独的查询。考虑一下:

// Product.php
public function colors()
{
    return $this->hasMany(Color::class, 'col_nopat', 'pat_nopat');
}

// ----

// CartItem.php
public function product()
{
    return $this->belongsTo(Product::class, 'product_ref');
}

public function getColorAttribute()
{
    return $this->product->colors
        ->where('col_nopat', $this->product_ref)
        ->first();
}

然后加载CartItem::with('product.colors')的关系,就可以$item->color了。


替代方案:

// CartItem.php
public function color()
{
    return $this->hasMany(Color::class, 'col_nocol', 'color_ref')
        ->select('colors.*')
        ->join('items', function ($join) {
            $join->on('colors.col_nocol', '=', 'items.color_ref');
            $join->on('colors.col_nopat', '=', 'items.product_ref');
        });
}

public function getColorAttribute()
{
    return $this->getRelation('color')
        ->where('col_nopat', $this->product_ref)
        ->first();
}

(在此您可以避免通过product 关系。)

【讨论】:

  • 我会提供问题中的所有模型,包括它们的关系并对其进行测试以获得赏金:)
  • @ThomasMoors 我会,但似乎很明显 OP 已经可以正常工作了。仅缺少此 color 关系
  • 非常感谢这确实有效 :) 看起来我必须等待 24 小时才能获得赏金,但我肯定会这样做。 @ThomasMoors 也感谢你 ;)
  • 当然没问题!
  • 感谢您更新您的答案。您的第一个解决方案似乎工作正常!这次我要三重检查:) 我只需要在getColorAttribute 函数中将col_nopatproduct_ref 更改为col_nocolcolor_ref。然而,第二个不起作用。我收到一条错误消息,指出该关系不存在。如果有什么东西坏了我会告诉你,否则我不会原谅给你赏金的;)
【解决方案2】:

将关系定义为:

public function color()
{
    return $this->hasOne('App\Color', 'nocol', 'color_ref');
}

然后使用eager loading constraint:

CartItem::with(['product', 'color' => function ($q) use ($productRef) {
        $q->where('nopat', $productRef);
    }])
    ->get();

【讨论】:

  • productRefCartItem 表上,所以这不会像你想的那样工作。
  • 没错,我不能这样做,因为当我想检索我所有的 CartItems 时,我没有 productRef
猜你喜欢
  • 2016-06-06
  • 2021-12-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-16
  • 2017-04-12
  • 2021-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多