Herança
Nesta página
Visão geral
Mongoid suporta herança em documentos de nível superior e incorporados. Quando um documento filho herda de um documento pai, os campos, associações, validações e escopos do documento pai são copiados para o documento filho.
class Canvas include Mongoid::Document field :name, type: String embeds_many :shapes end class Browser < Canvas field :version, type: Integer scope :recent, ->{ where(:version.gt => 3) } end class Firefox < Browser end class Shape include Mongoid::Document field :x, type: Integer field :y, type: Integer embedded_in :canvas end class Circle < Shape field :radius, type: Float end class Rectangle < Shape field :width, type: Float field :height, type: Float end
No exemplo acima, Canvas
, Browser
e Firefox
serão guardados na coleção de telas. Um atributo adicional _type
é armazenado para garantir que, quando carregado do banco de dados, o documento correto seja retornado. Isso também se aplica aos documentos incorporados Circle
, Rectangle
e Shape
.
Observação
Ao procurar um Circle
, a query só retornará documentos na coleção de formas em que o campo _type
(ou qualquer que seja o que a chave discriminadora foi definida) tiver o valor Circle
(ou qualquer que seja o valor do discriminador definido), todos os outros valores do discriminador serão considerados um objeto da classe Shape.
Da mesma forma, ao executar queries por classes principais (Canvas
neste exemplo), quaisquer documentos na coleção que não tenham um valor discriminador ou cujo valor discriminador não seja mapeado para o pai ou para nenhum de seus descendentes serão retornados como instâncias da classe principal.
Alterando a chave discriminadora
O Mongoid suporta a alteração da chave discriminadora a partir do _type
padrão. Existem alguns casos em que alguém pode querer fazer isso:
Para otimização: o usuário pode querer usar uma chave mais curta como
_t
.Ao tentar trabalhar com um sistema existente: é possível que o usuário esteja trabalhando com um sistema ou conjunto de dados existente que tenha chaves predefinidas.
Existem duas maneiras de alterar a chave discriminadora, no nível de classe e no nível global. Para alterar a chave discriminadora no nível da classe, o usuário pode defini-la diretamente na classe principal usando o método discriminator_key=
. Veja o exemplo acima:
class Shape include Mongoid::Document field :x, type: Integer field :y, type: Integer embedded_in :canvas self.discriminator_key = "shape_type" end class Circle < Shape field :radius, type: Float end class Rectangle < Shape field :width, type: Float field :height, type: Float end
Aqui, uma chamada para o setter discriminator_key=
foi adicionada à classe principal. Agora, na criação de um retângulo ou círculo, um campo shape_type
será adicionado.
Observe que a chave discriminadora só pode ser modificada na classe pai e um erro será gerado se tentar defini-la na classe filho.
Se a chave discriminadora for alterada após a criação da classe filho, um novo campo será adicionado com o novo valor da chave discriminadora e o campo antigo permanecerá inalterado. Por exemplo:
class Shape include Mongoid::Document field :x, type: Integer field :y, type: Integer embedded_in :canvas end class Circle < Shape field :radius, type: Float end class Rectangle < Shape field :width, type: Float field :height, type: Float end Shape.discriminator_key = "shape_type"
Nesse caso, na criação de um Retângulo ou Círculo, haverá um campo shape_type
e um campo _type
que têm como padrão Rectangle
ou Circle
respectivamente.
A chave discriminadora também pode ser definida no nível global. Ou seja, todas as classes usarão a chave discriminadora definida globalmente em vez de _type
. Veja o exemplo acima:
Mongoid.discriminator_key = "_the_type" class Shape include Mongoid::Document field :x, type: Integer field :y, type: Integer embedded_in :canvas end class Circle < Shape field :radius, type: Float end class Rectangle < Shape field :width, type: Float field :height, type: Float end
Após definir a chave do discriminador global, todas as classes utilizarão _the_type
como chave do discriminador e não conterão um campo _type
.
Observe que, ao definir a chave discriminadora no nível global, ela deve ser definida antes que a classe filho seja definida para que a classe filho use esse valor global. No nível global, no entanto, se o usuário não definir a chave discriminadora antes de definir uma classe filha, o campo discriminador usará a _type
padrão e não a nova configuração global nessa classe filha.
Alterando o valor do discriminador
O Mongoid também suporta a alteração do valor discriminador do valor padrão, que é o nome da classe. Pode-se alterar o valor do discriminador usando o método discriminator_value=
nessa classe específica.
Veja o exemplo acima:
class Shape include Mongoid::Document field :x, type: Integer field :y, type: Integer embedded_in :canvas end class Circle < Shape field :radius, type: Float self.discriminator_value = "round thing" end class Rectangle < Shape field :width, type: Float field :height, type: Float end
Aqui, uma chamada para o setter discriminator_value=
foi adicionada a Circle
. Agora, na criação de um Circle
, o documento conterá um campo com a chave _type
(ou qualquer que seja o discriminator_key
para o qual foi alterado) e o valor "round thing."
Observação
Como as substituições de valor do discriminador são declaradas em classes filhas, as classes filho potencialmente encontradas por uma query devem ser carregadas antes de enviar essa query. No exemplo acima, a definição de classe Circle
deve ser carregada ao executar queries no Shape
se os documentos retornados podem ser potencialmente instâncias de Circle
(já que o carregamento automático não resolveria "round thing"
para Circle
).
A executar queries de subclasses
A query de subclasses é tratada da maneira normal e, embora os documentos estejam todos na mesma coleção, as queries retornarão apenas documentos do tipo correto, semelhante à Herança de Tabela Única no ActiveRecord.
# Returns Canvas documents and subclasses Canvas.where(name: "Paper") # Returns only Firefox documents Firefox.where(name: "Window 1")
Associações
Você pode adicionar qualquer tipo de subclasse a uma associação tem ou tem muitas, por meio da configuração normal ou por meio dos métodos build e create na associação:
firefox = Firefox.new # Builds a Shape object firefox.shapes.build({ x: 0, y: 0 }) # Builds a Circle object firefox.shapes.build({ x: 0, y: 0 }, Circle) # Creates a Rectangle object firefox.shapes.create({ x: 0, y: 0 }, Rectangle) rect = Rectangle.new(width: 100, height: 200) firefox.shapes
Contextos de persistência
O Mongoid permite que o contexto de persistência de uma subclasse seja alterado do contexto de persistência de sua principal. Isso significa que, usando o método store_in
, podemos armazenar os documentos das subclasses em coleções diferentes (bem como em bancos de dados diferentes, clientes) das coleções principais:
class Shape include Mongoid::Document store_in collection: :shapes end class Circle < Shape store_in collection: :circles end class Square < Shape store_in collection: :squares end Shape.create! Circle.create! Square.create!
Definir a coleção nas crianças faz com que os documentos para essas crianças sejam armazenados na coleção definida, em vez de na coleção dos pais:
> db.shapes.find() { "_id" : ObjectId("62fe9a493282a43d6b725e10"), "_type" : "Shape" } > db.circles.find() { "_id" : ObjectId("62fe9a493282a43d6b725e11"), "_type" : "Circle" } > db.squares.find() { "_id" : ObjectId("62fe9a493282a43d6b725e12"), "_type" : "Square" }
Se a coleção for definida em algumas das subclasses e não em outras, as subclasses com coleções definidas armazenarão documentos nessas coleções, e as subclasses sem coleções definidas armazenarão documentos na coleção dos pais.
Observação
Observe que alterar a coleção em que uma subclasse está armazenada fará com que os documentos dessa subclasse não sejam mais encontrados nos resultados da query à sua classe principal.