【Rails】日付・期間を指定した投稿をトップページに自動的に表示する

個人開発のWebアプリまちかどルートをもうすぐv5.0正式版にバージョンアップします。

そのv5.0正式版にむけて現在v5.0rc1でテスト中なのが、投稿にあらかじめ日付・期間を指定しておき、その日付・期間が来たらトップページに自動的に表示される機能です。

プログラミングに入門して7ヶ月弱。初めての挑戦だったのでメモとして残します。

データベース

まず、期間の開始日start_onと終了日end_onというカラムをPostsテーブルに作成します。

$ rails g migration AddStartToPosts start_on:date  
$ rails g migration AddEndToPosts end_on:date  
$ rails db:migrate  

いずれのカラムもdate型にしておきます。

入力フォーム

期間の開始日  
<%= date_field_tag 'post[start_on]', nil %>  
期間の終了日  
<%= date_field_tag 'post[end_on]', nil %>  

date_field_tagを使うと下の写真のようにカレンダー形式で日付を指定できるので便利です。

controller

バリデーション

開始と終了の日付が正しく指定されたかどうかチェックするバリデーションはmodelではなくcontrollerで行っています。

def create  
(中略)  

sabun = (@post.start_on - Date.today).to_i  
unless sabun >= 1  
  flash[:error] = "開始日は明日以降で!"  
  render 'new'  
  return  
end  

sabun = (@post.end_on - @post.start_on).to_i  
unless sabun <= 10  
  flash[:error] = "期間は最長10日間まで!"  
  render 'new'  
  return  
end  
unless sabun >= 0  
  flash[:error] = "終了日は開始日以降で!"  
  render 'new'  
  return  
end  

(中略)  
end  

開始日は明日以降とするため、上記のようにして今日Date.todayとの日付の差分sabunを1日以上にするようバリデーションしています。

ほかにも、指定期間を最長10日間とするため差分を10日以下としたり、終了日は開始日以降とするために差分を0以上となっています。

指定期間にマッチする投稿をDBから検索

def index  
(中略)  

special_post_ids = Post.where.not(start_on: nil).pluck(:id)  
@special_posts = Post.where(id: special_post_ids).where('start_on <= ?', Date.today).where('end_on >= ?', Date.today)  

(中略)  
end  

トップページのviewであるindexに投稿のデータをわたすため、controllerで上記の処理を行います。

まず、すべての投稿の中から期間が指定されている投稿のみidを抽出してspecial_post_idsに格納します。

続いて、そのidをもとに開始日startと終了日endの期間内にマッチする投稿データを検索して@special_postsに格納する、という流れです。

とくにwhere('start_on <= ?', Date.today)という検索方法を使ったことがなかったので苦労しました。Date.todayという書式を使うとそこに今日の日付が自動的に代入されてstart_onend_onとの日付を比較しながらDBから探してくれるんですね。とても便利です。

トップページ

<%= render partial: 'special_posts', collection: @special_posts, as: :post %>  

トップページのviewであるindex.html.erbには上記のコードを書きました。指定期間がマッチする投稿データ@special_postsを、部分テンプレートspecial_postsに表示するようにしています。

ちなみに@special_postsに格納されたデータをすべて繰り返しレンダリングするうえでcollectionを使っています。each doよりもN+1問題に対応できるそうです。

タイトル  
  <%= post.title %>  
本文  
  <%= post.body %>  
期間開始日  
  <%= post.start_on %>  
期間終了日  
  <%= post.end_on %>  

あとがき

以上が大筋の流れとなります。こう書いてしまうと簡単に思えますけど、じぶんにとっては難しかったです。まだまだ未熟ですね。これからも必要に応じて学んでいきます。