渴望加载:在具有雄辩关系的枢轴上使用`with`

时间:2016-06-01 14:56:06

标签: php laravel laravel-5 eloquent

共有4个表:

  • bundles:id,name
  • products:id,name
  • prices:id,name
  • bundle_product:id,bundle_id,product_id,price_id

有3种型号:

  • Bundle
  • Product
  • Price

ProductPrice时有一个Bundle。我想让所有bundles与关联的products 和关联的prices保持一致。我可以获得所有产品和价格ID的捆绑包:

// I created a Bundle Model with a products method
class Bundle extends Model
{
    public function products()
    {
        return $this->belongsToMany(Product::class)->withPivot('price_id');
    }
}

// Then I call this in a controller
$all_bundles = Bundle::with('products')->get();

// Then I can get the price Id of the first product of the first bundle
$price_id = Bundle::with('products')->first()
    ->products()->first()
    ->pivot->price_id;

但我不想要价格ID ,我想要价格型号。有没有办法从枢轴预加载价格(使用Eager Loading)?

4 个答案:

答案 0 :(得分:5)

一种解决方案可能是为枢轴添加BundleProduct模型。然后将BundleProduct对象链接到Bundle模型:

class Bundle extends Model
{
    public function bundleProducts()
    {
        return $this->hasMany(BundleProduct::class, 'bundle_id', 'id');
    }
}

要在此捆绑包中获取所有捆绑包及其相关产品和价格,请执行以下操作:

Bundle::with('bundleProducts.product', 'bundleProducts.price')->get();

这对我有用,希望它可以帮助别人。

答案 1 :(得分:2)

文档:Eloquent: Relationships: Many To Many

  

默认情况下,只有模型键才会出现在数据透视对象上。如果数据透视表包含额外属性,则必须在定义关系时指定它们:

     

return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');

这意味着withPivot()方法只会向pivot对象添加缺少的字段。要检索实际的Price模型,您必须建立另一种关系。它看起来像这样:

/**
 * Class Bundle
 * 
 * Bundle model with the products relationship
 */
class Bundle extends Model
{
    public function products()
    {
        return $this->belongsToMany(Product::class);
    }
}

/**
 * Class Product
 * 
 * Product model with the prices relationship
 */
class Product extends Model
{
    public function prices()
    {
        return $this->belongsToMany(Price::class);
    }
}

/**
 * Class Price
 * 
 * Simple model, end result
 */
class Price extends Model
{
    // Model definition...
}

这是基本的,当然不是完整的代码,只有答案所需的功能。

在此之后,您需要做的就是检索关系,如下所示:

// You can query a child relationship using the dot (.). Prices collection will be accessible within each member of the products collection 
$all_bundles = Bundle::with('products.prices')->get();

$price_id = $all_bundles->first()
    ->products->first()
    ->prices->first()->id;

答案 2 :(得分:2)

我认为你会为一种单独加载价格模型的方法而苦苦挣扎,除非有办法将父母(在这种情况下是产品)传递给一个封闭 - 我和我一样。我很确定没有。

$all_bundles = Bundle::with(['products.prices' => function($query) use (PARENT) {
    $query->where('id', PARENT->pivot->price_id);
}])->get();

在初始查询之后,你可以这样做只加载相关的价格模型而不是所有的价格模型:

$all_bundles = Bundle::with('products')->get();

foreach($all_bundles as $bundle) {
    foreach($bundle->products as $product) {
        $product->load(['prices' => function($query) use ($product) {
            $query->where('id', $product->pivot->price_id);
        }]);
    }
}

答案 3 :(得分:1)

当前接受的答案与原始数据结构不同。 我创建了一个程序包,该程序包可以帮助您实现所需的功能,并且还可以维护原始数据结构。请在此处阅读中等故事:https://medium.com/@ajcastro29/laravel-eloquent-eager-load-pivot-relations-dba579f3fd3a

首先,在您的情况下,创建自定义枢轴模型并在枢轴模型上定义关系:

use Illuminate\Database\Eloquent\Relations\Pivot;

class BundleProduct extends Pivot
{
    public function price()
    {
        return $this->belongsTo(Price::class);
    }
}

然后在关系中使用数据透视模型:

class Bundle extends Model
{
    public function products()
    {
        return $this->belongsToMany(Product::class)
        ->withPivot('price_id') // this is needed to query the relation `price`
        ->using(BundleProduct::class);
    }
}

确保在AjCastro\EagerLoadPivotRelations\EagerLoadPivotTrait模型中使用特征Product,因为它是belongsToMany关系中的相关模型。这使我们能够急切地加载枢轴关系。

use AjCastro\EagerLoadPivotRelations\EagerLoadPivotTrait;

class Product extends Model 
{
  use EagerLoadPivotTrait;
}

然后渴望像这样加载它:

$bundle = Bundle::with('products.pivot.price')->first();
$price = $bundle->products->first()->pivot->price;