1

I have seen some related questions on my issue, but nothing that quite covers exactly what I need. We have 3 database tables that are splitting data on booths that I need accessed in our javascript. I will be pulling this data to convert into an array of javascript objects. The issue I have is trying to perform the join between these 3 tables, then pushing certain values into an object in an efficient manner that won't be too resource-draining. My 3 objects are outlined here. I am not very familiar with RoR still, so I wrote out what my query would look like in SQL first:

SELECT b.BoothNumber, bt.Width, bt.Depth, bl.room_id, bl.x, bl.y
FROM Booths b
LEFT JOIN booth_types bt ON bt.id = b.booth_type_id
JOIN booth_locations bl ON bl.id = b.booth_location_id
WHERE bl.room_id = @room_ID_provided_by_javascript

I can do the last line in the JS, but I included just for clarity. What is my best option? My first attempt looks like the below code. Of course, it doesn't work, because room_id/etc. does not belong to the 'booth' object.

    <% Booth.includes(:booth_location, :booth_type).find_each do |booth| %> 
        if(roomIds.indexOf(<%= booth.room_id%>) > -1){//If index in roomIds (is this room in the event?)
            booths.push({boothId: <%= booth.id %>, roomId: <%= booth.roomId %>, x: <%= booth.x %>, y: <%= booth.y %>,
                boothNumber: <%= booth.boothNumber %>, companyId: <%= booth.companyId %>, width: <%= booth.width %>, depth: <%= booth.depth %>});
        }
    <% end %>

I have the basic belongs_to and has_many connections between these tables in my controllers, but nothing more. I understand I need to include some sort of use of the through keyword in my controllers, but I don't really know how those are supposed to align with how my database is setup. I've seen answers vary from adding 20+ lines to each controller to just writing an extra few words on my opening Ruby line. What will work for me?

Ryan Minor
  • 25
  • 1
  • 6

1 Answers1

0

Learning has_many :through is a different concern than pushing to Javascript. Part of MVC is removing DB logic from HTML. It should instead be done in the model (though is often done in Controllers as well). I do sometimes write DB logic in HTML but it's a lazy approach.

Here's an example to explain has_many :through:

Say the Post table has a user_id column and the Tag table has a post_id column. In other words, a user has many Posts, and each Post has many Tags. We're trying to link the User and Tag columns so we can say a User has many Tags. We could write the following associations:

  • in post.rb: belongs_to :user
  • in tag.rb: belongs_to :post; has_one :user, through: :post
  • in user.rb: has_many :posts; has_many :tags, through: :posts

Note that there is no belongs_to with through, you need to use has_one instead.

You can get a little more flexibility in naming by using the source option.

class Tag
  has_one :post_author, through: :post, source: :author
  # if for example "post.author" were used instead of "post.user"
end

This is the simplest way to make associations for join tables. There are also other, more complicated approaches.

All that methods like has_many and belongs_to are doing is creating a getter method for your model. You can write your own instead if you are comfortable with ActiveRecord query syntax.

For example:

class User
  def tags
    Tag.joins(:posts).where("posts.user_id = ?", self.id)
    # Make sure this method returns an ActiveRecord_Relation,
    # not an array. 
  end
end

This user instance method will behave the same way as if you had defined it using has_one through. This approach is more customizable, but the has_one approach is less typing for simple cases.

There's also the concept of scopes, which has a domain-specific syntax for writing these sorts of methods.

So, as for Javascript, I'd recommend changing your approach a little. Passing all your data from Ruby to Javascript through ERB tags can start to get unwieldy. It's probably a better idea convert the data to JSON before sending it to Javascript. There are a lot of Rails helpers for converting data to Javascript, such as to_json. I'll show you an example for how I'd convert a hierarchical data set to JSON:

class User
  def attributes
    super.merge(
      posts: self.posts.map(&:attributes),
      tags: self.tags.map(&:attributes)
    )
  end
end

and then I'd make a controller method which responds with JSON:

render json: { user: @user.attributes }.to_json

This will structure the data so you could write user["posts"] and user["tags"]

If you wanted to nest the tags in posts, i.e. so you could write user["posts"][0]["tags"]

you could overwrite the Post attributes as well:

class Post
  def attributes
    super.merge(
      tags: self.tags.map(&:attributes)
    )
  end
end

But be careful not introduce recursion.

The final step to passing your Ruby data to Javascript is fetching it using AJAX.

Rails has view helpers for AJAX, but I've personally preferred to write the code in Javascript / jQuery, not with ERB. See this question for some info on the Javascript approach.

Community
  • 1
  • 1
max pleaner
  • 26,189
  • 9
  • 66
  • 118