Railsでブログアプリに月別アーカイブを導入
作成中のブログアプリケーションに月別アーカイブの機能を実装しました。
こんな感じのやつです。
前提
Rails: 5.2.1
リレーションは User has_one Blog, Blog has_many Articles です。
はじめにメソッドを用意
まずブログ記事の :created_at を元に月別に集計する必要があるので、blog.rbにdivide_monthlyメソッドを作成します。
def divide_monthly return self.articles.group("strftime('%Y%m', articles.created_at)") .order("strftime('%Y%m', articles.created_at) desc") .count end
この状態だと
DEPRECATION WARNING: Dangerous query method (method whose arguments are used as raw SQL) called with non-attribute argument(s): "strftime('%Y%m', articles.created_at) desc". Non-attribute arguments will be disallowed in Rails 6.0. This method should not be called with user-provided values, such as request parameters or model attributes. Known-safe values can be passed by wrapping them in Arel.sql().
という警告が出ます。
詳しくは参考リンクの通りですが、とりあえずArel.sql()
で囲めば大丈夫です。
def divide_monthly return self.articles.group("strftime('%Y%m', articles.created_at)") .order(Arel.sql("strftime('%Y%m', articles.created_at) desc")) .count end
コントローラとルーティング
次に、コントローラーです。blogsコントローラにarchivesアクションを作成しましょう。
そして、blogsコントローラのshowアクションとarchivesアクション(月別アーカイブを表示するアクション)に以下を記述します
@archives = @blog.divide_monthly
archivesアクションを作成したのでルーティングも追加します。
get '/blogs/:id/archives/:yyyymm', to: 'blogs#archives', as: :blog_archive
ヘルパーメソッド作成
そして、viewで「2018年10月(8)」のような表示をするために
application_helper.rbにヘルパーメソッドを作成します。
def ymconv(yyyymm,cnt) yyyy = yyyymm[0,4] mm = yyyymm[4,2] return yyyy + "年" + mm + "月 (" + cnt + ")" end
ビューを編集
blogs/archives.html.erbを作成して、blogs/show.html.erbの内容をコピペします。
そしてサイドバー部分に
<h4 class="font-italic">過去ログ</h4> <ul style="list-style:none;"> <% @archives.each do |yyyymm, count| %> <li><%= ymconv(yyyymm, count.to_s) %></li> <% end %> </ul>
これで、まだリンクにはなりませんが月別の記事数が表示されるようになります。
archivesアクション
さて、先程arichivesアクション用にルーティングを追加しました。
URLは
'/blogs/:id/archives/:yyyymm'
です。つまり
/blogs/2/archives/201809 だったら
IDが2のブログの、2018年9月に作成された記事一覧ページが表示されるようになります。
ブログのサイドバーの表示は先程作ったので、あとはリンクを付けるのと、archivesビューで月別の記事一覧を表示する部分を編集します。
Blogsコントローラー#archivesアクションに追記
def archives @blog = Blog.find(params[:id]) @yyyymm = params[:yyyymm] @articles = @blog.articles.where("strftime('%Y%m', articles.created_at) = '"+@yyyymm+"'").paginate(:page => params[:page], :per_page => 5).order('created_at DESC') @archives = @blog.make_archive end
@articlesには、@blogのブログ記事の作成年月(yyyymm)がURLの年月(@yyyymm)と一致するものが代入されます。
アーカイブ記事の表示部分を作る
月別の記事一覧を表示する部分を作成するため、archives.html.erbを編集します。
ちょっと私のだとごちゃごちゃしてるんですが、要は
<% @articles.each do |article| %> <%= article.title %> <%= article.text %> <% end %>
@articlesに入っているその月の記事たちをeachで回していけばOKです。
リンクに変更する
最後に、show.html.erbとarchives.html.erbを以下のように変更します。
<h4 class="font-italic">過去ログ</h4> <ul style="list-style:none;"> <% @archives.each do |yyyymm, count| %> <li><%= link_to ymconv(yyyymm, count.to_s), blog_archive_path(@blog, yyyymm) %></li> <% end %> </ul>
これで月別アーカイブを実装することが出来ました。
(おまけ)◯月の記事一覧 という表示をする
blogs_helper.rbにヘルパーメソッド作成
def ymconvn(yyyymm) yyyy = yyyymm[0,4] mm = yyyymm[4,2] return yyyy + "年" + mm + "月 " end
archives.html.erb で
<%= ymconvn(@yyyymm) %>の記事一覧
と書けばOKです。
参考リンク
strftime - リファレンス - - Railsドキュメント