Timmy Von Heiss
Timmy Von Heiss

Reputation: 2218

Is there a simpler way than using STI or polymorphic associations?

I have been reading a lot on the difference between STI and polymorphic associations and decided to use STI:

user.rb
Class User < ActiveRecord::Base
   has_many :articles
end

article.rb
Class Article < ActiveRecord::Base
   belongs_to :users
end

sport.rb
Class Sport < Article
end

politic.rb
Class Politic < Article
end

food.rb
Class Food < Article
end

create_table "articles", force: :cascade do |t|
t.string   "title"
t.string   "artwork"
t.integer  "user_id"
t.datetime "created_at",                 null: false
t.datetime "updated_at",                 null: false
t.boolean  "approved",   default: false
t.string   "type"

However, upon further reading, this becomes even more complicated. All I am really looking to do is to find some way to sort my articles by type. For example, is it possible that I simply have a string column tag and specify that tag must be either politics, sports, or food?

Upvotes: 0

Views: 350

Answers (1)

Richard Peck
Richard Peck

Reputation: 76784

In this case, use an enum:

#app/models/article.rb
class Article < ActiveRecord::Base
  enum article_type: [:sport, :politic, :food] #-> "article_type" (int) column required
end 

The only drawback to this would be that you can only assign one enum value to your model; from the use case you've outlined, it seems that's what you need.


The enum will allow you to use the following:

@article = Article.find params[:id]

@article.sport?    #-> true
@article.politic?  #-> false
@article.food?     #-> false

@article.profile_type #-> "sport"

You also get a set of class methods to identify the various objects you need from the db:

@sports_articles = Article.sport #-> collection of "sport" articles

To create an @article through a form, you'll need collection_select:

#app/views/articles/new.html.erb
<%= form_for @article do |f| %>
   <%= f.collection_select :profile_type, Article.profile_types, :first, :first %>
   <%= f.submit %>
<% end %>

Update

Pagination occurs on data received from the db.

Thus, if you wanted to "include" data in the pagination, you'd just have to make sure you're pulling it from the db. To do this, you'd need to include as many article_types as you want:

#app/models/article.rb
class Article < ActiveRecord::Base
   scope :by_type, (types) -> { where(article_type: Array.new(types)) }
end

This will allow you to use the following:

@articles = Article.by_type(:sport, :politic).paginate(page: params [:page], per_page: 12)

As per the docs:

Conversation.where(status: [:active, :archived])

Upvotes: 1

Related Questions