Herança
Nesta página
Visão geral
Neste guia, você pode aprender como implementar a herança em seus modelos Mongoid. A herança permite que você aplique as características de uma classe "pai" a uma ou mais classes "filhos".
Mongoid suporta herança em documentos de nível superior e incorporados. Quando uma classe de modelo filha herda de uma classe pai, o Mongoid copia os campos, associações, validações e escopos da classe pai para a classe filho.
Atribuir herança
Ao criar uma classe de modelo filha, use o caractere <
para implementar a herança de uma classe pai especificada. As seguintes classes de modelo demonstram como criar classes pai e filho entre os modelos Person
, Employee
e Manager
:
class Person include Mongoid::Document field :name, type: String end class Employee < Person field :company, type: String field :tenure, type: Integer scope :new_hire, ->{ where(:tenure.lt => 1) } end class Manager < Employee end
O Mongoid salva instâncias de Person
, Employee
e Manager
na coleção people
. O Mongoid define o campo discriminador _type
como o nome da classe de modelo em documentos para garantir que os documentos sejam retornados como os tipos esperados quando você executa operações de leitura.
Documentos incorporados
Você também pode implementar um padrão de herança em associações incorporadas. Semelhante ao comportamento das classes de modelo de nível superior, o Mongoid define o campo discriminador _type
em documentos incorporados, dependendo da classe de modelo usada para criá-los.
O exemplo a seguir adiciona uma associação incorporada ao modelo Person
e cria modelos pai e filho para a classe Info
incorporada :
class Person include Mongoid::Document field :name, type: String embeds_many :infos end ... class Info include Mongoid::Document field :active, type: Boolean embedded_in :person end class Phone < Info field :value, type: Float field :country, type: String end class Email < Info field :value, type: String field :category, type: String end
Comportamento da query
Quando você executa query de uma classe de modelo filho, a query retorna apenas documentos em que o valor do campo _type
corresponde à classe consultada ou a outras classes filho. Por exemplo, se você fizer query na classe Employee
, a query retornará documentos da collection people
na qual o valor _type
é "Employee"
ou "Manager"
. Todos os outros valores de discriminador são considerados instâncias da classe Person
principal.
Ao fazer query em uma classe principal como Person
, o Mongoid retorna documentos que atendem a qualquer um dos seguintes critérios:
O valor do discriminador é o nome da classe pai ou de qualquer uma das classes filhos. Por exemplo,
"Person"
,"Employee"
ou"Manager"
.Não tem um valor discriminador.
O valor do discriminador não é mapeado para a classe pai ou para nenhuma de suas classes filhas. Por exemplo,
"Director"
ou"Specialist"
.
Alterar a chave discriminadora
Você pode alterar a chave discriminadora do nome de campo padrão _type
por qualquer um dos seguintes motivos:
Otimização: você pode selecionar uma chave mais curta, como
_t
.Consistência com um sistema existente: você pode estar usando um sistema ou conjunto de dados existente que tenha chaves predefinidas.
Você pode alterar a chave discriminadora no nível da classe ou no nível global. Para alterar a chave discriminadora no nível da classe , defina o nome da chave personalizada na classe principal usando o método discriminator_key
.
O exemplo a seguir demonstra como definir uma chave discriminadora personalizada ao definir uma classe de modelo:
class Person include Mongoid::Document field :name, type: String self.discriminator_key = "sub_type" end
Quando você cria uma instância do Person
ou qualquer uma de suas classes filhas, o Mongoid adiciona o campo sub_type
aos documentos no MongoDB.
Observação
Você pode alterar a chave discriminadora somente na classe principal. O Mongoid gera um erro se você definir uma chave personalizada em qualquer classe filha .
Se você alterar a chave discriminadora após definir uma classe filho, o Mongoid adicionará o novo campo chave, mas o campo antigo permanecerá inalterado. Por exemplo, suponha que você adicione o seguinte código ao seu aplicação depois de definir suas classes de modelo:
Person.discriminator_key = "sub_type"
Nesse caso, quando você cria uma instância de uma classe filho como Employee
, o Mongoid adiciona os campos sub_type
e _type
ao documento.
Você também pode alterar a chave discriminadora no nível global, para que todas as classes usem a chave especificada em vez do campo _type
.
Você pode definir uma chave global adicionando o seguinte código ao seu aplicação antes de definir qualquer classe de modelo:
Mongoid.discriminator_key = "sub_type"
Todas as classes usam sub_type
como chave discriminadora e não incluem o campo _type
.
Observação
Você deve definir a chave discriminadora no nível global antes de definir quaisquer classes filho para as classes usar esse valor global. Se você definir a chave global após definir as classes filhas, seus documentos salvos conterão o campo _type
padrão.
Altere o valor do discriminador
Você pode personalizar o valor que Mongoid define como o valor discriminador em MongoDB. Utilize o método discriminator_value
ao definir uma classe para personalizar o valor do discriminador, conforme mostrado no exemplo a seguir:
class Employee include Mongoid::Document field :company, type: String self.discriminator_value = "Worker" end
Quando você cria uma instância de Employee
, o campo discriminador _type
do documento tem um valor de "Worker"
em vez do nome da classe .
Observação
Como a personalização do valor do discriminador é declarada em classes filhas, você deve carregar as classes filho recuperadas por uma query antes de enviar essa query.
No exemplo anterior, a definição de classe Employee
deve ser carregada antes de você executar a query no Person
se os documentos retornados incluírem instâncias de Employee
. O carregamento automático não pode resolver o valor do discriminador "Worker"
para retornar documentos como instâncias de Employee
.
Associações incorporadas
Você pode criar qualquer tipo de classe pai ou classe filho em uma associação incorporada por atribuição ou usando os métodos build
e create
. Você pode passar a classe de modelo desejada como o segundo parâmetro para os métodos build
e create
para instruir o Mongoid a criar essa instância específica como um documento incorporado.
O código a seguir cria uma instância de Employee
e demonstra como adicionar documentos incorporados usando os diferentes métodos de criação:
# Creates a new Employee instance e = Employee.create( name: "Lance Huang", company: "XYZ Communications", tenure: 2 ) # Builds an Info object e.infos.build({ active: true }) # Builds a Phone object e.infos.build( { active: true, value: 1239007777, country: "USA" }, Phone ) # Creates an Email object e.infos.create( { active: true, value: "l.huang@company.com", category: "work" }, Email ) # Creates and assigns an Email object p = Email.new(active: false, value: "lanceh11@mymail.com", category: "personal" ) e.infos << p # Saves the Employee instance to database e.save
O seguinte documento é armazenado no banco de dados do people
:
{ "_id": {...}, "name": "Lance Huang", "company": "XYZ Communications", "tenure": 2, "_type": "Employee", "infos": [ { "_id": {...}, "active": true, "value": "l.huang@company.com", "category": "work", "_type": "Email" }, { "_id": {...}, "active": false, "value": "lanceh11@mymail.com", "category": "personal", "_type": "Email" }, { "_id": {...}, "active": true, "_type": "Info" }, { "_id": {...}, "active": true, "value": 1239007777, "country": "USA", "_type": "Phone" } ] }
Contextos de persistência
Você pode alterar o contexto de persistência de uma classe filho do contexto de persistência de seu pai para armazenar o documento em um local diferente do padrão. Ao usar o método store_in
, você pode armazenar uma instância de uma classe filho em uma coleção, banco de dados ou cluster diferente de uma instância do modelo pai.
As seguintes definições de modelo demonstram como utilizar o método store_in
para armazenar instâncias de Employee
e Manager
em uma coleção diferente da coleção people
:
class Person include Mongoid::Document end class Employee < Person # Specifies "employees" as target collection store_in collection: :employees end class Manager < Employee # Specifies "managers" as target collection store_in collection: :managers end
Observação
O Mongoid ainda adiciona o campo discriminador aos documentos armazenados.
Se você definir uma coleção de destino alternativa em algumas classes filho e não em outras, as instâncias das classes sem coleções especificadas serão armazenadas na coleção associada à classe pai.
Observação
Quando você altera a coleção de destino de uma classe filha, as instâncias dessa classe não aparecem nos resultados das queries na classe pai.
Informações adicionais
Para saber mais sobre como configurar a collection de destino para suas operações, consulte o guia Configuração de persistência.