Come ha affermato l'OP nei suoi commenti: la progettazione del database è già impostata e quindi le relazioni polimorfiche di Laravel non sembrano essere un'opzione qui.
Mi piace la risposta di Chris Neal perché di recente ho dovuto fare qualcosa di simile (scrivendo il mio Database Driver per supportare Eloquent per i file dbase / DBF) e ho acquisito molta esperienza con gli interni di Eloquent ORM di Laravel.
Ho aggiunto il mio sapore personale ad esso per rendere il codice più dinamico mantenendo una mappatura esplicita per modello.
Funzionalità supportate che ho testato rapidamente:
Animal::find(1)
funziona come richiesto nella tua domanda
Animal::all()
funziona pure
Animal::where(['type' => 'dog'])->get()
restituirà AnimalDog
-object come una raccolta
- Mappatura dinamica degli oggetti per classe eloquente che utilizza questo tratto
- Fallback a
Animal
-model nel caso in cui non sia configurato alcun mapping (o è apparso un nuovo mapping nel DB)
svantaggi:
- Sta riscrivendo interamente
newInstance()
e newFromBuilder()
interamente il modello (copia e incolla). Ciò significa che se ci saranno aggiornamenti dal framework alle funzioni di questo membro, dovrai adottare il codice manualmente.
Spero che sia di aiuto e sono pronto per eventuali suggerimenti, domande e casi d'uso aggiuntivi nel tuo scenario. Ecco i casi d'uso ed esempi per questo:
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 {}
E questo è un esempio di come può essere utilizzato e sotto i rispettivi risultati per esso:
$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);
che risulta come segue:
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
E nel caso tu voglia usare MorphTrait
questo è ovviamente il codice completo per questo:
<?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);
}
}