O guia definitivo de Relacionamentos no Rails

Ruby on Rails
15 de junho de 2020

Uma das estruturas mais poderosas, e complexas do Rails é sem dúvida o Active Record. Em poucos minutos podemos criar um novo model e sua tabela para armazenar os dados. É normal que na hora de declarar relacionamentos, duvidas acabem surgindo. Então, eu decidi escrever este artigo para ajudar você a fazer uso desse recurso, quase mágico, que facilita nossas vidas diariamente.

giphy

Um relacionamento é a ligação entre duas classes do Active Record. Através dele podemos executar operações nos registros de forma mais fácil, como consultas e salvamento de informações. Podemos dividi-los em três tipos básicos:

  1. Um para um
  2. Um para muitos
  3. Muitos para muitos

Se você é novo no Rails, models são a abstração das tabelas do banco, utilizando classes do Active Record. Pense que para cada linha existente em uma tabela chamada users podemos instanciar um objeto da classe User, dentro da aplicação.

Uma das principais características do Ruby é a expressividade, permitindo que qualquer pessoa, possa ler o código e entender o que representa cada comando ou método escrito. Tal característica também é encontrada no Rails.

Vamos imaginar que estamos construindo um sistema de entregas. Isso vai nos ajudar a aplicar, de forma prática, alguns dos conceitos que vamos abordar neste artigo. Se vamos falar de entrega, é importante que nosso App tenha cadastro de usuários e endereços.

Um para um

De acordo com a documentação de nosso aplicativo, cada usuário deve possuir apenas um endereço cadastrado. Neste caso, podemos aplicar um relacionamento um para um. Vamos ver como se dá a declaração dentro das classes do ActiveRecord:

class User < ApplicationRecord
  has_one :address
end

class Address < ApplicationRecord
  belongs_to :user
end

Conforme podemos perceber, utilizamos métodos para indicar os relacionamentos. Em um model será utilizado o has_one e no outro será o belongs_to. Analisando o código, podemos entender que o usuário tem um endereço e que o endereço pertence a um usuário, com base nos termos em inglês aplicados para nomear os respectivos métodos.

É relevante saber que o Rails é construído sobre convenções, e assim podemos usar essa frase como mantra: O nome de uma função deve indicar o que ela faz. Caso contrário, o uso desta será desagradável.

O belongs_to ficará no model que possui a chave estrangeira. Como podemos observar na imagem abaixo, a coluna user_id faz o vínculo de informação, indicando que o endereço pertence ao usuário informado, pelo ID.

Diagrama de representação de um relacionamento um para um

Se for seguida a convenção, o relacionamento belongs_to deve ser declarado no singular. A coluna que armazenará a referência, será o nome do relacionamento acrescido de _id. Assim foi feito no diagrama acima, indicando a coluna user_id para o relacionamento user. Por consequência, espera-se que a outra tabela possua uma chave primária, que de acordo com a convenção é a coluna id.

É importante adicionarmos um índice de unicidade no banco de dados, que impeça múltiplas duplicidade. Tal adição adição é possível via migration, conforme o exemplo abaixo.

class AddUniqueUserToAddresses < ActiveRecord::Migration[6.0]
  def change
    add_index :addresses, :user_id, unique: true
  end
end

Um para muitos

Vamos imaginar que neste mesmo sistema um usuário possa ter mais de um pedido. Neste caso, utilizaremos um relacionamento um para muitos.

O primeiro passo é identificar em qual model terá o has_many e qual será utilizado o belongs_to. De acordo com o requisito deste sistema, um usuário pode ter vários pedidos, mas um pedido pertencerá apenas a um usuário. Sendo assim, a representação no diagrama será conforme abaixo:

Diagrama de representação de um relacionamento um para muitos

Manteremos a mesma regra aplicada ao relacionamento anterior, adicionando belongs_to no mesmo model que possui a chave estrangeira, neste caso user_id. As classes do ActiveRecord ficarão assim:

class User < ApplicationRecord
  has_many :orders
end

class Order < ApplicationRecord
  belongs_to :user
end

Muitos para muitos

Os relacionamentos muitos para muitos podem ser declarados de duas formas no Rails. Vamos entender a diferença.

Ter e pertencer a muitos

Parece algo esquisito, lendo assim em português, mas o método has_and_belongs_to_many significa exatamente isso. Ele cria uma associação direta, do tipo muitos para muitos, com o outro model. Para utilizá-lo basta apenas chamar o método em ambos os models.

Diagrama de representação de um relacionamento muitos para muitos

Vamos dizer que nesse sistema seja possível adicionar múltiplos produtos a um pedido, e que um produto pode pertencer mais de um pedido. As respectivas classes seriam, como segue:

class Order < ApplicationRecord
  has_and_belongs_to_many :products
end

class Product < ApplicationRecord
  has_and_belongs_to_many :orders
end

Você precisará criar uma join table para que esse relacionamento funcione. Essa será uma tabela que conecta dois models diferentes. Podemos criá-la utilizando o próprio rails, através da função create_join_table em uma migration separada.

class CreateOrdersProducts < ActiveRecord::Migration[6.0]
  def change
    create_join_table :orders, :products
  end
end

Essa migration criará uma tabela chamada orders_products. Ela será usada para armazenar o ID do pedido e do produto que estão relacionados.

Tem muitos através de um relacionamento

Por definição, é possível criar relacionamentos que se baseiam em outros relacionamentos. Isso parece confuso, mas vamos por partes!

Vamos supor que em nosso sistema exista, além das colunas de relacionamentos, mais informações nessa tabela intermediária, como quantidade e valor unitário.

Neste caso, o ideal seria criar um terceiro model, denominado OrderProduct, e sua respectiva tabela, com todas as colunas do diagrama. Assim vamos ter mais controle durante a adição e remoção de registros, e poderemos adicionar validações, utilizar callbacks e todos os outros recursos que o ActiveRecord pode nos proporcionar. Para que isso seja possível, os relacionamentos ficarão um pouco diferente do exemplo anterior.

class Order < ApplicationRecord
  has_many :order_products
  has_many :products, through: :order_products
end

class Product < ApplicationRecord
  has_many :order_products
  has_many :orders, through: :order_products
end

class OrderProduct < ApplicationRecord
  belongs_to :order
  belongs_to :product
end

Esse relacionamento permitirá que você faça coisas como order.products e obtenha uma lista de todos os produtos existentes para o pedido criado. Também permitirá que você realize o processo inverso, obtendo quais pedidos estão vinculados a um produto.

Em breve, publicarei um artigo explicando como customizar as opções dos relacionamentos, com nomes de colunas e tabelas não padronizados e até mesmo utilizando relacionamentos polimórficos.

Deixe um comentário, contando pra gente um pouco mais qual sua principal dificuldade na hora de usar o ActiveRecord.

Até a próxima!

5 min. de leitura
Top