Visão geral
Associações no Mongoid permitem criar relacionamentos entre modelos. Neste guia, você pode aprender como usar o Mongoid para personalizar como as associações se comportam em seu aplicação. As seções a seguir descrevem maneiras de personalizar comportamentos de associação.
Extensões
As extensões permitem adicionar funcionalidade personalizada a uma associação. Você pode definir uma extensão em uma associação especificando um bloco na definição da associação, conforme mostrado no exemplo a seguir:
class Band include Mongoid::Document embeds_many :albums do def find_by_name(name) where(name: name).first end end end band.albums.find_by_name("Omega") # returns album "Omega"
Nomes de associação personalizados
Você pode utilizar a macro class_name para especificar um nome de classe personalizado para uma associação. Isto é útil quando você deseja nomear a associação com algo diferente do nome da classe. O exemplo a seguir usa a macro class_name para especificar que uma associação embutida chamada records representa a classe Album :
class Band include Mongoid::Document embeds_many :records, class_name: "Album" end
Chaves personalizadas
Por padrão, o Mongoid usa o campo _id da classe pai ao procurar associações. Você pode especificar campos diferentes para utilizar utilizando as macros primary_key e foreign_key. O exemplo a seguir especifica uma nova chave primária e externa para a associação albums em uma classe Band :
class Band include Mongoid::Document field :band_id, type: String has_many :albums, primary_key: 'band_id', foreign_key: 'band_id_ref' end class Album include Mongoid::Document field :band_id_ref, type: String belongs_to :band, primary_key: 'band_id', foreign_key: 'band_id_ref' end
Se você estiver especificando uma associação has_and_belongs_to_many, também poderá usar as macros inverse_primary_key e inverse_foreign_key. A macro inverse_primary_key especifica o campo no modelo local que o modelo remoto utiliza para procurar os documentos. A macro inverse_foreign_key especifica o campo no modelo remoto que armazena os valores encontrados no inverse_primary_key.
O exemplo a seguir especifica uma nova chave primária e externa para as classes Band e Members em uma associação has_and_belongs_to_many:
class Band include Mongoid::Document field :band_id, type: String field :member_ids, type: Array has_many :members, primary_key: 'member_id', foreign_key: 'member_ids', inverse_primary_key: 'band_id', inverse_foreign_key: 'band_ids' end class Member include Mongoid::Document field :member_id, type: String field :band_ids, type: Array has_many :bands, primary_key: 'band_id', foreign_key: 'band_ids', inverse_primary_key: 'member_id', inverse_foreign_key: 'member_ids' end
Escopos personalizados
Você pode especificar o escopo de uma associação usando o parâmetro scope. O parâmetro scope determina os documentos que o Mongoid considera parte de uma associação. Uma associação com escopo gera apenas documentos que correspondem às condições do escopo quando consultadas. Você pode configurar o scope para um Proc com uma aridade de zero ou um Symbol que faz referência a um escopo nomeado no modelo associado. O exemplo a seguir define escopos personalizados em associações em uma classe Band :
class Band include Mongoid::Document has_many :albums, scope: -> { where(published: true) } # Uses a scope called "upcoming" on the Tour model has_many :tours, scope: :upcoming end
Observação
Você pode adicionar documentos que não correspondem às condições de escopo a uma associação. O Mongoid salva os documentos no banco de dados e eles aparecerão na memória associada. No entanto, você não verá os documentos ao consultar a associação.
Validações
Quando o Mongoid carrega uma associação na memória, por padrão, ele usa a macro validates_associated para validar que quaisquer filhos também estão presentes. O Mongoid valida crianças para os seguintes tipos de associação:
embeds_manyembeds_onehas_manyhas_onehas_and_belongs_to_many
Você pode desativar esse comportamento de validação definindo a macro validate como false ao definir a associação, conforme mostrado no exemplo a seguir:
class Band include Mongoid::Document embeds_many :albums, validate: false end
Polimorfismo
O Mongoid suporta o polimorfismo nas classes filhos de associações um-para-um e um-para-muitos. Associações polimórficas permitem que uma única associação contenha objetos de diferentes tipos de classe . Você pode definir uma associação polimórfica definindo a opção polymorphic como true em uma associação filho e adicionando a opção as à associação pai. O exemplo seguinte cria uma associação polimórfica em uma classe Band :
class Tour include Mongoid::Document has_one :band, as: :featured end class Label include Mongoid::Document has_one :band, as: :featured end class Band include Mongoid::Document belongs_to :featured, polymorphic: true end
No exemplo anterior, a associação :featured na classe Band pode conter um documento Label ou Album.
Importante
O Mongoid suporta o polimorfismo apenas de filho para pai. Você não pode especificar uma associação pai has_one ou has_many como polimórfica.
has_and_belongs_to_many associações não apoiam polimorfismo.
Tipos polimórficos personalizados
A partir da versão 9.0.2, O Mongoid suporta tipos polimórficos personalizados por meio de um registro global. Você pode especificar chaves alternativas para representar classes diferentes, desacoplar seu código dos dados. O exemplo a seguir especifica a string "artist" como uma chave alternativa para a classe Band :
class Band include Mongoid::Document identify_as 'artist' has_many :albums, as: :record end
No exemplo anterior, a diretiva identify_as instrui o Mongoid a armazenar a classe Band no banco de dados como a string "artist".
Você também pode especificar vários aliases, como mostrado no exemplo a seguir:
class Band include Mongoid::Document identify_as 'artist', 'group', 'troupe' has_many :albums, as: :record end
No exemplo anterior, artist é o nome padrão e os outros são usados somente para procurar registros. Isso permite que você refatore seu código sem quebrar as associações em seus dados.
Os aliases de tipo polimórfico são globais. As chaves especificadas devem ser exclusivas em toda a sua base de código. No entanto, você pode registrar resolvedores alternativos que podem ser usados para diferentes subconjuntos de seus modelos. Nesse caso, as chaves devem ser exclusivas apenas para cada resolvedor. O exemplo a seguir mostra como registrar resolvedores alternativos:
Mongoid::ModelResolver.register_resolver Mongoid::ModelResolver.new, :mus Mongoid::ModelResolver.register_resolver Mongoid::ModelResolver.new, :tool module Music class Band include Mongoid::Document identify_as 'bnd', resolver: :mus end end module Tools class Band include Mongoid::Document identify_as 'bnd', resolver: :tool end end
Tanto Music::Band quanto Tools::Band têm nomes alternativos "bnd", mas cada modelo usa seu próprio resolvedor para evitar conflitos.
Comportamento dependente
Você pode fornecer opções dependent às associações referenciadas para especificar como o Mongoid lida com documentos associados quando um documento é excluído. Você pode especificar as seguintes opções:
delete_all: Exclui todos os documentos filhos sem executar nenhum retorno de chamada de resposta.destroy: Exclui os documentos filhos e executa todos os retornos de chamada do modelo.nullify: define a chave estrangeira dos documentos filhos comonil. O documento filho pode se tornar órfão se for referenciado apenas pelo pai.restrict_with_exception: Gera uma exceção se o documento filho não estiver vazio.restrict_with_error: cancela a operação e retornafalsese o documento filho não estiver vazio.
Se você não especificar nenhuma opção dependent, o Mongoid deixará o documento filho inalterado quando o documento pai for excluído. O documento filho continua a fazer referência ao documento pai excluído e, se for referenciado apenas pelo pai, o documento filho ficará órfão.
O exemplo seguinte especifica dependent opções na classe Band :
class Band include Mongoid::Document has_many :albums, dependent: :delete_all belongs_to :label, dependent: :nullify end
Salvamento automático de associações referenciadas
Por padrão, o Mongoid não salva automaticamente os documentos associados de associações não incorporadas ao salvar o documento pai. Isso pode resultar em referências pendentes a documentos que não existem.
Você pode usar a opção autosave em uma associação referenciada para salvar automaticamente os documentos associados ao salvar o documento pai. O exemplo seguinte cria uma classe Band com uma classe Album associada e especifica a opção autosave:
class Band include Mongoid::Document has_many :albums end class Album include Mongoid::Document belongs_to :band, autosave: true end band = Band.new album = Album.create!(band: band) # The band is persisted at this point.
Observação
O Mongoid adiciona automaticamente a funcionalidade de salvamento automático a uma associação que usa a opção accepts_nested_attributes_for.
Você não precisa especificar a opção autosave para associações incorporadas porque o Mongoid salva documentos incorporados no documento pai.
Construção automática
Você pode adicionar a opção autobuild a associações um-para-um, como has_one e embeds_one, para instanciar automaticamente um novo documento ao acessar uma associação nil. O exemplo a seguir adiciona a opção autobuild a uma associação na classe Band :
class Band include Mongoid::Document embeds_one :label, autobuild: true has_one :producer, autobuild: true end
Toque em
Quando o Mongoid toca um documento, ele atualiza o campo do documento updated_at para a data e hora atuais. Você pode adicionar a opção touch a qualquer associação belongs_to para garantir que o Mongoid toque no documento pai sempre que o documento filho for atualizado. O exemplo a seguir adiciona a opção touch a uma associação na classe Band :
class Band include Mongoid::Document field :name belongs_to :label, touch: true end
Você também pode utilizar a opção touch para especificar outro campo na associação pai, como uma string ou um símbolo. Quando o Mongoid toca a associação pai, ele define o campo updated_at e o campo especificado para a data e hora atuais.
O exemplo a seguir instrui o Mongoid a tocar no campo bands_updated_at :
class Band include Mongoid::Document belongs_to :label, touch: :bands_updated_at end
Observação
Em associações incorporadas, quando um documento incorporado é tocado, o Mongoid afeta seus pais recursivamente. Por esse motivo, adicionar um atributo touch a uma associação embedded_in é desnecessário.
O Mongoid não suporta a especificação de campos adicionais para tocar em embedded_in associações.
Cache do contador
Use a opção counter_cache para armazenar o número de objetos que pertencem a um campo associado. Quando você especifica esta opção, o Mongoid armazena um campo de contador no modelo principal associado.
A opção counter_cache aceita um dos dois seguintes valores para o nome do campo do contador:
true: o Mongoid usa um nome de campo de contador padrão, com base no nome da associação. Por exemplo, se a associação for chamadaBand, o nome do campo contador padrão serábands_count.Um
stringousymbol: o Mongoid usa esse valor diretamente como o nome do campo do contador.
Você pode fornecer o contêiner para o cache do contador de uma das seguintes maneiras:
Defina um campo de contador explícito no modelo principal.
Inclua o módulo
Mongoid::Attributes::Dynamicno modelo principal, que permite ao Mongoid criar o campo de contador dinamicamente no primeiro uso.
Campo de contador explícito
Nessa abordagem, declare o campo do contador diretamente no modelo principal. Isso é útil quando você deseja um esquema fixo e tipos de campo explícitos.
O exemplo seguinte declara um campo bands_count explícito na classe Label e utiliza o nome de campo contador padrão:
class Band include Mongoid::Document # Uses the default counter field name: bands_count belongs_to :label, counter_cache: true end class Label include Mongoid::Document field :bands_count, type: Integer has_many :bands end
Você também pode especificar um nome de campo de contador personalizado passando uma string para a opção counter_cache:
class Band include Mongoid::Document belongs_to :label, counter_cache: :active_bands_count end class Label include Mongoid::Document field :active_bands_count, type: Integer has_many :bands end
Campo de contador dinâmico
Se você incluir o módulo Mongoid::Attributes::Dynamic no modelo pai, o Mongoid poderá criar e atualizar o campo de contador dinamicamente durante o tempo de execução. Isso é útil quando você quer um esquema flexível e não quer declarar o campo de contador explicitamente.
O exemplo a seguir utiliza Mongoid::Attributes::Dynamic para permitir que o Mongoid crie o campo de contador dinamicamente na classe Label :
class Band include Mongoid::Document belongs_to:label, counter_cache: true end class Label include Mongoid::Document include Mongoid::Attributes::Dynamic has_many :bands end
Quando você usa o módulo Mongoid::Attributes::Dynamic, o Mongoid usa as mesmas regras de nomenclatura para o campo de contador e quando você declara um campo de contador explícito : ele usa um nome de campo *_count padrão com base na associação, a menos que você especifique um nome de campo personalizado com uma string na opção counter_cache.