Laravel 5

Requête dynamique avec table pivot

  • Avatar de g_bu
    Membre depuis :
    29/06/2018
    Messages :
    8

    Bonjour,
    J'ai de la peine à m'en sortir avec la construction d'une requête dynmaique avec une relation "belongsToMany".
    J'ai une table products et une table attributs.
    Un produit peut appartenir à plusieurs attributs et un attribut peut appartenir à plusieurs produits.
    J'aimerais par exemple retrouver tous les produits qui sont liés à la fois à l'attribut 1 ET à l'attribut 2. La requête suivante fonctionne:

    $products = Product::whereHas('attributes', function ($q) {
    $q->where('attribute_id', '=', 1);
    })->whereHas('attributes', function ($q) {
    $q->where('attribute_id', '=', 2);
    })->get();

    J'aimerais maintenant retrouver tous les produits qui sont liés aux attributs dont les ID sont contenus dans un tableau:
    $tab_id_attributes = array(1,2,3,4,8,9);
    Comment puis-je construire cette requête?

    Merci d'avance pour votre aide!!!

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    2299

    Salut,

    $products = Product::whereHas('attributes', function ($q) {
    $q->whereIn('attribute_id', [1,2,3,4,8,9]);
    })->get();
  • Avatar de g_bu
    Membre depuis :
    29/06/2018
    Messages :
    8

    Merci beaucoup pour ton aide!
    Cette solution ne fonctionne pas... car si un produit est lié seulement à un de ces attributs, il sera présent dans la collection. J'aimerais que seul les produits qui sont liés à TOUS ces attributs soient présents dans la collection...
    J'imaginais quelque chose comme ça (évidemment ça ne fonctionne pas, mais c'est pour présenter l'idée):

    $products = Product::all();
    foreach ($attribute_id as $id) {
    $products->whereHas('attributes', function ($q) {
    $q->where('attribute_id', '=', $id);
    });
    }
    $products->get();
  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    2299

    Ah ce n'est pas OU mais ET...

    Je m'interroge sur la structure de la relation attributes. Classiquement avec belongsToMany on ne peut pas se retrouver avec le champ attribute_id.

  • Avatar de g_bu
    Membre depuis :
    29/06/2018
    Messages :
    8

    J'ai ça comme relation dans mon model Product:

    public function attributes()
    {
    return $this->belongsToMany('App\Models\Attribute', 'product_attribute')->withPivot('value');
    }

    Avec comme tables:

    products
    id
    name
    ...

    attributes
    id
    name
    ...

    product_attribute
    attribute_id
    product_id
    value
  • Avatar de g_bu
    Membre depuis :
    29/06/2018
    Messages :
    8

    Il semblerait que la solution suivante fonctionne (je n'ai pas encore fait énormément de tests):
    Dans mon modèle Product:

    public function scopeTabattributes($query, $tab_id)
    {
    foreach($tab_id as $id){
    $query->whereHas('attributes', function($q) use($id){
    $q->where('attribute_id', '=', $id);
    });
    }
    return $query;
    }

    Et dans mon controller:
    $products = Product::Tabattributes([1,2,3,4,8,9])->get();

    Je dois avouer que ça m'étonne qu'il n'y ait pas une solution plus propre pour faire cela. Si quelqu'un a une solution moins "tordue", je suis preneur!

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    2299

    Essaie comme ça :

    $attribut_id = [1,2,3,4,8,9];
    $products = Product::whereHas('attributes', function ($q) use ($attribut_id) {
    foreach ($attribute_id as $id) {
    $q->where('attributs.id', $id);
    }
    })->get();

    Il est aussi possible d'établir une relation hasMany avec la table pivot mais ce n'est pas très conventionnel...

  • Avatar de g_bu
    Membre depuis :
    29/06/2018
    Messages :
    8

    Merci pour ta proposition!
    Mais ça ne fonctionne pas... ma collection est vide.

    Je crois qu'il faut vraiment avoir plusieurs "whereHas".
    Je vais garder ma fonction scope. Elle me sera utile à d'autres endroits également.

    Merci pour ton aide @bestmomo!

Vous ne pouvez pas répondre à ce sujet.