You are here: Home Solidarrow Ruby on Rails Solidarrow Relationships

Self-referential table joins

We show you how to create a HABTM association for a table that links back to itself. For example, an article that has many related articles, or a person who has many related people.

 

Imagine you have a table of articles for a blog. Now imagine that for each article, you would like to be able to list all similar articles. Or a table containing people, where you would like to link related people to each other. This is a has_and_belongs_to_many association, but there's only one table involved. It's a self-referential relationship: a person has many people, and one of those people may also have many other people, and so on. So how do we implement that in Rails?

The join table

Like all other HABTM relationships, we'll need to create a join table that keeps track of which articles are related to one another. However, in this case, both of the ids in the join table will each belong to an article. We'll want a column to keep track of the main article's id, and a column that contains the id of the related article. Let's called this join table related_articles and create a migration...

    create_table "related_articles", :force => true, :id => false do |t|
        t.column "related_article_id", :integer
        t.column "main_article_id", :integer
    end

Remember that as this is a join table, it doesn't require its own id, so we set :id => false. Now let's take a look at the Article class...

The self-referential class

Article is just like any other class, except we need to be verbose when defining the HABTM relationship. In a standard HABTM relationship, Rails would be able to work out the name of the join table, but in this case, the join table doesn't join two models together, it joins a model back to itself. We need to specify some additional options as follows...

    class Article < ActiveRecord::Base

        has_and_belongs_to_many :related_articles, :class_name => "Article", :join_table => "related_articles", :foreign_key => "main_article_id", :association_foreign_key => "related_article_id"

    end

So what do each of those options do?

  • :join_table is where we tell Rails the name of our join table. We called it related_articles.
  • :foreign_key specifies the name of the foreign key column that contains the id of the main article, the one that is acting as a parent of the related articles: we called this main_article_id
  • :association_foreign_key is the name of the foreign key column that contains the id of our related articles--the articles that "belong to" our main article
  • :class_name contains the name of our current class, Article

Implementation

A suitable interface to allow us to add related articles would be a select drop-down list that can accept multiple options. Let's edit the form in our new.rhtml view to allow us to select related articles when adding a new article...

    <% form_for(@article) do |f| %>

      <b>Content</b><br />
      <%= f.text_field :content %>

      <b>Title</b><br />
      <%= f.text_field :title %>

      <b>Related articles</b><br />
      <%= f.collection_select(:related_article_ids, Article.find(:all), :id, :title, {}, :multiple => true) %>

      <%= f.submit "Create" %>

    <% end %>

We use the collection_select helper with the related_article_ids method. This method automatically becomes available when we define a HABTM relationship, and it accepts an array of ids to insert into our join table. We use Article.find(:all) to provide a list of all the other articles in our database, and ask collection_select to format them so that the :id gets sent when we submit the form, but the :title is displayed in the drop-down list. We also enable :multiple => true so that we can choose more than one article.

I won't go into any further details, as drop-down lists for HABTM are a completely separate article, but don't worry--it's one I'll be writing about soon (check the right-hand side to see our very own related articles, as I might already have written it!)

 

* Comments

3 MONTHS AGO

About Controllers

Hello, very interesting and very useful lessons. i learn lots of thinks from this codes. Also add lessons for controller and model part.

 

> RELATED ARTICLE

* Auto migrations

A brilliant plugin that makes changing your database schema even easier and faster. > More