Ich habe ein Animal
Modell, basierend auf der animal
Tabelle.
Diese Tabelle enthält ein type
Feld, das Werte wie Katze oder Hund enthalten kann .
Ich möchte Objekte erstellen können wie:
class Animal extends Model { }
class Dog extends Animal { }
class Cat extends Animal { }
Ein Tier wie dieses holen zu können:
$animal = Animal::find($id);
Aber wo $animal
wäre eine Instanz von Dog
oder Cat
abhängig von dem type
Feld, das ich mit überprüfen kann instance of
oder das mit typgeführten Methoden funktioniert. Der Grund ist, dass 90% des Codes geteilt werden, aber einer bellen kann und der andere miauen kann.
Ich weiß, dass ich das kann Dog::find($id)
, aber es ist nicht das, was ich will: Ich kann den Typ des Objekts erst bestimmen, wenn es abgerufen wurde. Ich könnte auch das Tier holen und dann find()
auf dem richtigen Objekt ausführen , aber dies führt zwei Datenbankaufrufe aus, die ich offensichtlich nicht möchte.
Ich habe versucht, nach einer Möglichkeit zu suchen, ein eloquentes Modell wie Dog from Animal "manuell" zu instanziieren, konnte jedoch keine entsprechende Methode finden. Irgendeine Idee oder Methode, die ich bitte verpasst habe?
*_type
Namen, um das Subtypmodell zu bestimmen. In meinem Fall habe ich wirklich nur einen Tisch, also ist es zwar eine nette Funktion, aber nicht in meinem Fall.Antworten:
Sie können die polymorphen Beziehungen in Laravel verwenden, wie in den offiziellen Laravel-Dokumenten erläutert . So können Sie das machen.
Definieren Sie die Beziehungen im Modell wie angegeben
Hier benötigen Sie zwei Spalten in der
animals
Tabelle, die erste istanimable_type
und eine andereanimable_id
, um den Typ des Modells zu bestimmen, das zur Laufzeit daran angehängt ist.Sie können das Hunde- oder Katzenmodell wie angegeben abrufen.
Danach können Sie die
$anim
Klasse des Objekts mit überprüfeninstanceof
.Dieser Ansatz hilft Ihnen bei der zukünftigen Erweiterung, wenn Sie der Anwendung einen anderen Tiertyp (z. B. Fuchs oder Löwe) hinzufügen. Es wird funktionieren, ohne Ihre Codebasis zu ändern. Dies ist der richtige Weg, um Ihre Anforderung zu erfüllen. Es gibt jedoch keinen alternativen Ansatz, um Polymorphismus und eifriges Laden zusammen zu erreichen, ohne eine polymorphe Beziehung zu verwenden. Wenn Sie keine polymorphe Beziehung verwenden , erhalten Sie mehr als einen Datenbankaufruf. Wenn Sie jedoch eine einzelne Spalte haben, die den Modaltyp unterscheidet, haben Sie möglicherweise ein falsch strukturiertes Schema. Ich schlage vor, dass Sie dies verbessern, wenn Sie es auch für die zukünftige Entwicklung vereinfachen möchten.
Das interne Modell wird neu geschrieben
newInstance()
undnewFromBuilder()
ist keine gute / empfohlene Methode. Sie müssen es überarbeiten, sobald Sie das Update vom Framework erhalten.quelle
Ich denke, Sie könnten die
newInstance
Methode für dasAnimal
Modell überschreiben , den Typ anhand der Attribute überprüfen und dann das entsprechende Modell initiieren.Sie müssen die
newFromBuilder
Methode auch überschreiben .quelle
type
in der Datenbank?Wenn Sie dies wirklich tun möchten, können Sie in Ihrem Tiermodell den folgenden Ansatz verwenden.
quelle
Wie der OP in seinen Kommentaren feststellte: Das Datenbankdesign ist bereits festgelegt, und daher scheinen Laravels polymorphe Beziehungen hier keine Option zu sein.
Ich mag die Antwort von Chris Neal, weil ich in letzter Zeit etwas Ähnliches tun musste (meinen eigenen Datenbanktreiber schreiben, um Eloquent für Datenbank- / DBF-Dateien zu unterstützen) und viel Erfahrung mit den Interna von Laravels Eloquent ORM gesammelt habe.
Ich habe mein persönliches Flair hinzugefügt, um den Code dynamischer zu gestalten und gleichzeitig eine explizite Zuordnung pro Modell beizubehalten.
Unterstützte Funktionen, die ich schnell getestet habe:
Animal::find(1)
funktioniert wie in Ihrer Frage gestelltAnimal::all()
funktioniert auchAnimal::where(['type' => 'dog'])->get()
gibtAnimalDog
-objects als Sammlung zurückAnimal
-model, falls keine Zuordnung konfiguriert ist (oder eine neue Zuordnung in der Datenbank angezeigt wurde)Nachteile:
newInstance()
undnewFromBuilder()
vollständige Modell neu geschrieben (Kopieren und Einfügen). Dies bedeutet, dass Sie den Code von Hand übernehmen müssen, wenn das Framework diese Mitgliedsfunktionen aktualisiert.Ich hoffe es hilft und ich bin offen für Vorschläge, Fragen und zusätzliche Anwendungsfälle in Ihrem Szenario. Hier sind die Anwendungsfälle und Beispiele dafür:
Und dies ist ein Beispiel dafür, wie es verwendet werden kann und unter den jeweiligen Ergebnissen dafür:
was zu folgendem Ergebnis führt:
Und falls Sie möchten, dass Sie das
MorphTrait
hier verwenden, ist natürlich der vollständige Code dafür:quelle
Ich glaube ich weiß was du suchst. Betrachten Sie diese elegante Lösung, die Laravel-Abfragebereiche verwendet. Weitere Informationen finden Sie unter https://laravel.com/docs/6.x/eloquent#query-scopes :
Erstellen Sie eine übergeordnete Klasse mit gemeinsamer Logik:
Erstellen Sie ein untergeordnetes (oder mehrere) Element mit einem globalen Abfragebereich und einem
saving
Ereignishandler:(Gleiches gilt für eine andere Klasse
Cat
, ersetzen Sie einfach die Konstante)Der globale Abfragebereich fungiert als Standardabfrageänderung, sodass die
Dog
Klasse immer nach Datensätzen mit suchttype='dog'
.Angenommen, wir haben 3 Datensätze:
Jetzt anrufen
Dog::find(1)
würde zunull
, da der Standard - Abfragebereich wird nicht das finden ,id:1
das eines istCat
. AufrufAnimal::find(1)
undCat::find(1)
beide funktionieren, obwohl nur der letzte Ihnen ein tatsächliches Cat-Objekt gibt.Das Schöne an diesem Setup ist, dass Sie die obigen Klassen verwenden können, um Beziehungen zu erstellen wie:
Und diese Beziehung gibt Ihnen automatisch nur alle Tiere mit dem
type='dog'
(in Form vonDog
Klassen). Der Abfragebereich wird automatisch angewendet.Darüber hinaus wird beim Aufruf
Dog::create($properties)
automatisch dertype
Wert'dog'
aufgrund dessaving
Ereignishakens festgelegt (siehe https://laravel.com/docs/6.x/eloquent#events ).Beachten Sie, dass das Anrufen
Animal::create($properties)
keine Standardeinstellungtype
hat. Daher müssen Sie diese hier manuell festlegen (was zu erwarten ist).quelle
Obwohl Sie Laravel verwenden, sollten Sie sich in diesem Fall nicht an die Abkürzungen von Laravel halten.
Dieses Problem, das Sie lösen möchten, ist ein klassisches Problem, das viele andere Sprachen / Frameworks mithilfe des Factory-Methodenmusters ( https://en.wikipedia.org/wiki/Factory_method_pattern ) lösen .
Wenn Sie möchten, dass Ihr Code leichter zu verstehen ist und keine versteckten Tricks vorhanden sind, sollten Sie ein bekanntes Muster anstelle von versteckten / magischen Tricks unter der Haube verwenden.
quelle
Der bisher einfachste Weg ist es, eine Methode in der Tierklasse zu erstellen
Modell auflösen
Dies gibt je nach Modelltyp eine Instanz der Klasse Tier, Hund oder Katze zurück
quelle