Arquivo de Tags: relacionamentos

Associações – ActiveRecord

Este guia aborda as características de associação do ActiveRecord. Ao fazer referência a este guia, você será capaz de:

· Declarar associações entre os Models do ActiveRecord.

· Compreender os diferentes tipos de associações do ActiveRecord.

· Utilizar os métodos adicionados aos seus modelos através das associações criadas.

1. Associações, Por quê?

Por que precisamos de associações entre os modelos? Porque tornam mais fácil e mais simples operações comuns no código. Considere, por exemplo, uma simples aplicação Rails que inclui um modelo para os clientes e um modelo para as encomendas. Cada cliente pode ter muitas encomendas. Sem as associações, os modelos de declarações seriam mais ou menos assim:

class Customer < ActiveRecord::Base
end

class Order < ActiveRecord::Base
end

Agora, suponha se quisesse acrescentar uma nova encomenda de um cliente existente. Gostaríamos de fazer algo parecido com isto:

@order = Order.create(:order_date => Time.now, :customer_id => @customer.id)

Ou apagar um cliente, e garantindo que todas as suas ordens fossem excluídas também:

@orders = Order.find_by_customer_id(@customer.id)
@orders.each do |order|
  order.destroy
end
@customer.destroy

Com as associações do ActiveRecord, que podemos simplificar – e outras – operações por declarações dizendo que existe uma conexão entre os dois modelos. Veja abaixo o código revisado para a criação de clientes e encomendas:

class Customer < ActiveRecord::Base
  has_many :orders
end

class Order < ActiveRecord::Base
  belongs_to :customer
end

Com essa mudança, criando uma nova ordem para um determinado cliente é mais fácil:

@order = @customer.orders.create(:order_date => Time.now)

Excluindo um cliente e todas as suas encomendas é muito mais fácil:

@customer.destroy

Para saber mais sobre os diferentes tipos de associações, leia a próxima seção deste Guia. Seguido por alguns truques e dicas para trabalhar com as associações e, em seguida, por uma referência completa para os métodos e opções para as associações em Rails.

2. Os tipos de associações

Em Rails, uma associação é uma conexão entre os dois modelos do ActiveRecord. As associações são implementadas usando macro de chamadas, de modo que você pode adicionar funcionalidades nos seus modelos. Por exemplo, declarando que um modelo possui belongs_to com outro, você instrui o Rails a manter as informações de Chave Primária entre instâncias dos dois modelos, e você também receberá um número métodos úteis adicionado ao seu modelo. Rails suporta seis tipos de associação:

* belongs_to 

* has_one 

* has_many 

* has_many :through 

* has_one :through 

* has_and_belongs_to_many

No restante deste guia, você aprenderá a declarar e utilizar as diversas formas de associações. Mas, primeiro, uma introdução rápida a situações em que cada tipo de associação é adequado.

2.1. A Associação belongs_to

Uma associação belongs_to cria uma conexão um-para-um com outro modelo, de modo a que cada instância do modelo com a declaração “pertence a” uma instancia de um outro modelo. Por exemplo, se sua aplicação incluir clientes e encomendas, e cada encomenda pode ser atribuído por exatamente um cliente, você declararia o modelo desta forma:

class Order < ActiveRecord::Base
  belongs_to :customer
end

clip_image002[5]

2.2. A Associação has_one

A associação has_one também prevê a criação de uma conexão um-para-um com outro modelo, mas com uma semântica um pouco diferente (e conseqüências). Esta associação indica que cada instância de um modelo contém ou possui uma instancia de outro modelo. Por exemplo, se cada um fornecedor na sua aplicação possui somente uma conta, você iria declarar o modelo fornecedor como este:

class Supplier < ActiveRecord::Base
  has_one :account
end

clip_image004[5]

2.3. A Associação has_many

Uma associação has_many indica uma conexão um-para-muitos com outro modelo. Você irá encontrar muitas vezes esta associação do “outro lado” de uma associação belongs_to. Esta associação indica que cada instância do modelo possui nenhuma ou mais instancias do outro modelo. Por exemplo, em uma aplicação contendo clientes e encomendas, o modelo cliente deve ser declarado como este:

class Customer < ActiveRecord::Base
  has_many :orders
end

clip_image006[6]

O nome do outro modelo é pluralizado quando declarado uma associação has_many.

clip_image008[5]

2.4. A Associação has_many :through

Um associação has_many :through é freqüentemente utilizado para criar um conexão muitos-para-muitos com outro modelo. Esta associação indica que a declaração do modelo possa ser compensada com zero ou mais instancias de outro modelo “através de” um processo de um terceiro modelo. Por exemplo, considere uma prática médica onde pacientes podem marcar para ver médicos. As declarações relevantes da associação devem ser semelhantes a este:

class Physician < ActiveRecord::Base
  has_many :appointments
  has_many :patients, :through => :appointments
end

class Appointment < ActiveRecord::Base
  belongs_to :physician
  belongs_to :patient
end

class Patient < ActiveRecord::Base
  has_many :appointments
  has_many :physicians, :through => :appointments
end

clip_image010[5]

O associação has_many :through é também útil para a criação de “atalhos” através de associações has_many aninhadas. Por exemplo, se um documento tem muitas seções, e uma seção possui muitos parágrafos, você pode algumas vezes pegar uma simples coleção de todos os parágrafos no documento. Você poderia declarar desta forma:

class Document < ActiveRecord::Base
  has_many :sections
  has_many :paragraphs, :through => :sections
end

class Section < ActiveRecord::Base
  belongs_to :document
  has_many :paragraphs
end

class Paragraph < ActiveRecord::Base
  belongs_to :section
end
2.5. A Associação has_one: through

Uma associação has_one :through cria uma conexão de um-para-um com outro modelo. Esta associação indica que a declaração do modelo possa ser combinada com uma instancia de outro modelo através de um terceiro modelo. Por exemplo, se cada fornecedor possui uma conta, e cada conta está associada a um histórico da conta, então o modelo cliente poderá ser declarado desta forma:

class Supplier < ActiveRecord::Base
  has_one :account
  has_one :account_history, :through => :account
end

class Account < ActiveRecord::Base
  belongs_to :supplier
  has_one :account_history
end

class AccountHistory < ActiveRecord::Base
  belongs_to :account
end

clip_image012[5]

2.6. A Associação has_and_belongs_to_many

Uma associação has_and_belongs_to_many cria uma conexão direta muitos-para-muitos com outro modelo, sem intervir no modelo. Por exemplo, se a sua aplicação incluir peças e conjuntos, onde cada conjunto inclui várias peças e que cada peça aparece em muitos conjuntos, você poderia declarar os modelos desta maneira:

class Assembly < ActiveRecord::Base
  has_and_belongs_to_many :parts
end

class Part < ActiveRecord::Base
  has_and_belongs_to_many :assemblies
end

clip_image014[7]

2.7. Escolhendo Entre belongs_to e has_one

Se você deseja criar uma relação 1×1 entre dois modelos, será necessário adicionar a um belongs_to e ao outro has_one. Como você sabe quem é quem?

A distinção está no lugar onde você colocou a chave estrangeira (ele fica na tabela da classe que foi declarada a associação belongs_to), mas você deveria refletir um pouco mais sobre o real significado desses dados. O relacionamento has_one fala alguma coisa para os seus – ou seja, algo que aponta de volta para você. Por exemplo, faz mais sentido dizer que um fornecedor possui uma conta do que uma conta que possui um fornecedor. Isso sugere que o relacionamento correto é algo como este:

class Supplier < ActiveRecord::Base
  has_one :account
end

class Account &lt; ActiveRecord::Base
  belongs_to :supplier
end

A migração correspondente se parece com isso:

class CreateSuppliers < ActiveRecord::Migration
  def self.up
    create_table :suppliers do |t|
      t.string  :name
      t.timestamps
    end

    create_table :accounts do |t|
      t.integer :supplier_id
      t.string  :account_number
      t.timestamps
    end
  end

  def self.down
    drop_table :accounts
    drop_table :suppliers
  end
end

clip_image006[7]

Usando t.integer :supplier_id faz a nomeação da chave estrangeira óbvia e implicitamente. Nas versões atuais do Rails, você pode abstrair a implementação deste detalhe usando t.references :supplier.

2.8. Escolhendo Entre has_many :through e has_and_belongs_to_many

Rails oferece duas maneiras diferentes para declarar um relacionamento um-para-muitos entre modelos. A maneira mais simples é usar has_and_belongs_to_many, que permite que você faça a associação diretamente:

class Assembly < ActiveRecord::Base
  has_and_belongs_to_many :parts
end

class Part < ActiveRecord::Base
  has_and_belongs_to_many :assemblies
end

A segunda forma de declarar o relacionamento muitos-para-muitos é usar has_many :through. Isto faz com que crie uma associação indiretamente, através de um join no modelo:

class Assembly < ActiveRecord::Base
  has_many :manifests
  has_many :parts, :through => :manifests
end

class Manifest < ActiveRecord::Base
  belongs_to :assembly
  belongs_to :part
end

class Part < ActiveRecord::Base
  has_many :manifests
  has_many :assemblies, :through => :manifests
end

A regra é simples, você deve criar um relacionamento has_many :through se você precisa trabalhar com o relacionamento do modelo como uma entidade independente. Se você não precisa fazer nada com relacionamento do modelo, pode ser mais simples se criar um relacionamento has_and_belongs_to_many (mas você precisa se lembrar se precisa criar joins nas tabelas).

Você deve usar has_many :through se você precisar de validações, callbacks (chamadas), ou atributos extras no join do modelo.

2.9. Associações polimórficas

Um pouco mais avançado é o twist em associações polimórficas. Com associações Polimórficas, um modelo pode pertencer a mais de um modelo, em uma única associação. Por exemplo, imagine que você possa ter um modelo foto que pertence a um empregado ou ao modelo produto. Veja como isso poderia ser declarada:

class Picture < ActiveRecord::Base
  belongs_to :imageable, :polymorphic => true
end

class Employee < ActiveRecord::Base
  has_many :pictures, :as => :imageable
end

class Product < ActiveRecord::Base
  has_many :pictures, :as => :imageable
end

Você pode pensar em uma declaração belongs_to polimórfica criando uma interface que pode ser usada por qualquer outro modelo. A partir de uma instância do modelo Employee, você pode recuperar uma coleção de fotos: @employee.pictures. Da mesma forma, você pode usar @product.pictures. Se você tem uma instancia do modelo Picture você pode pegar seu pai através @picture.imageable. Para que isto funcione, você necessita declarar tanto uma coluna chave estrangeira e uma coluna tipo no modelo declarado com uma interface polimórficas:

class CreatePictures < ActiveRecord::Migration
  def self.up
    create_table :pictures do |t|
      t.string  :name
      t.integer :imageable_id
      t.string  :imageable_type
      t.timestamps
    end
  end

  def self.down
    drop_table :pictures
  end
end

Esta migração pode ser simplificada utilizando o formulário t.references:

class CreatePictures < ActiveRecord::Migration
  def self.up
    create_table :pictures do |t|
      t.string  :name
      t.references :imageable, :polymorphic => true
      t.timestamps
    end
  end

  def self.down
    drop_table :pictures
  end
end

clip_image016[9]

2.10. Self Joins

Na criação de um modelo de dados, às vezes você irá encontrar um modelo que deverá ter uma relação a si própria. Por exemplo, você pode armazenar todos os empregados em uma única base de dados, mas ser capaz de rastrear relacionamentos como gerentes e subordinados. Esta situação pode ser modelada com as associações com ele mesmo:

class Employee < ActiveRecord::Base
  has_many :subordinates, :class_name => "User", :foreign_key => "manager_id"
  belongs_to :manager, :class_name => "User"
end

Com essa configuração, você pode utilizar @employee.subordinates e @employee.manager.

Créditos:

Link Conteúdo Original: http://guides.rails.info/association_basics.html

Tradução: Herminio Torres. [ http://hidenowt.wordpress.com ]

Revisão: Cairo Noleto. [ http://www.caironoleto.com ]