Comme l'OP l'a déclaré dans ses commentaires: La conception de la base de données est déjà définie et, par conséquent, les relations polymorphes de Laravel ne semblent pas être une option ici.
J'aime la réponse de Chris Neal parce que j'ai dû faire quelque chose de similaire récemment (écrire mon propre pilote de base de données pour prendre en charge Eloquent pour les fichiers dbase / DBF) et j'ai acquis beaucoup d'expérience avec les composants internes de Laroquel's Eloquent ORM.
J'y ai ajouté ma saveur personnelle pour rendre le code plus dynamique tout en conservant un mappage explicite par modèle.
Fonctionnalités prises en charge que j'ai rapidement testées:
Animal::find(1)
fonctionne comme demandé dans votre question
Animal::all()
fonctionne aussi
Animal::where(['type' => 'dog'])->get()
renverra AnimalDog
-objects en tant que collection
- Mappage d'objet dynamique par classe éloquente qui utilise ce trait
Animal
Retour à -model dans le cas où aucun mappage n'est configuré (ou si un nouveau mappage apparaît dans la base de données)
Désavantages:
- C'est la réécriture interne
newInstance()
et newFromBuilder()
complète du modèle (copier-coller). Cela signifie que s'il y aura une mise à jour du cadre vers ces fonctions membres, vous devrez adopter le code à la main.
J'espère que cela aide et je suis prêt à toute suggestion, question et cas d'utilisation supplémentaires dans votre scénario. Voici les cas d'utilisation et des exemples:
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 {}
Et ceci est un exemple de la façon dont il peut être utilisé et ci-dessous les résultats respectifs pour cela:
$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);
ce qui donne les résultats suivants:
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
Et au cas où vous voudriez utiliser le MorphTrait
voici bien sûr le code complet:
<?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);
}
}