📆

【Rails】指定した日の記録を表示(非同期通信化)

2023/10/13に公開

本の投稿サイトに、指定した日の記録を表示させます!

前回の続きです。
https://zenn.dev/ganmo3/articles/39dea581126646

実装要件

  1. ユーザ詳細ページに、指定した日の記録(投稿数)を非同期で表示させる

完成イメージ

ルーティング追加

routes.rb
resources :users, only: [:index,:show,:edit,:update] do
+ get "posts_on_date" => "users#posts_on_date"
end

ユーザー詳細ページ内で指定した日の記録を表示させるためのパスを作ってあげます。
このルートを作ることで、UsersControllerposts_on_dateアクションを呼び出すことができるようになります。

コントローラ記述

users_controller.rb
def posts_on_date
  # リクエストの中からユーザーIDを取得し、データベースからユーザー情報を取得
  user = User.includes(:books).find(params[:user_id])

  # リクエストの中から指定された日付を取得し、日付オブジェクトに変換
  date = Date.parse(params[:created_at])

  # ユーザーが指定した日に投稿した本(books)をデータベースから検索
  @books = user.books.where(created_at: date.all_day)

  # 検索結果を表示するためのビュー(posts_on_date_form)にデータを送る
  render :posts_on_date_form
end

解説:

  1. user = User.includes(:books).find(params[:user_id])
    上記は次のように書くことも可能だが、N+1問題を回避するためにincludesメソッドを使いました。
    user = User.find(params[:user_id])
    
    ここでのN+1問題

    N+1問題を回避しない場合:

    1. ユーザー1を取得
    2. ユーザー1の書籍を取得

    ユーザー1の情報を取得するために1回、ユーザー1の書籍情報を取得するためにもう1回の計2回のデータベースのクエリ(データベースから情報を取得するための命令)が必要です。

    N+1問題を回避する場合(includes使用):

    1. 一度にユーザー1とその書籍情報を取得

    1つのデータベースクエリでユーザー1とその書籍情報をまとめて取得します。データベースへのアクセスが1回のみで済むため、効率が向上します。

以下も参照くださいね♪
https://zenn.dev/ganmo3/articles/a4b2204088d7de

  1. date = Date.parse(params[:created_at])
    params[:created_at]の値は文字列なので、日付として使用することができません。そのため、Date.parseを使って文字列を日付型に変換してあげます。
    このコードを書かずに、まとめて以下のよう書くこともできます。

    @books = user.books.where(created_at: params[:created_at].to_date.all_day)
    
  2. @books = user.books.where(created_at: date.all_day)
    where(created_at: date.all_day)はフィルタリング条件で、特定の日付でbooksを絞り込むためのものです。dateは前述のコードで日付型に変換したもの。date.all_dayは、指定した日の開始から終わりまでの期間のこと。


ビュー作成

部分テンプレート作成

views/users/_posts_on_date_form.html.erb
<!-- 検索フォーム部分 -->
<div class="row my-3">
  <div class="col-auto">
    <!-- フォームを生成 -->
    <%= form_with url: user_posts_on_date_path(user), method: :get, local: false do |f| %>
      <!-- 入力フィールドと送信ボタンをグループ化 -->
      <div class="input-group">
        <!-- 日付を選択する入力フィールド -->
        <%= f.date_field :created_at, class: "form-control" %>
        <!-- 送信ボタンを表示 -->
        <div class="input-group-append">
          <%= f.submit "検索", class: "btn btn-info" %>
        </div>
      </div>
    <% end %>
  </div>
</div>

<!-- 検索結果を表示するためのコンテナ -->
<div class="posts_on_date_table mb-2">
</div>

<div class="posts_on_date_table">で非同期処理で検索結果を表示するための箱を作ってあげます。

非同期のためのjsファイル作成

上記で作成した箱の中身を作ります。

views/users/posts_on_date_form.js
$('.posts_on_date_table').html(`
  <h3>検索結果</h3>
  <div class="card" >
    <div class="card-header">投稿数</div>
    <div class="card-body">
      <%= j( @books.count ) %>
    </div>
  </div>
`)

解説:

  1. $('.posts_on_date_table').html(...)
    '.posts_on_date_table'という箱を指定します。これで箱の中身を変えることができます。
  2. <%= j( @books.count ) %>
    サーバーからのデータを持ってきて表示させます。jメソッドを使うことでJavaScript内でデータを安全に表示できる形式に変換しています。

部分テンプレート呼び出し

適当な場所で呼び出してあげて完成!

views/users/show.html.erb
<%= render "posts_on_date_form", user: @user %>

Discussion