Laravel 5

Requête dynamique avec table pivot

Avatar de g_bu
g_bu

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

Posté il y a 1 an
Avatar de bestmomo
bestmomo

Salut,

$products = Product::whereHas('attributes', function ($q) {
         $q->whereIn('attribute_id', [1,2,3,4,8,9]);
})->get();
Posté il y a 1 an
Avatar de g_bu
g_bu

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();
Posté il y a 1 an
Avatar de bestmomo
bestmomo

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.

Posté il y a 1 an
Avatar de g_bu
g_bu

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
Posté il y a 1 an
Avatar de g_bu
g_bu

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!

Posté il y a 1 an
Avatar de bestmomo
bestmomo

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

Posté il y a 1 an
Avatar de g_bu
g_bu

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!

Posté il y a 1 an
Avatar de Weneed_livraison
Weneed_livraison

Bonjour,

J'ai : -Une table "products" -Une table "categories" -Une table pivot belongs to many "category_product"

La table "categories" a une colonne "code_postal" car des produits sont disponibles seulement sur certains code postaux.

Sur la page principale, l'utilisateur remplit un formulaire avec son code postal et une vue doit être retournée avec les produits disponibles pour son code postal.

Je bloque au bout de 12h de recherche... Pouvez-vous m'aider s'il vous plaît ?

Le formulaire ↓

<form action="{{ route('products.searchcp') }}" class="d-flex mr-3">
    <div class="form-group mb-0 mr-1">
        <input type="text" name="cp" class="form-control" value="{{ request()->cp ?? '' }}">
    </div>
    <button type="submit" class="btn btn-info"><i class="fa fa-search" aria-hidden="true"></i></button>
</form>

ProductController.php↓

public function searchcp()
    {
        request()->validate([
            'cp' => 'required|min:5'
        ]);

        $cp = request()->input('cp');

        $products = Product::with('categories')->whereHas('categories', function ($query){
                        $query->where('code_postal', request()->input('cp'));
                        })->paginate(6);
                             
                    

        return view('products.searchcp')->with('products', $products);
    }

La Vue ↓

extends('layouts.master')

@section('content')
      @foreach($products as $product)
  <div class="col-md-6">
     <div class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">
       <div class="col p-4 d-flex flex-column position-static">
          <!--<strong class="d-inline-block mb-2 text-success">Design</strong>-->
          <span class="badge badge-dark"> 
            @foreach ($product->categories as $category)
                {{ $category->code_postal }}{{ $loop->last ? '' : ', '}}
            @endforeach
          </span>
          <h5 class="mb-0">{{$product->title}}</h5>
          <div class="mb-1 text-muted">{{$product->created_at->format('d/m/Y')}}</div>
          <p class="mb-auto">{{$product->subtitle}}</p>
          <strong class="mb-auto">{{$product->getPrice()}}</strong>
          <a href=" {{route('products.show', $product->slug)}} " class="stretched-link btn btn-dark">Voir l'article</a>
        </div>
        <div class="col-auto d-none d-lg-block">
          <img src="{{ asset('storage/' . $product->image) }}"alt="">

      </div>
     </div>
   </div>
  @endforeach
  {{ $products->appends(request()->input())->links() }}
@endsection

L'erreur retournée :

Facade\Ignition\Exceptions\ViewException Call to a member function format() on null (View: C:\laragon\www\site\resources\views\products\searchcp.blade.php)

Console erreur ↓

@extends('layouts.master')


@section('content')

      @foreach($products as $product)

  <div class="col-md-6">

     <div class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">

       <div class="col p-4 d-flex flex-column position-static">

          <!--<strong class="d-inline-block mb-2 text-success">Design</strong>-->

          <span class="badge badge-dark">

            @foreach ($product->categories as $category)

                {{ $category->code_postal }}{{ $loop->last ? '' : ', '}}

            @endforeach

          </span>

          <h5 class="mb-0">{{$product->title}}</h5>

          <div class="mb-1 text-muted">{{$product->created_at->format('d/m/Y')}}</div>

          <p class="mb-auto">{{$product->subtitle}}</p>

          <strong class="mb-auto">{{$product->getPrice()}}</strong>

          <a href=" {{route('products.show', $product->slug)}} " class="stretched-link btn btn-dark">Voir l'article</a>

        </div>

        <div class="col-auto d-none d-lg-block">

          <img src="{{ asset('storage/' . $product->image) }}"alt="">


      </div>

     </div>

   </div>

  @endforeach

  {{ $products->appends(request()->input())->links() }}

@endsection

Vous ne pouvez pas répondre à ce sujet.