Tengo un Animalmodelo, basado en la animaltabla.
Esta tabla contiene un typecampo que puede contener valores como cat o dog .
Me gustaría poder crear objetos como:
class Animal extends Model { }
class Dog extends Animal { }
class Cat extends Animal { }Sin embargo, poder buscar un animal como este:
$animal = Animal::find($id);Pero donde $animalsería una instancia de Dogo Catdependiendo del typecampo, que puedo verificar usando instance ofo que funcionará con métodos de tipo insinuado. La razón es que el 90% del código se comparte, pero uno puede ladrar y el otro puede maullar.
Sé que puedo hacer Dog::find($id), pero no es lo que quiero: puedo determinar el tipo de objeto solo una vez que fue recuperado. También podría buscar el Animal y luego ejecutar find()el objeto correcto, pero esto está haciendo dos llamadas a la base de datos, que obviamente no quiero.
Traté de buscar una forma de instanciar "manualmente" un modelo Eloquent como Dog from Animal, pero no pude encontrar ningún método correspondiente. ¿Alguna idea o método que me perdí por favor?

*_typenombre para determinar el modelo de subtipo. En mi caso, realmente solo tengo una tabla, así que si bien es una buena característica, no en mi caso.Respuestas:
Puede usar las Relaciones polimórficas en Laravel como se explica en Documentos oficiales de Laravel . Así es como puedes hacer eso.
Definir las relaciones en el modelo como se da
Aquí necesitará dos columnas en la
animalstabla, la primera esanimable_typey otra esanimable_iddeterminar el tipo de modelo adjunto en tiempo de ejecución.Puede buscar el modelo de perro o gato como se indica,
Después de eso, puede verificar la
$animclase del objeto utilizandoinstanceof.Este enfoque lo ayudará para una futura expansión si agrega otro tipo de animal (es decir, zorro o león) en la aplicación. Funcionará sin cambiar su base de código. Esta es la forma correcta de cumplir con sus requisitos. Sin embargo, no existe un enfoque alternativo para lograr el polimorfismo y la carga ansiosa juntos sin utilizar una relación polimórfica. Si no usa una relación polimórfica , terminará con más de una llamada a la base de datos. Sin embargo, si tiene una sola columna que diferencia el tipo modal, quizás tenga un esquema estructurado incorrecto. Le sugiero que mejore eso si desea simplificarlo también para el desarrollo futuro.
Reescribir el modelo interno
newInstance()ynewFromBuilder()no es una forma buena / recomendada, y debe volver a trabajar en él una vez que obtenga la actualización del marco.fuente
Creo que podría anular el
newInstancemétodo en elAnimalmodelo y verificar el tipo de los atributos y luego iniciar el modelo correspondiente.También deberá anular el
newFromBuildermétodo.fuente
typeen la base de datos?Si realmente quieres hacer esto, puedes usar el siguiente enfoque dentro de tu modelo Animal.
fuente
Como el OP declaró en sus comentarios: El diseño de la base de datos ya está configurado y, por lo tanto, las Relaciones Polimórficas de Laravel no parecen ser una opción aquí.
Me gusta la respuesta de Chris Neal porque recientemente tuve que hacer algo similar (escribir mi propio controlador de base de datos para admitir Eloquent para archivos dbase / DBF) y obtuve mucha experiencia con los componentes internos de Eloquent ORM de Laravel.
Le agregué mi sabor personal para hacer que el código sea más dinámico y al mismo tiempo mantener una asignación explícita por modelo.
Funciones compatibles que probé rápidamente:
Animal::find(1)funciona como se le preguntó en su preguntaAnimal::all()funciona bienAnimal::where(['type' => 'dog'])->get()devolveráAnimalDog-objetos como una colecciónAnimalutilizar el modelo en caso de que no haya una asignación configurada (o si aparece una nueva asignación en la base de datos)Desventajas
newInstance()ynewFromBuilder()completamente (copiar y pegar). Esto significa que si habrá alguna actualización del marco para las funciones de este miembro, deberá adoptar el código a mano.Espero que ayude y estoy preparado para cualquier sugerencia, pregunta y casos de uso adicionales en su escenario. Aquí están los casos de uso y ejemplos para ello:
Y este es un ejemplo de cómo se puede usar y debajo de los resultados respectivos:
que resulta lo siguiente:
Y en caso de que quiera usar,
MorphTraitaquí está, por supuesto, el código completo:fuente
Creo que sé lo que estás buscando. Considere esta elegante solución que utiliza los ámbitos de consulta de Laravel; consulte https://laravel.com/docs/6.x/eloquent#query-scopes para obtener información adicional:
Cree una clase principal que contenga lógica compartida:
Cree un elemento secundario (o múltiple) con un alcance de consulta global y un
savingcontrolador de eventos:(lo mismo se aplica a otra clase
Cat, solo reemplace la constante)El alcance de la consulta global actúa como una modificación de consulta predeterminada, de modo que la
Dogclase siempre buscará registros contype='dog'.Digamos que tenemos 3 registros:
Ahora
Dog::find(1)se produciría una llamadanull, porque el alcance de la consulta predeterminada no encontraráid:1cuál es unCat. LlamarAnimal::find(1)yCat::find(1)funcionará, aunque solo el último le proporciona un objeto Cat real.Lo bueno de esta configuración es que puede usar las clases anteriores para crear relaciones como:
Y esta relación automáticamente solo le dará todos los animales con el
type='dog'(en forma deDogclases). El alcance de la consulta se aplica automáticamente.Además, llamando
Dog::create($properties)establecerá automáticamente eltypeque'dog'debido alsavingenlace de eventos (ver https://laravel.com/docs/6.x/eloquent#events ).Tenga en cuenta que las llamadas
Animal::create($properties)no tienen un valor predeterminado,typepor lo que aquí debe configurarlo manualmente (lo cual es de esperar).fuente
Aunque está utilizando Laravel, en este caso, creo que no debe apegarse a los atajos de Laravel.
Este problema que está tratando de resolver es un problema clásico que muchos otros lenguajes / marcos resuelven usando el patrón del método Factory ( https://en.wikipedia.org/wiki/Factory_method_pattern ).
Si desea que su código sea más fácil de entender y sin trucos ocultos, debe usar un patrón bien conocido en lugar de trucos ocultos / mágicos debajo del capó.
fuente
La forma más fácil hasta ahora es hacer un método en la clase Animal
Resolviendo modelo
Esto devolverá la instancia de la clase Animal, Perro o Gato dependiendo del tipo de modelo
fuente