【问题标题】:Has many through many-to-many通过多对多有很多
【发布时间】:2016-09-22 15:35:15
【问题描述】:

我有以下表格布局:

deals:
- id
- price

products:
- id
- name

deal_product:
- id
- deal_id
- product_id

metrics:
- id
- name

metric_product:
- id
- metric_id
- product_id
- value

productsmetrics 与值的枢轴列具有多对多关系。

dealsproducts 也具有多对多关系。

我可以使用 $product->metrics 获取产品的指标,但我希望能够获得与交易相关的所有产品的所有指标,因此我可以执行以下操作:$deal->metrics

我目前在我的Deal 模型中有以下内容:

public function metrics()
{
    $products = $this->products()->pluck('products.id')->all();

    return Metric::whereHas('products', function (Builder $query) use ($products) {
        $query->whereIn('products.id', $products);
    });
}

但这不会返回关系,所以我不能急切地加载它或从中获取相关模型。

它需要采用关系格式,因为它们需要为我的用例预先加载。

感谢您的帮助!

【问题讨论】:

  • 我曾一度落入同一个兔子洞;这是我为社区提出并回答的问题。老实说,我不是 100% 确定这是最干净的解决方案,但我没有时间回去重新考虑它。但是,它确实有效,您可以让它在尽可能多的表中工作,以获得这种关系Have a look here
  • @Ohgodwhy 这行得通!但我忘了提到我需要它以标准数据透视格式返回metric_product 中的value 列。如果您不确定如何执行此操作,我将创建一个新问题。

标签: laravel laravel-5 laravel-5.2


【解决方案1】:

我没有足够的代表发表评论,所以我会发布一个答案。

几天前我发布了一个类似的question 并且能够自己回答。我认为该解决方案也适用于这个问题。

通过手动加入表并添加 where 子句,您可以检索所需的指标。

可能的解决方案:

// Deal.php
public function metrics()
{
    return Metric

        ::join('metric_product', 'metric.id', '=', 'metric_product.metric_id')

        ->join('products', 'metric_product.product_id', '=', 'products.id')

        ->join('deal_product', 'products.id', '=', 'deal_product.product_id')

        ->join('deals', 'deal_product.deal_id', '=', 'deal.id')

        ->where('deal.id', $this->id);

}

// How to retrieve metrics
$deal->metrics()->get();

// You can still use other functions like (what I needed) pagination
$deal->metrics()->paginate(24);

// Or add more where clauses
$deal->metrics()->where(blablabla)->get();

希望这会有所帮助:)

@ntzm 评论后编辑

如果您只需要指标表的一些属性而不需要模型功能,您可以在连接之前添加一个 select('*')。

像这样:

return Metric

    ::select('*')

    ->join(...........

它将返回一个包含连接表的所有属性的 Metric 模型。

这个例子将返回一个像这样的对象:

Metric (
    ....
        attributes: [
            id => 0, // From metrics table
            name => 'somename', // Metrics table
            metric_id => 0, // metric_product table
            product_id => 0, // metric_product table
            ....
        ]
    ....
)

由于有很多同名的列,您将无法访问这些值。但是,您可以多选并重命名结果中的列。

像这样:

retrun Metric

    ::select([
        *,
        DB::raw('products.name as product_name'),
        ....
    ])

    ->join(....

这将导致类似:

Metric (
    ....
        attributes: [
            id => 0, // From metrics table
            name => 'somename', // Metrics table
            metric_id => 0, // metric_product table
            product_id => 0, // metric_product table
            product_name => 'somename' // products table
            ....
        ]
    ....
)

我希望这能解决一些问题 :) 当然有很多更简洁的方法可以解决您的问题。

【讨论】:

  • 谢谢你,但不幸的是我需要预先加载关系,这不允许我这样做。
  • 我想你误会了。为了预先加载关系 (Deal::with('metrics')),$deal->metrics() 需要返回一些 Illuminate\Database\Eloquent\Relations\Relation 的实例。
  • 是的,但在示例中 ::with() 没有使用。如示例所述:使用 select(DB:raw([...]) 检索名称不明确的列。但是您将无法使用 Product 和 Deal 模型的功能。选择取决于您想要什么检索后处理指标。
  • 我在 OP 中确实提到过。 >它需要采用关系格式,因为它们需要为我的用例预先加载。感谢您的帮助
  • Np :) 我希望这个答案在其他方面仍然有用。
【解决方案2】:

这应该可以解决问题:

Models/Product.php 文件:

class Product extends Model
{
    public function deals()
    {
        return $this->belongsToMany(Deal::class);
    }

    public function metrics()
    {
        return $this->belongsToMany(Metric::class);
    }
}

Models/Deal.php 文件:

class Deal extends Model
{
    public function products()
    {
        return $this->belongsToMany(Product::class);
    }
}

Models/Metric.php 文件:

class Metric extends Model
{
    public function products()
    {
        return $this->belongsToMany(Product::class);
    }
}

从您的控制器:

class ExampleController extends Controller
{
    public function index()
    {
        $deal = Deal::with('products.metrics')->get();
    }
}

现在您拥有一系列产品交易及其相关指标。请参阅 docs 以了解嵌套预加载。

【讨论】:

  • 不幸的是,我需要Deal 才能拥有一个返回关系的metrics 方法。
【解决方案3】:

没有可用的“多对多通过”方法。

要获取某个交易的所有产品中可用的所有指标,请创建一个 getter 来急切加载该交易的所有产品和指标,并利用 Laravel 的 Collections 方法。

在您的Deal 模型中:

public function products()
{
    return $this->belongsToMany('App\Product');
}

public function getMetrics()
{
    return $this->products->lists('metrics')->collapse()->values();
}

然后您可以通过$deal->getMetrics() 获取交易的指标

这将返回一个 Metrics 对象的集合,这应该正是您正在寻找的。​​p>

【讨论】:

    【解决方案4】:

    如果您想拥有一个自定义关系,您可以创建自己的扩展关系抽象类。例如:BelongsToManyThought

    但是如果你不想实现一个 Relation,我认为它可以满足你的需求:

    在App\Deal.php中可以结合@thomas-van-der-veen的解决方案

    public function metrics()
    {
        return Metric
    
        ::join('metric_product', 'metric.id', '=', 'metric_product.metric_id')
    
        ->join('products', 'metric_product.product_id', '=', 'products.id')
    
        ->join('deal_product', 'products.id', '=', 'deal_product.product_id')
    
        ->join('deals', 'deal_product.deal_id', '=', 'deal.id')
    
        ->where('deal.id', $this->id);
    
    }
    
    
    // you can access to $deal->metrics and use eager loading
    public function getMetricsAttribute()
    {
        if (!$this->relationLoaded('products') || !$this->products->first()->relationLoaded('metrics')) {
            $this->load('products.metrics');
        }
    
        return collect($this->products->lists('metrics'))->collapse()->unique();
    }
    

    您可以参考此post 了解如何简单地使用嵌套关系。

    这个解决方案可以用来查询与方法的关系和访问指标属性。

    【讨论】:

    • lists 方法在 Laravel 5.3 中变成了“pluck”。所以这条线应该是 $this->products->pluck('metrics')
    【解决方案5】:

    如果有人仍然需要它:

    首先,您将渴望加载产品和指标

    $deals->with('products.metrics');
    

    在交易模式中

    public function getMetricsAttribute(){
       return $this->products->pluck('metrics')->collapse();
    }
    

    现在您可以通过以下方式获取交易产品的所有指标:

    $deal->metrics
    

    【讨论】:

      【解决方案6】:

      有一个 Laravel 5.5 composer 包可以执行多级关系(深度)

      包装: https://github.com/staudenmeir/eloquent-has-many-deep

      例子:

      User → 属于许多 → Role → 属于许多 → Permission

      class User extends Model
      {
          use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
      
          public function permissions()
          {
              return $this->hasManyDeep(
                  'App\Permission',
                  ['role_user', 'App\Role', 'permission_role'], // Pivot tables/models starting from the Parent, which is the User
              );
          }
      }
      

      是否需要定义外键的例子:

      https://github.com/staudenmeir/eloquent-has-many-deep/issues/7#issuecomment-431477943

      【讨论】:

      【解决方案7】:

      如果有人还在研究这个,请查看我最近开发的这个包。它为您提供belongsToManyThrough() 关系,并且非常易于安装和使用:)

      https://github.com/shomisha/unusual-relationships

      【讨论】:

        猜你喜欢
        • 2014-02-12
        • 2021-11-16
        • 1970-01-01
        • 2020-03-10
        • 2012-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多