実現させたいこと
YouTubeやFacebookのタイムラインのように、投稿と広告を作成降順に同じ画面に表示する機能が必要になった(通常なら広告は作成順の表示ではないかもしれないが、話を簡単にするために作成順で並べると仮定する)。
上記の状況に限らず、いくつかのモデルパターンを想定して、コントローラーでうまいこと取得し、ビューで並列して処理する方法を考える。
前提
バージョン
- Ruby 2.6.3
- Rails 5.2.3
ケース1
関連のないモデルを作成日順に並べる
# 投稿モデルと広告モデルはカラムが異なるとする
# 投稿モデル(Post)
t.integer "user_id"
t.string "content"
t.string "image"
t.datetime "created_at"
t.datetime "updated_at"
# 広告モデル(Advertisement)
t.integer "company_id"
t.string "content"
t.string "link_url"
t.datetime "expired_date"
t.datetime "created_at"
t.datetime "updated_at"
実装方法
timeline_controller.rb
def index
posts = Post.all
ads = Advertisement.all
# それぞれの複数インスタンスを1つの配列にする
@instances = posts | ads
# 作成降順(新しい順)に並び替え
# 作成昇順の場合は、a.created_at <=> b.created_at }
@instances.sort!{ |a, b| b.created_at <=> a.created_at }
end
index.html.erb
<% @instances.each do |instance| %>
<% if instance.class == "Post" %>
<%# Postの表示 %>
<%= instance.user_id %>
<%= instance.content %>
<%= instance.image %>
<% else %>
<%# Advertisementの表示 %>
<%= instance.company_id %>
<%= instance.content %>
<%= instance.link_url %>
<% end %>
<% end %>
ケース2
関連のないモデルを異なるDate型カラムで日付順に並べる
# ケース1と同じ
# 投稿モデル(Post)
t.integer "user_id"
t.string "content"
t.string "image"
t.datetime "created_at" <= これで並び替えたい
t.datetime "updated_at"
# 広告モデル(Advertisement)
t.integer "company_id"
t.string "content"
t.string "link_url"
t.datetime "expired_date" <= これで並び替えたい
t.datetime "created_at"
t.datetime "updated_at"
実装方法
timeline_controller.rb
def index
posts = Post.all
ads = Advertisement.all
@instances = posts | ads
# 投稿モデルの場合はcreated_atを、広告モデルの場合はexpired_dateで判定する
@instances.sort! do |a, b|
a_created_at = a.class == "Post" ? a.created_at : a.expired_date
b_created_at = b.class == "Post" ? b.created_at : b.expired_date
b_created_at <=> a_created_at
end
ケース3
子がある場合は子の最新の作成日、ない場合は親の作成日を見て並べる
# トークルームモデル(TalkRoom)
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
# 投稿モデル(Post)
t.integer "talk_room_id"
t.string "content"
t.string "image"
t.datetime "created_at"
t.datetime "updated_at"
実装方法
timeline_controller.rb
def index
@instances = TalkRoom.includes(:posts)
.order("posts.created_at DESC")
.to_a
# 子モデルが存在するかを判定し、ある場合は最新の子モデルのcreated_at、ない場合は親のcreated_atで判定する
@instances.sort! do |a, b|
a_created_at = a.posts.present? ? a.posts.first.created_at : a.created_at
b_created_at = b.posts.present? ? b.posts.first.created_at : b.created_at
b_created_at <=> a_created_at
end
end