2016-02-16 2 views
6

Ich habe eine eins zu viele polymorphe Beziehung genau wie beschrieben in documentation, außer ich habe Stimmen statt Fotos und eine ganze Zahl Spalte in der Stimmen-Tabelle. Wie folgt aus:Laravel 5.2 Query Bereich auf polymorphe Beziehung

-votes 
--id 
--vote 
--voteable_id 
--voteable_type 

The Vote-Modell hat die voteable Methode:

public function voteable() 
{ 
    return $this->morphTo(); 
} 

Aber für Modelle, die ich will Stimmen haben an (Seite in diesem Beispiel) Ich habe ein VoteableTrait geschaffen, das hat die Votes() MorphMany-Methode und ein Bereich, den ich versuche zu arbeiten. Der Gültigkeitsbereich würde die Summe aller zu einem bestimmten Modell gehörenden Stimmen hinzufügen und die übergeordneten Modelle durch die Summe ordnen. Scope als ich versucht habe:

public function scopeWithVotesTrait($query) 
{ 
    return $query->with(['votes' => function($q){ 
     $q->select(\DB::raw("SUM(vote) AS votes"), 'voteable_id')->first(); 
    }]); 
} 

Aber damit kann ich nicht orderBy als Laravel 2 verschiedene Abfragen durchführt, eine für Eltern Modell und eine für Stimmen es scheint. Das Ergebnis der Seite :: withVotesTrait() -> first() setzt Stimmen in einem Array:

{ 
"id": 1, 
    "votes": [ 
    { 
     "votes": "-1", 
     "voteable_id": 1 
    } 
    ] 
} 

Die andere Idee, die ich hatte, war ein Join statt ausführen -> mit() wie folgt aus:

public function scopeWithVotesTrait($query, $model, $table) 
{ 
    return $query->join('votes', function($join) use($model, $table) 
    { 
     $join->on('votes.voteable_id', '=', $table . '.id') 
      ->where('votes.voteable_type', '=', $model); 
    })->select('*', \DB::raw('sum(vote) as votes')) 
    ->orderBy('votes')->groupBy($table . '.id'); 
} 

Aber wenn ich $ modell ('App \ ParentModel') und $ table ('parent_models') jedes Mal manuell eingeben muss, wenn ich den Bereich verwende, schlägt diese Art den Zweck, es in ein Merkmal zu setzen.

Gibt es also eine Möglichkeit, effizient nach der Summe der Stimmen in meinem ersten Beispiel des Umfangs zu ordnen? Oder gibt es eine statische Methode, die Sie von Eloquent Model aufrufen könnten, die den Modellnamen zurückgibt, und eine für die Modelltabelle, damit das zweite Beispiel funktioniert?

Irgendwelche anderen Ideen, wie man einen Bereich macht, der die Summe aller Stimmen an das Elternmodell anhängt und damit sortiert?

Antwort

4

Ich habe es mit dem zweiten Beispiel mit Join zu arbeiten. Eloquent \ Model verfügt über eine statische Methode getTable(), die den Tabellenname des Modells zurückgibt, sodass ich sie als $ model-Parameter dynamisch übergeben kann. Und ich PHP-Funktion get_class() wird den Namespace mit Klassennamen des übergeordneten Modells für $ Modellparameter zurückgeben.

Richtige Abfrage für das, was ich erreichen wollte:

public function scopeWithVotesTrait($query) 
{ 
    return $query->leftJoin('votes', function($join) use($model, $table) 
    { 
     $join->on('votes.voteable_id', '=', $table . '.id') 
      ->where('votes.voteable_type', '=', $model); 
    })->addSelect('*', $table . '.id', \DB::raw('COALESCE(SUM(vote),0) as votes')) 
    ->groupBy($table . '.id')->orderBy('votes'); 
} 

Ich habe auch hinzugefügt COALESCE, die Abfrage return 0 machen, wenn keine Stimmen zu finden sind. Das ist so, die Reihenfolge ist immer korrekt.

Ich habe auch * und parent_table.id angegeben, um Anweisungen auszuwählen. Ohne * liefert die Abfrage nur das, was wir in der select-Anweisung angeben. Und ohne parent_table.id gibt die Abfrage keine Modelle zurück, die wir mit Beziehungsmethoden abfragen könnten (Beispiel: Model :: withVotesTrait() -> with ('some_other_child') fügt some_other_child nicht zum Ergebnis hinzu).

Ich habe auch leftJoin anstelle von Join verwendet, so dass es immer alle Eltern zurückgibt, egal wie viele Stimmen damit verbunden sind.