Associação Polimórfica

Tutoriais - 03/Out/2019 - por Campus Code

Em linguagens de programação orientadas a objetos, como o Ruby, polimorfismo descreve um conceito que provê uma única interface compartilhada entre objetos diferentes, dependendo de sua classe ou tipo de dado. Isso pode ser obtido de algumas formas, tirando proveito das propriedades das linguagens de programação como, por exemplo, Herança e duck typing. Neste texto, vamos falar sobre uma forma específica de usarmos polimorfismo em Ruby on Rails: a Associação Polimórfica.

Associação Polimórfica

O framework Rails oferece uma forma muito elegante de usarmos polimorfismo por meio da Associação Polimórfica. Essa estratégia se baseia no conceito de Polimorfismo Paramétrico. De acordo com este artigo:

Polimorfismo Paramétrico: código que é escrito sem menção a um tipo específico e, portanto, pode ser usado de forma transparente com qualquer número de tipos.

Para falar mais sobre Associações Polimórficas, vamos utilizar um exemplo importado das redes sociais. É muito comum que publicações nas redes possam ser "curtidas" por outros usuários. Além disso, as fotos e até os comentários nessa publicação também podem receber curtidas. Nesse cenário, nossa aplicação teria as classes Post, Comment e Image, cada um deles podendo receber curtidas, representadas pela classe Like. Se cada instância de Like estiver atrelada a um tipo específico, seria necessário criar mais um model para cada associação: post_like, comment_like e image_like, com as amarrações necessárias entre eles.

Utilizando as Associações Polimórficas, um Model pode pertencer a mais de um Model com uma única associação. Vamos fazer o processo passo a passo começando pela criação dos Models com os comandos do Rails.

  $ rails generate model post
  $ rails generate model comment
  $ rails generate model image

Agora falta gerar o Model Like e é aqui que as coisas ficam interessantes. Com Rails, podemos gerá-lo usando o seguinte comando:

  $ rails generate model like likeable:references{polymorphic}

Lembre de rodar rails db:migrate em algum momento depois de gerar os Models.

No arquivo like.rb, a declaração do belongs_to polimórfico é responsável pela configuração da interface que os outros Models poderão usar:

class Like < ApplicationRecord
  belongs_to :likeable, polymorphic: true
end

E a sua migration ficou assim:

class CreateLikes < ActiveRecord::Migration[5.0]
  def change
    create_table :likes do |t|
      t.string :name
      t.references :likeable, polymorphic: true, index: true
      t.timestamps
    end
  end
end

Note que likeable será usado como referência nas amarrações polimórficas. Assim, para finalizar as associações, precisamos declarar o has_many em cada um dos outros Models que podem receber "curtidas":

class Post < ApplicationRecord
  has_many :likes, as: :likeable
end

class Comment < ApplicationRecord
  has_many :likes, as: :likeable
end

class Image < ApplicationRecord
  has_many :likes, as: :likeable
end

Agora que está tudo pronto, podemos observar como isso vai funcionar na prática, fazendo alguns testes no console do Rails.

$ rails console
Running via Spring preloader in process 10918
Loading development environment (Rails 5.2.3)

2.6.0 :001 > article = Post.create
 => #<Post id: 1, created_at: "2019-07-31 15:21:59", updated_at: "2019-07-31 15:21:59">

2.6.0 :002 > article.likes.create
 => #<Like id: 1, likeable_type: "Post", likeable_id: 1, created_at: "2019-07-31 15:22:18", updated_at: "2019-07-31 15:22:18">

Note que likeable_id se refere ao objeto article ao qual a instância de Like está associada. Nesse caso é 1, pois é o primeiro objeto da classe Post. Agora, é possível resgatar as curtidas da publicação, por exemplo, com article.likes, que nos retorna um Array com todos os likes associados ao objeto.

2.6.0 :003 > article.likes
 => #<ActiveRecord::Associations::CollectionProxy [#<Like id: 1, likeable_type: "Post", likeable_id: 1, created_at: "2019-07-31 15:22:18", updated_at: "2019-07-31 15:22:18">]>

Quando criamos mais um like para article:

2.6.0 :004 > article.likes.create
 => #<Like id: 2, likeable_type: "Post", likeable_id: 1, created_at: "2019-07-31 15:23:20", updated_at: "2019-07-31 15:23:20">

A instância criada tem id 2, mas likeable_id permanece o mesmo, pois ele está associado ao mesmo objeto article.

Graças à associação polimórfica, objetos da classe 'Like' podem ser atrelados a qualquer outro objeto ao qual atribuímos a característica ''likeable', independente do seu tipo. :)

Você pode ler mais sobre a associação polimórfica e outras maneiras de utilizar o polimorfismo nos artigos listados abaixo.

Referências