Menu Docs

Herança

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.

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.

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

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".

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.

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.

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"
}
]
}

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.

Para saber mais sobre como configurar a collection de destino para suas operações, consulte o guia Configuração de persistência.