Tengo un Animal
modelo, basado en la animal
tabla.
Esta tabla contiene un type
campo 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 $animal
sería una instancia de Dog
o Cat
dependiendo del type
campo, que puedo verificar usando instance of
o 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?
*_type
nombre 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
animals
tabla, la primera esanimable_type
y otra esanimable_id
determinar 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
$anim
clase 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
newInstance
método en elAnimal
modelo y verificar el tipo de los atributos y luego iniciar el modelo correspondiente.También deberá anular el
newFromBuilder
método.fuente
type
en 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ónAnimal
utilizar 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,
MorphTrait
aquí 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
saving
controlador 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
Dog
clase 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:1
cuá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 deDog
clases). El alcance de la consulta se aplica automáticamente.Además, llamando
Dog::create($properties)
establecerá automáticamente eltype
que'dog'
debido alsaving
enlace 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,type
por 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