sexta-feira, 9 de novembro de 2012

Filtrando associações em Rails

Alguns posts aqui no blog já exemplificaram como funcionam as associações na camada de modelo do Rails com ActiveRecord e como efetuar alguma pesquisa (query) a partir de algum modelo. Neste post vou demonstrar como combinar as duas coisas, efetuando filtros a partir de uma associação, usando tanto recurso de blocos quanto de mixins com módulos, também já abordados em posts anteriores.






Vou utilizar os mesmos modelos aqueles de Pessoa, Livro e Leitura:

class Pessoa < ActiveRecord::Base
  attr_accessible :nome, :idade

  has_many :leituras
  has_many :livros, through: :leituras
end

class Livro < ActiveRecord::Base
  attr_accessible :titulo

  has_many :leituras
  has_many :leitores, through: :leituras, source: :pessoa
end

class Leitura < ActiveRecord::Base
  attr_accessible :livro_id, :pessoa_id

  belongs_to :pessoa
  belongs_to :livro
end


Ao buscar a lista de leitores de um livro, intuitivamente espera-se um Array, porém, em vez de retornar um Array com todos os objetos resultantes, o Rails retorna uma instância de um proxy próprio, que já nos possibilita invocar diretamente na associação alguns métodos como o create, que delega a chamada ao create do respectivo modelo da associação:

novo_leitor = livro.leitores.create(nome: "Daniel")


Além de create, as associações em Rails nos permitem invocar outros métodos como count, clear, etc. Mas e se quisermos invocar um método dessa forma?

leitores_criancas = livro.leitores.criancas


Há duas opções. A primeira é utilizar Mixins definindo em um módulo separado as queries personalizadas de que desejamos dispor e estendendo nossa associação com ele:

module FiltroLeitor
  def criancas
    where('idade < 10')
  end
end

require "filtro_leitor"

class Livro < ActiveRecord::Base
  attr_accessible :titulo

  has_many :leituras
  has_many :leitores, through: :leituras, source: :pessoa,
                      extend: FiltroLeitor
end


Uma vantagem desta primeira opção é que a associação leitores fica menos poluída, especialmente se vários métodos de pesquisa forem definidos.

E a segunda opção é adicionar um bloco à associação:

class Livro < ActiveRecord::Base
  attr_accessible :titulo

  has_many :leituras
  has_many :leitores, through: :leituras, source: :pessoa do
                        def criancas
                          where('idade < 10')
                        end
                      end
end


Com a primeira ou a segunda opção, poderemos testar rodando os seguintes comandos no rails console:

# Primeiro criamos o livro
livro = Livro.create(titulo: "Meu pé de laranja lima")

# Adicionamos quatro leitores, sendo dois crianças
livro.leitores.create(nome:"Daniel", idade: 27) # eu
livro.leitores.create(nome:"Juliana", idade: 26) # patroa
livro.leitores.create(nome:"Isabela", idade: 5) # sobrinha
livro.leitores.create(nome:"Samuel", idade: 2) # sobrinho superdotado

livro.leitores.count    # Retorna 4
livro.leitores.criancas.count   # Retorna 2


Recurso muito interessante e bem útil!

Até a próxima!

Nenhum comentário:

Postar um comentário