web-dev-qa-db-ja.com

多から多対多

次のテーブルレイアウトがあります。

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);
    });
}

しかし、これはリレーションシップを返さないため、熱心にロードしたり、関連モデルを取得したりできません。

彼らは私のユースケースのために熱心にロードされる必要があるので、それは関係フォーマットである必要があります。

ご協力いただきありがとうございます!

18
ntzm

カスタムリレーションが必要な場合は、独自の拡張をRelation抽象クラスに作成できます。例:BelongsToManyThought

しかし、リレーションを実装したくない場合、私はそれがあなたのニーズを満たすことができると思います:

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 を参照して、ネストされた関係を簡単に使用する方法を確認できます。

このソリューションは、メソッドとの関係をクエリし、メトリックス属性にアクセスするためのトリックを実行できます。

13
mpur

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-43147794

4
NicksonYap

これでうまくいくはずです:

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 を参照してください。

3
Rafael Berro

コメントを投稿するのに十分な担当者がいないため、代わりに回答を投稿します。

数日前、私は同様の 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 (
    ....
        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
            ....
        ]
    ....
)

私はこれが何かを解決することを願っています:)もちろん、あなたの問題を解決する多くのよりきれいな方法があります。

3

「多対多」の方法はありません。

特定のディールのすべての製品で使用可能なすべてのメトリックを取得するには、ディールのすべての製品とメトリックをEager Loadするゲッターを作成し、Laravelのコレクションメソッドを利用します。

Dealモデル:

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

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

次に、$deal->getMetrics()を使用してDealのメトリックを取得できます。

これにより、Metricsオブジェクトのコレクションが返されます。これは、まさに目的のオブジェクトでなければなりません。

1
crabbly

それでも誰かがそれを必要とする場合:

まず、商品と指標を熱心に読み込みます

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

取引モデル

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

これで、取引の製品のすべてのメトリックを取得できます。

$deal->metrics
0
Zeyad Mounir

まだ誰かがこれを研究している場合は、私が最近開発したこのパッケージをチェックしてください。 belongsToManyThrough()関係を提供し、インストールと使用が非常に簡単です:)

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

0
Misa Kovic