Alex Zakruzhetskyi
Alex Zakruzhetskyi

Reputation: 1433

How to loop through a joined table

The models I have:

Category:

class Category < ApplicationRecord
  has_many :categorizations
  has_many :providers, through: :categorizations
  accepts_nested_attributes_for :categorizations
end

Provider:

class Provider < ApplicationRecord
  has_many :categorizations
  has_many :categories, through: :categorizations
  accepts_nested_attributes_for :categorizations
end

Categorization:

class Categorization < ApplicationRecord
  belongs_to :category
  belongs_to :provider
  has_many :games, dependent: :destroy
  accepts_nested_attributes_for :games
end

Game:

class Game < ApplicationRecord
  belongs_to :categorization
end

I need to display the games, that belongs to a specific provider. I tried to do it like:

<% @provider.categorizations.joins(:games).each do |game| %>
 <%= game.title %>
<% end %>

It gives me an error: NoMethodError: undefined method 'title' for #<Categorization:0x007f2cf6ee49e8>. So, it loops through the Categorization. What is the best way to loop through the joined games table? Thanks.

Upvotes: 0

Views: 1183

Answers (2)

JayJay
JayJay

Reputation: 814

I'm guessing 'title' is an attribute of games and not categorization, so you either need to return an array of games, or add a select on the end to pull the title attribute into the categorization object, like so:

<% @provider.categorizations.joins(:games).select('dba.games.title').each do |game| %>
  <%= game.title %>
<% end %>

Just to add- you shouldn't really be doing this in the view file. I'd go as far as not even doing this in the controller. I tend to encapsulate this sort of logic into a service class, which is instantiated in the controller to return a set of results. The controller should only be passing the result set on, which is then presented by the view.

class Provider < ActiveRecrord::Base

    # this could be a scope instead, or in a seperate class which 
    # the provider model delegates to- whatever floats you boat
    def get_games
        # you could use pluck instead, which would return an array of titles
        categorizations.joins(:games).select('dba.games.title')
    end
end 

class ProviderController < ApplicationController
    def show
        provider = Provide.find(params[:id])
        @games = provider.get_games
    end
end

<% @games.each do |game| %>
    <%= game.title %>
<% end %>

Upvotes: 0

Syl
Syl

Reputation: 3829

First, you should do the request in your controller, or even better call a scope (defined in a model) from the controller.

Do not forget that Active Record is just an ORM, a tool allowing you to manipulate SQL.

With @provider.categorizations.joins(:games) you are not asking for games. You are asking for the categorizations and you do a JOIN with the games table. This joins is usually to allow to filter by games attributes.

To do what you want you should do the following :

@games = Game.joins(:categorization).where('categorization.provider_id = ?',@provider.id)

As you can see, the join do not return categorization, it allow me to use categorization as a filter.

You should always be aware of the SQL generated by Active Record. Look at the SQL query generated in your server's traces.

Upvotes: 1

Related Questions