WordPress – flamingoに保存されたContact Form 7のデータをHighchartsでグラフ化する(デモあり)

Highchartsグラフ WordPress
この記事は約32分で読めます。
スポンサーリンク

WordPressのプラグイン flamingo は、メールフォームプラグイン Contact Form 7 の送信データをデータベース上に保存するプラグインです。
このデータをグラフ化する案件があったので覚書です。とある装置の日々の測定データの報告をメールフォームで送ってもらい、それをグラフ化するといった感じです。
グラフ化には Highcharts.js を使いました。つまり、flamingo に保存されたデータから Highcharts.js でグラフ化する方法です。

Highchartsグラフ

スポンサーリンク

Contact Form 7 でフォームを作る

Contact Form 7

flamingo にデータを保存するので、Contact Form 7 でフォームを作ります。今回は下記のような感じです。

<dl class="report">
<dt>担当者</dt>
<dd>[select* your-name first_as_label "選択してください" "佐藤" "鈴木" "高橋" "田中"]</dd>
<dt>装置1</dt>
<dd class="implement-ck"><span>測定:</span>[radio measuring-1 use_label_element default:2 "実施" "未実施"]</dd>
</dl>
<div class="measuring-1 hide">
<dl class="report">
<dd class="measuring-time"><span>測定時刻:</span>[select hour-1 include_blank "00" "01" "02" "03" "04" "05" "06" "07" "08" "09" "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" "20" "21" "22" "23"]:[select min-1 include_blank "00" "10" "20" "30" "40" "50"]</dd>
<dd class="measuring-value"><span>測定値:</span>[number value-1 min:3000 max:20000 placeholder "99999"]mg/L</dd>
</dl>
</div>
<dl class="report">
<dt>装置2</dt>
<dd class="implement-ck"><span>測定:</span>[radio measuring-2 use_label_element default:2 "実施" "未実施"]</dd>
</dl>
<div class="measuring-2 hide">
<dl class="report">
<dd class="measuring-time"><span>測定時刻:</span>[select hour-2 include_blank "00" "01" "02" "03" "04" "05" "06" "07" "08" "09" "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" "20" "21" "22" "23"]:[select min-2 include_blank "00" "10" "20" "30" "40" "50"]</dd>
<dd class="measuring-value"><span>測定値:</span>[number value-2 min:0 max:20 placeholder "6.5"]pH</dd>
</dl>
</div>
<dl class="report">
<dd class="cleaning"><span>清掃:</span>[radio cleaning use_label_element default:2 "実施" "未実施"]</dd>
</dl>
<p class="submit">[submit class:fas "&#xf0e0; 送 信"]</p>

ラジオボタンで関連箇所の表示/非表示(本題と関係ありません)

ラジオボタンの選択が“未実施”の場合に入力不要なinput要素を含む要素を非表示にしましたが、本題とは関係ありません。

jQuery
(function($){
  $('.implement-ck input[type="radio"]').change(function(){
    var c = $(this).attr("name");
    $('div.' + c).removeClass('hide');
    if($('.implement-ck input[name="' + c + '"]:checked').val() == "未実施"){
      $('div.' + c).addClass('hide');
    }
  });
})(jQuery);

グラフを入れる要素と日付を遡るためのform

id=”test-graph”のdiv要素にグラフが表示されます。
日付を遡るためのformは、Contact Form 7 とは別のformです。

HTML
<!-- ↓グラフの日付を遡るためのフォーム↓ -->
<form action="" method="POST">
<span class="offset">最新より<input type="number" name="offset" class="offset" min="0" max="366" aria-invalid="false" placeholder="0" />日前</span><span><input type="submit" name="submit" value="送 信" /></span>
</form>
<!-- ↓グラフを表示するコンテナ↓ -->
<div id="test-graph" style="height: 400px; margin: 0 auto;"></div>

フォームの外観を整えるCSS

CSS
/*### Contact Form 7 のフォーム用 ###*/
dl.report dt,
dl.report dd {
margin-left: 0;
}
dl.report dd {
margin-bottom: .5em;
}
.measuring-time span:nth-of-type(1),
.measuring-value span:nth-of-type(1) {
width: 8em;
display: inline-block;
}
.cleaning > span:nth-of-type(1) {
width: 7em;
display: inline-block;
}
.measuring-time span:nth-of-type(2) {
margin-right: .5em;
}
.measuring-time span:nth-of-type(3) {
margin-left: .5em;
}
.measuring-time input {
width: 3.5em;
}
.measuring-time select {
width: 4.0em;
}
.measuring-value input {
width: 6em;
}
.measuring-1.hide,
.measuring-2.hide { /*ラジオボタンによる非表示スクリプト用*/
display: none;
}
/*### グラフ上部フォーム用 ###*/
.entry-content form {
margin-bottom: 40px;
}
.entry-content form span {
display: inline-block;
}
.entry-content form span.offset {
width: 13em;
margin-left: 1em;
margin-bottom: 10px;
}
.entry-content form input.offset {
width: 5em;
}
.entry-content form input[name="submit"] {
width: 5em;
}

テンプレートファイルにコード追加

今回は固定ページ用のテンプレートファイル page.php をコピーしてグラフ用のテンプレートファイル page-graph.php を作成し、その最上部(header要素より上)に下記のコードを追加しました。
flamingo のデータは post_type が flamingo_inbound のカスタムフィールドなので、get_post_meta() で取得可能です。それをJSONエンコードしてJavaScriptで使用できる形式に変換します。

PHP
// フォームから送信されたinputの値を$offsetに格納
if(isset($_POST["offset"])) {
  $offset = $_POST["offset"];
} else {
  $offset = 0;
}
//投稿データ作成のためのパラメータ
$args = array(
  'post_type' => 'flamingo_inbound', // flamingoのデータはpost_typeがflamingo_inbound
  'posts_per_page'   => 10, // 10件取得
  'orderby' => array(
                     'date' => 'DESC' // 最新から取得したいので降順
               ),
  'offset' => $offset // 取得開始位置(日付を遡るため)
);
$flamingo_posts = get_posts( $args ); // 投稿データの配列作成
// 空の配列をそれぞれ作成
$report_date_arr = array();
$report_time_arr = array();
$your_name_arr = array();
$measuring_1_arr = array();
$measuring_2_arr = array();
$hour_1_arr = array();
$min_1_arr = array();
$hour_2_arr = array();
$min_2_arr = array();
$value_1_arr = array();
$value_2_arr = array();
$cleaning_arr = array();
// 配列を繰り返し処理
foreach( $flamingo_posts as $value ) { // 要素の値を$valueに代入
  $post_id = $value->ID; // $valueからIDを取得
  $flamingo_post = get_post( $post_id ); // IDから投稿レコードを取得
  $report_date = $flamingo_post->post_date; // 投稿レコードから送信日時を取得
  $report_time = mysql2date('H:i:s', $flamingo_post->post_date); // tooltip表示用書式(時:分:秒)
  // IDからカスタムフィールドの値を取得
  $your_name = get_post_meta( $post_id, '_field_your-name', true ); // 担当者取得
  $measuring_1 = get_post_meta( $post_id, '_field_measuring-1', true); // 装置1測定実施/未実施取得
  $measuring_2 = get_post_meta( $post_id, '_field_measuring-2', true); // 装置2測定実施/未実施取得
  $hour_1 = get_post_meta( $post_id, '_field_hour-1', true ); // 装置1測定時刻(時)取得
  $min_1 = get_post_meta( $post_id, '_field_min-1', true ); // 装置1測定時刻(分)取得
  $hour_2 = get_post_meta( $post_id, '_field_hour-2', true ); // 装置2測定時刻(時)取得
  $min_2 = get_post_meta( $post_id, '_field_min-2', true ); // 装置2測定時刻(分)取得
  $value_1 = get_post_meta( $post_id, '_field_value-1', true ); // 装置1測定値取得
  $value_2 = get_post_meta( $post_id, '_field_value-2', true ); // 装置2測定値取得
  $cleaning = get_post_meta( $post_id, '_field_cleaning', true ); // 清掃実施/未実施
  // 取得したカスタムフィールドの値を空の配列に入れていく
  $report_date_arr[] = $report_date;
  $report_time_arr[] = $report_time;
  $your_name_arr[] = $your_name;
  $measuring_1_arr[] = $measuring_1;
  $measuring_2_arr[] = $measuring_2;
  $hour_1_arr[] = $hour_1;
  $min_1_arr[] = $min_1;
  $hour_2_arr[] = $hour_2;
  $min_2_arr[] = $min_2;
  $value_1_arr[] = (int)$value_1;
  $value_2_arr[] = (float)$value_2;
  $cleaning_arr[] = $cleaning;
}
// グラフのX軸は右方向に最新となるので配列を逆順にする
$report_date_arr_r = array_reverse($report_date_arr);
$report_time_arr_r = array_reverse($report_time_arr);
$your_name_arr_r = array_reverse($your_name_arr);
$measuring_1_arr_r = array_reverse($measuring_1_arr);
$measuring_2_arr_r = array_reverse($measuring_2_arr);
$hour_1_arr_r = array_reverse($hour_1_arr);
$min_1_arr_r = array_reverse($min_1_arr);
$hour_2_arr_r = array_reverse($hour_2_arr);
$min_2_arr_r = array_reverse($min_2_arr);
$value_1_arr_r = array_reverse($value_1_arr);
$value_2_arr_r = array_reverse($value_2_arr);
$cleaning_arr_r = array_reverse($cleaning_arr);
// JavaScriptにデータを渡すためにJSONエンコード
$report_date_j = json_encode($report_date_arr_r);
$report_time_j = json_encode($report_time_arr_r);
$your_name_j = json_encode($your_name_arr_r);
$measuring_1_j = json_encode($measuring_1_arr_r);
$measuring_2_j = json_encode($measuring_2_arr_r);
$hour_1_j = json_encode($hour_1_arr_r);
$min_1_j = json_encode($min_1_arr_r);
$hour_2_j = json_encode($hour_2_arr_r);
$min_2_j = json_encode($min_2_arr_r);
$value_1_j = json_encode($value_1_arr_r);
$value_2_j = json_encode($value_2_arr_r);
$cleaning_j = json_encode($cleaning_arr_r);

Highcharts.js のグラフ設定

テンプレートファイル page-graph.php の body要素最下部(タグ直前)に記述します。highcharts.js と exporting.js を読み込んでその下にスクリプトを記述します。

jQuery
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script type="text/javascript">
  $(document).ready(function () {
    // PHPから値を受ける
    let offset = '<?php echo $offset; ?>';
    // JSON形式の文字列をJavaScriptのJSONオブジェクトに変換
    let report_date_j = JSON.parse('<?php echo $report_date_j; ?>');
    let report_time_j = JSON.parse('<?php echo $report_time_j; ?>');
    let your_name_j = JSON.parse('<?php echo $your_name_j; ?>');
    let measuring_1_j = JSON.parse('<?php echo $measuring_1_j; ?>');
    let measuring_2_j = JSON.parse('<?php echo $measuring_2_j; ?>');
    let hour_1_j = JSON.parse('<?php echo $hour_1_j; ?>');
    let min_1_j = JSON.parse('<?php echo $min_1_j; ?>');
    let hour_2_j = JSON.parse('<?php echo $hour_2_j; ?>');
    let min_2_j = JSON.parse('<?php echo $min_2_j; ?>');
    let value_1_j = JSON.parse('<?php echo $value_1_j; ?>');
    let value_2_j = JSON.parse('<?php echo $value_2_j; ?>');
    let cleaning_j = JSON.parse('<?php echo $cleaning_j; ?>');

    // POST後にinputのvalueを残す
    $('input.offset').val(offset);
    // highcharts.jsの設定
    chart = new Highcharts.Chart({
      chart: { // グラフの設定
      renderTo: 'test-graph', // グラフを表示する要素のid
      defaultSeriesType: 'line', // データ系列の既定タイプ
      marginRight: 140, // 右マージン
      marginBottom: 70 // 下マージン
      },
      title: {
        text: 'テストグラフ' // グラフのタイトル
      },
      subtitle: {
      },
      xAxis: { // X軸
        categories: report_date_j, // X軸の項目(送信日時)
        labels: {
          formatter: function(){
            return this.value.toString().substring(0,10); // グラフX軸表示用書式(最初の文字から10文字分の日付のみ)
          }
        }
      },
      yAxis: [{ // 1番目のY軸(既定の左)
        title: {
          text: '(mg/L)' // 1番目のY軸のタイトル
        }
      }, { // 2番目のY軸
        title: {
          text: '(pH)' // 2番目のY軸のタイトル
        },
        opposite: true // 右側に設定
      }],
      plotOptions: { // データ系列のオプション
        line: { // 線分タイプの設定
          dataLabels: {
            enabled: true,
            formatter: function() { // 値により色を変える
              if (this.y > 8.0 || (this.y < 6.0 && this.y > 0)){ // 0を非表示
                var color = '<span style="color:#c00">' + this.y + '</span>';
              } else if (this.y <= 8.0 && this.y >= 6.0){
                var color = '<span style="color:#00c">' + this.y + '</span>';
              }
              return color;
            }
          },
        },
        area: { // エリアタイプの設定
          fillOpacity: 0.2,
          marker: {
            enabled: false
          },
          lineWidth: 0,
          dataLabels: {
            enabled: true,
            formatter: function() { // 値により色を変える
              if (this.y > 6000 ){
                var color = '<span style="color:#c00">' + this.y + '</span>';
              } else if (this.y <= 6000 && this.y > 0){ // 0を非表示
                var color = '<span style="color:#00c">' + this.y + '</span>';
              }
              return color;
            }
          },
        },
      },
      tooltip: {
        formatter: function(){
          var index = report_date_j.indexOf(this.x); // 送信日時からX軸の項目を検索してその位置を返す
          var man = your_name_j[index]; // index位置の担当者
          var clean = cleaning_j[index]; // index位置の清掃実施/未実施
          var report_time = report_time_j[index]; // index位置の送信時刻
          if(this.series.name == '装置1'){ //装置1のデータ系列において
            var hour = hour_1_j[index]; // index位置の測定時刻(時)
            var min = min_1_j[index]; // index位置の測定時刻(分)
            var implement = measuring_1_j[index]; // index位置の測定実施/未実施
          } else if(this.series.name == '装置2'){ //装置2のデータ系列において
            var hour = hour_2_j[index]; // index位置の測定時刻(時)
            var min = min_2_j[index]; // index位置の測定時刻(分)
            var implement = measuring_2_j[index]; // index位置の測定実施/未実施
          }
          if(this.series.name == '装置1' && implement == '実施'){ // 装置1が測定実施の場合
            var meas = '<br />' + this.y + 'mg/L<br />' + hour + ':' + min + '測定<br />';
          } else if(this.series.name == '装置2' && implement == '実施'){ // 装置2が測定実施の場合
            var meas = '<br />pH' + this.y + '<br />' + hour + ':' + min + '測定<br />';
          } else { // 装置1装置2ともに未実施の場合
            var meas = '<br />';
          }
          var word = this.series.name + '<br />測定' + implement + meas + '清掃' + clean + '<br />' + '担当:' + man + 'さん' + '<br />' + report_time + '送信'; // tooltip表示内容
          return word; // 値を返す
        }
      },
      legend: { // 凡例の設定
        layout: 'vertical', // 各凡例を垂直方向に配置
        align: 'right', // 右に配置
        verticalAlign: 'middle' // 垂直方向中央配置
      },
      series: [{ // データ系列1の設定
        name: '装置1', // 名前
        type: 'area', // エリアタイプ
        data: value_1_j // データ系列
      }, {
        name: '装置2', // 名前
        yAxis: 1, // 2番目のY軸を使う
        data: value_2_j // データ系列
      }]
    });
  });
</script>

DEMOはこちら

以上、ご参考になれば幸いです。
ありがとうございます。

コメント

タイトルとURLをコピーしました