目的:データベースから任意のデータを抽出してその合計や割合をグラフで表現する
環境:rails 6
DB :mysql 5.6
完成物
①アンケート結果の割合を円グラフで表示
②毎日の売上を折れ線グラフで表示
直面した課題
・各グラフに挿入するデータの抽出。割合を求める場合はグラフの配列にどのようにアンケート結果を振り分けていくべきか?折れ線グラフの方は特定のユーザーが投稿しているポストの中でオーダー実績があるポストの価格を合計するという指定。
以下手順。
まず、必要なライブラリのインストール
require("chartkick")
require("chart.js")
yarn add chartkick chart.js
でインストール実施。
グラフ自体の作成は極めて簡潔です。公式リファレンスも参考にして頂ければと思います。
https://chartkick.com/
①円グラフの作成
割合を表す円グラフは下のように配列形式で、項目とデータを指定。
@result = [["男性",0],["女性",0],["その他",0]]
HTMLは
<%= pie_chart @result, width: "400px", height:"350px", class:"chart"%>
のように記述します。
今回の問題、データの指定方法として、アンケート(データ)が入力されるたびに上記のような各配列の値を増やしていく繰り返し処理を定義致しました。
result[0][1]という記述で、配列のキーと値を指定、それに対してアンケートの結果(id)によって+1を振り分ける処理の繰り返しを実行。あとは、引数に抽出したいデータが入っているカラムデータを指定してあげればOKです。参考までに私の場合はいかになりました。
@first_id = Evaluation.where(post_id: @post.id).pluck(:first_id)
@aggregate = aggregateOpinion(@first_id)
whereでテーブル内検索の条件を指定、pluckでその中で特定のカラムのデータを全て取得。
②折れ線グラフで売上の推移を表示
こちらでは主にデータの抽出に関して苦労した点を共有致します。完成のHTMLは以下となります。
<%= line_chart @order_posts.group_by_day(:created_at).sum(:price), width: "800px", height: "300px",xmax: @today,label: "Value",xtitle: "日付",ytitle: "販売金額"%>
ざっと説明だけしておくと、group_by_day(:created_at)で日時の指定、sum(:price)で抽出したデータの全てのpriceを合計。
苦戦したデータ抽出
前提として、usersテーブル、postsテーブル、post_ordersテーブルがありそれぞれがアソシエーションが組まれている状態。current_userが投稿したpostの中で、価格が0円の物を除く(今回のアプリの都合上0円で投稿されている物もあり)、その中でpost_ordersテーブルにデータがあるものを全て指定する。
(初学者で変数名の定義がおかしいです。ご容赦下さい)
@user = User.find(params[:user_id]) //ユーザーの指定
@post = @user.posts //ユーザーが投稿した全てのpost
@posts = @post.where.not(price: 0) //価格0円を除く
@order_posts = PostOrder.where(post_id: @posts.ids) //ユーザーが投稿した中で売れたpost
@sales = Post.where(id: @order_posts.post_id) //postsテーブル内のユーザーが投稿して売れているアイテムを指定
雑魚初心者の私はこれでOK、と考えましたがこれではダメです。最終行の@order_postsでpost_idは定義されていませんというエラーとなります。色々なパターンを確認しましたが、このように複数のデータを抽出している場合はアソシエーションが組まれているテーブル同士の要素を組みわせて指定することはできません。
しかし、グラフに表示するにはpostsテーブルのpriceのデータが必須なのでモデルはpostでなくてはなりません。かなり試行錯誤しましたが、雑魚初心者の私には解決できず....。
結局、post_ordersテーブルにpriceカラムを追加することで解決しました。上記のグラフの指定データが@order_postsになっているのはその為です。
今回とは別に各postごとの売上を棒グラフ化するというのも合わせて実装しました。
これも含めて今回の教訓となったのはデータを抽出するソート方法です。
各投稿という指定でいくと、重複はダメなので、distinctというオプションを使うのですが、これはwhereメソッドとは併用できずselectメソッドを使います。私が苦労したように複数データを指定する場合は、変数.子テーブルのカラムという定義ができなかったり、where(正の条件).where.not(否の条件)と合わせて使うことも可能だったり、countメソッドも使える場合と使えない場合があったり。
どうにもならない時は少し視点を変えて、今回のようにカラム自体を追加するという解決方法もアリかと思います。何かお気づきな点ございましたら、何卒ご教授お願い致します。