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 gestellt
Animal::all()
funktioniert auch
Animal::where(['type' => 'dog'])->get()
gibt AnimalDog
-objects als Sammlung zurück
- Dynamische Objektzuordnung pro beredter Klasse, die dieses Merkmal verwendet
- Fallback auf
Animal
-model, falls keine Zuordnung konfiguriert ist (oder eine neue Zuordnung in der Datenbank angezeigt wurde)
Nachteile:
- Es wird das interne
newInstance()
und newFromBuilder()
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:
class Animal extends Model
{
use MorphTrait; // You'll find the trait in the very end of this answer
protected $morphKey = 'type'; // This is your column inside the database
protected $morphMap = [ // This is the value-to-class mapping
'dog' => AnimalDog::class,
'cat' => AnimalCat::class,
];
}
class AnimalCat extends Animal {}
class AnimalDog extends Animal {}
Und dies ist ein Beispiel dafür, wie es verwendet werden kann und unter den jeweiligen Ergebnissen dafür:
$cat = Animal::find(1);
$dog = Animal::find(2);
$new = Animal::find(3);
$all = Animal::all();
echo sprintf('ID: %s - Type: %s - Class: %s - Data: %s', $cat->id, $cat->type, get_class($cat), $cat, json_encode($cat->toArray())) . PHP_EOL;
echo sprintf('ID: %s - Type: %s - Class: %s - Data: %s', $dog->id, $dog->type, get_class($dog), $dog, json_encode($dog->toArray())) . PHP_EOL;
echo sprintf('ID: %s - Type: %s - Class: %s - Data: %s', $new->id, $new->type, get_class($new), $new, json_encode($new->toArray())) . PHP_EOL;
dd($all);
was zu folgendem Ergebnis führt:
ID: 1 - Type: cat - Class: App\AnimalCat - Data: {"id":1,"type":"cat"}
ID: 2 - Type: dog - Class: App\AnimalDog - Data: {"id":2,"type":"dog"}
ID: 3 - Type: new-animal - Class: App\Animal - Data: {"id":3,"type":"new-animal"}
// Illuminate\Database\Eloquent\Collection {#1418
// #items: array:2 [
// 0 => App\AnimalCat {#1419
// 1 => App\AnimalDog {#1422
// 2 => App\Animal {#1425
Und falls Sie möchten, dass Sie das MorphTrait
hier verwenden, ist natürlich der vollständige Code dafür:
<?php namespace App;
trait MorphTrait
{
public function newInstance($attributes = [], $exists = false)
{
// This method just provides a convenient way for us to generate fresh model
// instances of this current model. It is particularly useful during the
// hydration of new objects via the Eloquent query builder instances.
if (isset($attributes['force_class_morph'])) {
$class = $attributes['force_class_morph'];
$model = new $class((array)$attributes);
} else {
$model = new static((array)$attributes);
}
$model->exists = $exists;
$model->setConnection(
$this->getConnectionName()
);
$model->setTable($this->getTable());
return $model;
}
/**
* Create a new model instance that is existing.
*
* @param array $attributes
* @param string|null $connection
* @return static
*/
public function newFromBuilder($attributes = [], $connection = null)
{
$newInstance = [];
if ($this->isValidMorphConfiguration($attributes)) {
$newInstance = [
'force_class_morph' => $this->morphMap[$attributes->{$this->morphKey}],
];
}
$model = $this->newInstance($newInstance, true);
$model->setRawAttributes((array)$attributes, true);
$model->setConnection($connection ?: $this->getConnectionName());
$model->fireModelEvent('retrieved', false);
return $model;
}
private function isValidMorphConfiguration($attributes): bool
{
if (!isset($this->morphKey) || empty($this->morphMap)) {
return false;
}
if (!array_key_exists($this->morphKey, (array)$attributes)) {
return false;
}
return array_key_exists($attributes->{$this->morphKey}, $this->morphMap);
}
}