Eu tenho um Animal
modelo, baseado na animal
tabela.
Esta tabela contém um type
campo que pode conter valores como gato ou cachorro .
Eu gostaria de poder criar objetos como:
class Animal extends Model { }
class Dog extends Animal { }
class Cat extends Animal { }
No entanto, ser capaz de buscar um animal como este:
$animal = Animal::find($id);
Mas onde $animal
seria uma instância Dog
ou Cat
dependendo do type
campo, que eu possa verificar usando instance of
ou que funcionará com métodos sugeridos por tipo. O motivo é que 90% do código é compartilhado, mas um pode latir e o outro pode miar.
Sei que posso fazer Dog::find($id)
, mas não é o que quero: só posso determinar o tipo do objeto depois que ele foi buscado. Eu também poderia buscar o Animal e, em seguida, executar find()
no objeto certo, mas isso está fazendo duas chamadas ao banco de dados, o que obviamente não quero.
Tentei procurar uma maneira de instanciar "manualmente" um modelo Eloquent como Dog from Animal, mas não consegui encontrar nenhum método correspondente. Alguma idéia ou método que eu perdi por favor?
*_type
nome para determinar o modelo do subtipo. No meu caso, eu realmente tenho apenas uma tabela, portanto, embora seja um recurso interessante, não no meu caso.Respostas:
Você pode usar os relacionamentos polimórficos no Laravel, conforme explicado em Documentos oficiais do Laravel . Aqui está como você pode fazer isso.
Defina os relacionamentos no modelo conforme fornecido
Aqui você precisará de duas colunas na
animals
tabela, a primeira éanimable_type
e a outra éanimable_id
para determinar o tipo de modelo anexado a ela em tempo de execução.Você pode buscar o modelo Cão ou Gato conforme determinado,
Depois disso, você pode verificar a
$anim
classe do objeto usandoinstanceof
.Essa abordagem ajudará você na expansão futura se você adicionar outro tipo de animal (por exemplo, raposa ou leão) no aplicativo. Ele funcionará sem alterar sua base de código. Esta é a maneira correta de atingir seus requisitos. No entanto, não existe uma abordagem alternativa para alcançar o polimorfismo e a carga ágil em conjunto sem usar uma relação polimórfica. Se você não usar um relacionamento polimórfico , terá mais de uma chamada no banco de dados. No entanto, se você tiver uma única coluna que diferencie o tipo modal, talvez tenha um esquema estruturado errado. Eu sugiro que você melhore isso se você quiser simplificá-lo também para desenvolvimento futuro.
Reescrever o modelo interno
newInstance()
enewFromBuilder()
não é uma maneira boa / recomendada e você deve refazê-lo assim que receber a atualização da estrutura.fonte
Eu acho que você poderia substituir o
newInstance
método noAnimal
modelo e verificar o tipo dos atributos e, em seguida, iniciar o modelo correspondente.Você também precisará substituir o
newFromBuilder
método.fonte
type
no banco de dados?Se você realmente quiser fazer isso, poderá usar a seguinte abordagem dentro do seu modelo Animal.
fonte
Como o OP afirmou em seus comentários: O design do banco de dados já está definido e, portanto, o relacionamento polimórfico do Laravel parece não ser uma opção aqui.
Gosto da resposta de Chris Neal porque tive que fazer algo semelhante recentemente (escrever meu próprio driver de banco de dados para dar suporte ao Eloquent para arquivos dbase / DBF) e adquiri muita experiência com os elementos internos do Eloquent ORM do Laravel.
Eu adicionei meu sabor pessoal a ele para tornar o código mais dinâmico, mantendo um mapeamento explícito por modelo.
Recursos suportados que testei rapidamente:
Animal::find(1)
funciona como solicitado em sua perguntaAnimal::all()
funciona bemAnimal::where(['type' => 'dog'])->get()
retornaráAnimalDog
-objects como uma coleçãoAnimal
-model no caso de não haver mapeamento configurado (ou um novo mapeamento aparecer no DB)Desvantagens:
newInstance()
enewFromBuilder()
inteiramente (copiar e colar). Isso significa que, se houver alguma atualização da estrutura para essas funções de membro, você precisará adotar o código manualmente.Espero que ajude e estou pronto para todas as sugestões, perguntas e casos de uso adicionais no seu cenário. Aqui estão os casos de uso e exemplos para isso:
E este é um exemplo de como ele pode ser usado e abaixo dos respectivos resultados:
que resulta do seguinte:
E, caso você queira usar o
MorphTrait
aqui, é claro, o código completo:fonte
Eu acho que sei o que você está procurando. Considere esta solução elegante que usa escopos de consulta do Laravel, consulte https://laravel.com/docs/6.x/eloquent#query-scopes para obter informações adicionais:
Crie uma classe pai que possui lógica compartilhada:
Crie um filho (ou vários) com um escopo de consulta global e um
saving
manipulador de eventos:(o mesmo se aplica a outra classe
Cat
, basta substituir a constante)O escopo da consulta global atua como uma modificação de consulta padrão, de modo que a
Dog
classe sempre procure registros comtype='dog'
.Digamos que temos 3 registros:
Agora, a chamada
Dog::find(1)
resultaria emnull
, porque o escopo da consulta padrão não encontrará oid:1
que é aCat
. LigarAnimal::find(1)
eCat::find(1)
trabalhará, embora apenas o último dê a você um objeto Cat real.O bom dessa configuração é que você pode usar as classes acima para criar relações como:
E essa relação automaticamente fornecerá apenas todos os animais com o
type='dog'
(na forma deDog
classes). O escopo da consulta é aplicado automaticamente.Além disso, a chamada
Dog::create($properties)
definirá automaticamentetype
como'dog'
devido aosaving
gancho do evento (consulte https://laravel.com/docs/6.x/eloquent#events ).Observe que a chamada
Animal::create($properties)
não tem um padrão,type
então aqui você precisa configurá-la manualmente (o que é esperado).fonte
Embora você esteja usando o Laravel, nesse caso, acho que você não deve se atentar aos atalhos do Laravel.
Esse problema que você está tentando resolver é um problema clássico que muitas outras linguagens / estruturas resolvem usando o padrão do método Factory ( https://en.wikipedia.org/wiki/Factory_method_pattern ).
Se você deseja que seu código seja mais fácil de entender e sem truques ocultos, use um padrão conhecido em vez de truques ocultos / mágicos sob o capô.
fonte
A maneira mais fácil ainda é criar método na classe Animal
Modelo de resolução
Isso retornará a instância da classe Animal, Dog ou Cat, dependendo do tipo de modelo
fonte