Menu Docs
Página inicial do Docs
/ / /
Mongoid
/

Herança

Nesta página

  • Visão geral
  • Alterando a chave discriminadora
  • Alterando o valor do discriminador
  • A executar queries de subclasses
  • Associações
  • Contextos de persistência

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.

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:

  1. Para otimização: o usuário pode querer usar uma chave mais curta como _t.

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

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

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

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.

Voltar

Definição de campo