[RoR] Relacionamento NxN com Ruby on Rails
21 set 2011(há 14 anos)[RoR] Relacionamento NxN
introdução
Dando continuidade ao ultimo post, hoje veremos como configurar relacionamento NxN em Rails, nosso relacionamento será: Usuário, como autor possui vários posts e post pertence a um autor.
1 - Vamos começar adicionando um campo author_id ao model Post pois é ele que será relacionado com o model User (author), ou seja, um author tem varios post, bem sem mais delongas, "let's go nessa"... vamos gerar uma migration para adicionar o campo author_id
rails g migration add_author_id_to_post author_id:integer
Bem vamos abrir o arquivo 20110914163321_add_author_id_to_post.rb para verificar se está tudo cerinho, vejam que no arquivo no método up existe a linha: add_column :posts, :author_id, :integer
, que faz referência a Post, porém em nenhum momento eu disse que ele teria um referencia de Post, bem isso acontece por causa de uma das convenções de rails no comando ...migration add_author_id_to_post...
o to_post do final indica que ele deve referenciar Post
class AddAuthorIdToPost < ActiveRecord::Migration def self.up add_column :posts, :author_id, :integer end def self.down remove_column :posts, :author_id end end
Vamos rodar um "rake db:migrate" pois é assim que queremos que fique essa migration:
rake db:migrate
Depois do "rake db:migrate" precisamos fazer as configurações dos models User, que será o "Author", e Post, bem no model User adicionaremos:
has_many :posts
Isso identifica que um usuário terá vários posts, o model Post adicionaremos:
belongs_to :author, :class_name => "User", :foreign_key => "author_id"
Que indica que um post pertence a um autor que é referenciada na classe User e author_id como chave estrangeira, aproveitando que estamos no post e também adicionaremos validações para autor deste post, bem no model já existiam algumas validações para atributo em branco adicionaremos o de autor associado:
validates_presence_of :title, :body, :author validates_associated :author
vamos adicionar um método para retornar o nome completo:
def full_name "#{first_name} #{last_name}".capitalize end
Bem agora é hora inserir o combobox de user(author) no formulário de post, vou inserir antes do campo title:
<%= f.label :author_id, "Author" %>
<%= f.collection_select :author_id, @authors, :id, :full_name, :prompt => "Selecione o autor" %>
Vejam que não declarei o @authors em canto nenhum, poderia passar nas actions new, create, edit, update, porém rails tem uma coisa bacana chamada before_filter que podemos dizer que um metodo será carregado em algumas actions pré-definidas, vamos ver com vai ficar o post_controller:
# adicione esta linha no inicio before_filter :load_authors, :only => [:new,:create, :edit, :update] # e esse método no fim protected def load_authors @authors = User.all end
Depois disso vamos criar um scaffold de categoria e rodar um "rake db:migrate":
rails g scaffold category name:string rake db:migrate
Como em todo banco relacional quando há um relacionamento NxN é necessário criar tabela auxiliar para isso é só rodar uma "migration"
rails g migration create_categories_posts
abra o arquivo que foi criado e deixe-o como o code abaixo:
class CreateCategoriesPosts < ActiveRecord::Migration def self.up create_table "categories_posts", :id => false, :force => true do |t| t.integer "post_id", :null => false t.integer "category_id", :null => false end end def self.down drop_table "categories_posts" end end
e rode um
rake db:migrate
no model category adicione:
validates_presence_of :name
e no model post adicione:
has_and_belongs_to_many :categories
e o inverso model categoty:
has_and_belongs_to_many :posts
Lá em cima usamos um before_filter com o metodo load_authors vou renomea-lo para load_resources, pois ele ira carregar também as categorias, então no post_controller mude para
# adicione esta linha no inicio before_filter :load_resources, : only => [:new,:create, :edit, :update] # e esse método no fim protected def load_resources @categories = Category.all @authors = User.all end
Depois de fazer as alterações acima é necessário colocar o campo categoria no form post:
<% @categories.each do |c| %> <%= check_box_tag "post[category_ids][]", c.id, @post.categories.include?(c) %> <%= c.name%> <% end %>
Se você tentar testar se tudo der certo não irar apercer nenhum checkbox mas é só acessar http://0.0.0.0:3000/categories e adicionar algumas categorias.
Conclusão
Para finalização deste post vamos deixar a receita de bolo de como fazer o relacionamento NxN.
1º has_and_belongs_to_many nos dois modelos
- para o rails criar os métodos de relacionamento ex: para pegar todas as categorias de um post é só chamar o método Post.first.categories
2º criar migração manualmente
3º ajustar migração sem chave primári
4º seguir a ordem alfabética para nome das tabelas
Como sempre ta aqui o link para download do projeto até então, bem "Isso é tudo pessoal" em breve voltarei com a continuação