2014-07-03 19 views
35

Zwei meiner Tabellen (Kunden und Produkte) haben eine ManyToMany-Beziehung mit Laravels blongToMany und einer Pivot-Tabelle. Jetzt möchte ich überprüfen, ob ein bestimmter Kunde ein bestimmtes Produkt hat.Überprüfen Sie, ob die angleToMany-Beziehung existiert - Laravel

Ich könnte ein Modell erstellen, in der Pivot-Tabelle zu überprüfen, aber da Laravel dieses Modell nicht für das Verfahren belongsToMany benötige ich mich gefragt, ob es eine andere Möglichkeit, wenn eine bestimmte Beziehung, ohne dass ein Modell für die Pivot-Tabelle besteht zu überprüfen ist, .

Antwort

96

Ich denke, der offizielle Weg, dies zu tun, um zu tun ist:

$client = Client::find(1); 
$exists = $client->products->contains($product_id); 

Es ist etwas verschwenderisch, dass es die Abfrage SELECT tun werden, alle Ergebnisse in ein Collection und dann tut endlich ein foreach über die Collection, um ein Modell mit der übergebenen ID zu finden. Es ist jedoch nicht erforderlich, die Pivot-Tabelle zu modellieren.

Wenn Sie die Verschwendung nicht mögen, könnten Sie es selbst im SQL/Query Builder tun, was auch keine Modellierung der Tabelle erfordert (noch würde es erforderlich sein, das Client Modell zu bekommen, wenn Sie es nicht schon tun haben sie für andere Zwecke:

$exists = DB::table('client_product') 
    ->whereClientId($client_id) 
    ->whereProductId($product_id) 
    ->count() > 0; 
+1

+1. Dies ist der offizielle Weg und sollte als beste Antwort markiert werden. – Arda

+0

Methode existiert nicht in 4.2 in equeststomany – DavidMIRV

+1

@DavidMIRV erhalten Sie diesen Fehler aus dem oberen Codebeispiel? Wenn ja, bist du sicher, dass du die Beziehung als Attribut nennst? Wenn Sie es als Methodenaufruf tun (d. H. '$ Exists = $ client-> products() -> enthält ($ product_id);') dann wird es tatsächlich sagen, dass 'contains' nicht in' BelongsToMany' existiert. Wenn Sie es jedoch als Attribut bezeichnen, wird das BelongsToMany automatisch in eine Collection konvertiert, die * contains() 'enthält. – alexrussell

7

Update: ich habe nicht berücksichtigt, den Nutzen mehrere Beziehungen zu prüfen, ob das der Fall ist, dann hat @deczo eine Art und Weise bessere Antwort auf diese Frage Laufen nur ein. Abfrage für alle Beziehungen ist die gewünschte Lösung

/** 
    * Determine if a Client has a specific Product 
    * @param $clientId 
    * @param $productId 
    * @return bool 
    */ 
    public function clientHasProduct($clientId, $productId) 
    { 
     return ! is_null(
      DB::table('client_product') 
       ->where('client_id', $clientId) 
       ->where('product_id', $productId) 
       ->first() 
     ); 
    } 

Sie könnten dies in Ihr Benutzer/Client-Modell einfügen, oder Sie könnten es in einem ClientRepository haben und verwenden, wo immer Sie es brauchen.

if ($this->clientRepository->clientHasProduct($clientId, $productId) 
{ 
    return 'Awesome'; 
} 

Aber wenn Sie bereits die belongsToMany Beziehung auf einem Client-Eloquent Modell definiert haben, können Sie dies, in Ihrem Client-Modell tun, statt:

return ! is_null(
     $this->products() 
      ->where('product_id', $productId) 
      ->first() 
    ); 
6

@ nielsiano Methoden funktionieren werden, aber sie werden Abfrage DB für jedes Benutzer/Produkt-Paar, was meiner Meinung nach eine Verschwendung ist.

Wenn Sie nicht wollen, alle zugehörigen Modelle Daten laden, dann ist es das, was ich für einen einzelnen Benutzer tun würde:

// User model 
protected $productIds = null; 

public function getProductsIdsAttribute() 
{ 
    if (is_null($this->productsIds) $this->loadProductsIds(); 

    return $this->productsIds; 
} 

public function loadProductsIds() 
{ 
    $this->productsIds = DB::table($this->products()->getTable()) 
      ->where($this->products()->getForeignKey(), $this->getKey()) 
      ->lists($this->products()->getOtherKey()); 

    return $this; 
} 

public function hasProduct($id) 
{ 
    return in_array($id, $this->productsIds); 
} 

Dann können Sie einfach tun:

$user = User::first(); 
$user->hasProduct($someId); // true/false 

// or 
Auth::user()->hasProduct($someId); 

Nur 1 Abfrage wird ausgeführt, dann arbeiten Sie mit dem Array.


wäre der einfachste Weg, contains wie @alexrussell vorgeschlagen werden.

Ich denke, dies ist eine Frage der Präferenz, also wenn Ihre App ist ziemlich groß und erfordert eine Menge Optimierung, können Sie wählen, was Sie leichter zu arbeiten finden.

+0

Große Antwort @deczo und erstaunliche Verwendung von Laravel eingebauten Helfern, um die Abfrage Dynamik in Bezug auf Tasten/Tabellen zu machen - ich tendiere dazu, mich selbst zu bemühen, aber dies bringt es auf ein neues Niveau! : D – alexrussell

+1

Danke, noch muss man viel mit Eloquent spielen, um zu erfahren, welche dieser Methoden nur einen Schlüssel 'col_id' zurückgeben und welche' table.col_id' qualifiziert/vorangestellt sind. Leider mangelt es Eloquent häufig an Konsistenz. –

+0

Ja - ich muss sagen, mit so viel Respekt für Taylor wie möglich, je mehr Sie sich in Laravel vertiefen, desto mehr irritierende Inkonsistenzen und Code, der nicht übermäßig klar ist (und ohne brauchbare Kommentare/Dokumentation).Aber hey, ich könnte es nicht besser machen! – alexrussell

3

Hallo alle) Meine Lösung für dieses Problem: Ich habe eine eigene Klasse, erweitert von Eloquent, und erweitern alle meine Modelle daraus.In dieser Klasse ich diese einfache Funktion geschrieben:

function have($relation_name, $id) { 
    return (bool) $this->$relation_name()->where('id','=',$id)->count(); 
} 

Für eine Überprüfung der vorhandenen Beziehung machen Sie so etwas wie schreiben müssen:

if ($user->have('subscribes', 15)) { 
    // do some things 
} 

Auf diese Weise erzeugt nur eine SELECT count (...) Abfrage ohne echte Daten von Tabellen zu erhalten.

1

Verwendung Merkmal:

trait hasPivotTrait 
{ 
    public function hasPivot($relation, $model) 
    { 
     return (bool) $this->{$relation}()->wherePivot($model->getForeignKey(), $model->{$model->getKeyName()})->count(); 
    } 
} 

.

if ($user->hasPivot('tags', $tag)){ 
    // do some things... 
} 
+0

Hat mein Problem nicht lösen, sondern half ich auf dem richtigen Weg bekommen, danke. – phaberest

3

Die Frage ist ziemlich alt, aber diese anderen helfen kann nach einer Lösung gesucht:

$client = Client::find(1); 
$exists = $client->products()->where('products.id', $productId)->exists(); 

Nein „Vergeudung“, wie in @ alexrussell-Lösung und die Abfrage ist effizienter, auch.

+0

ich Fehler erhalten '' 'SQLSTATE [42000]: Syntaxfehler oder Zugriffsverletzung: 1066 Nicht eindeutige Tabelle/Alias:' '', wenn ich versuche, dass in meinem Modell:/ – George

+0

@George Dann versuchen 'wo ('products.id' , $ productId) '. – Mouagip

+0

denke nicht, dass Sie Tabellenalias dort brauchen, ID ist genug. dies ist in der Produktbeziehung festgelegt. Die sauberste Lösung, sofar imho. – Johan

0

Alex's Lösung funktioniert, aber es wird ein Client Modell und alle damit verbundenen Modelle von DB in den Speicher laden und erst danach wird es prüfen, ob die Beziehung existiert.

Ein besserer eloquenter Weg, dies zu tun, ist whereHas() Methode zu verwenden.

1. Sie müssen das Client-Modell nicht laden, Sie können einfach seine ID verwenden.

2. Sie müssen auch nicht alle Produkte im Zusammenhang mit diesem Client in den Speicher laden, wie Alex tut.

3. Eine SQL-Abfrage an die DB.

$doesClientHaveProduct = Product::where('id', $productId) 
    ->whereHas('clients', function($q) use($clientId) { 
     $q->where('id', $clientId); 
    }) 
    ->count(); 
Verwandte Themen