前回はパスワードの再設定機能を開発しました。
今回は収支データを月だけでなく、カテゴリーや選択項目などで絞り込み検索ができる機能を実装します。
絞り込み検索機能
・絞り込み検索ボタンの設置
・ボタン押下で絞り込みモーダルを表示
・絞り込みモーダルの検索押下で絞り込み検索処理を実行し該当データを表示
・絞り込み中のワードを出力
上記4ステップで進めていきます。
絞り込み検索ボタンの設置
まずは絞り込みモーダルを出すボタンを設置します。
以下を追記します。
<!--以下1行追記-->
<a href="#modal" class="p-banner c-button c-button--bg-blue js-detail-search">絞り込み検索</a>
<div class="pc_only">CSSはすでに設定済みです。

ボタン押下で絞り込みモーダルを表示
続いて絞り込み検索ボタンを押下したら、選択項目やキーワードなどを指定するモーダルを表示するようにします。
このモーダル機能はremodal.jsというプラグインを使って実装します。
上記「Download」からファイルをダウンロードして使用することもできますが、今回はCDNでの読み込みで進めます。
以下のようにCDNでJavaScriptファイルとCSSファイルの読み込み記述を追記します。
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/remodal/1.0.5/remodal.min.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/remodal/1.0.5/remodal-default-theme.min.css">
<!--↑↑以上2行追記↑↑-->
<link rel="stylesheet" href="./css/style.min.css"><script src="./js/tableexport.min.js"></script>
<!--↓↓以下1行追記↓↓-->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/remodal/1.0.5/remodal.min.js"></script>続いてモーダルとして表示する要素を絞り込みボタンの下に追記します。
<form method="POST" class="p-form--detail-search remodal" data-remodal-id="modal">
  <button data-remodal-action="close" class="remodal-close"></button>
  <div class="p-form__flex-input">
    <p>タイトルキーワード</p>
    <input type="text" name="filtering-title" maxlength="15" value=''>
  </div>
  <div class="p-form__flex-input">
    <p>支出カテゴリー</p>
    <select name="filtering-spendingcat" id="">
      <option value="0">選択してください</option>
      <?php
      $sql = 'SELECT id, name FROM spending_category WHERE user_id = ?';
      $stmt = $db->prepare($sql);
      $stmt->bind_param('i', $user_id);
      sql_check($stmt, $db);
      $stmt->bind_result($id, $name);
      while ($stmt->fetch()) : ?>
        <option value='<?php echo h($id); ?>'><?php echo h($name); ?></option>
      <?php endwhile; ?>
    </select>
    <i class="fa-solid fa-angle-down"></i>
  </div>
  <div class="p-form__flex-input">
    <p>収入カテゴリー</p>
    <select name="filtering-incomecat" id="">
      <option value="0">選択してください</option>
      <?php
      $sql = 'SELECT id, name FROM income_category WHERE user_id = ?';
      $stmt = $db->prepare($sql);
      $stmt->bind_param('i', $user_id);
      sql_check($stmt, $db);
      $stmt->bind_result($id, $name);
      while ($stmt->fetch()) : ?>
        <option value='<?php echo h($id); ?>'><?php echo h($name); ?></option>
      <?php endwhile; ?>
    </select>
    <i class="fa-solid fa-angle-down"></i>
  </div>
  <div class="p-form__flex-input">
    <p>支払い方法</p>
    <select name="filtering-paymentmethod" id="">
      <option value="0">選択してください</option>
      <?php
      $sql = 'SELECT id, name FROM payment_method WHERE user_id = 0 OR user_id = ?';
      $stmt = $db->prepare($sql);
      $stmt->bind_param('i', $user_id);
      sql_check($stmt, $db);
      $stmt->bind_result($id, $name);
      while ($stmt->fetch()) : ?>
        <option value='<?php echo h($id); ?>'><?php echo h($name); ?></option>
      <?php endwhile; ?>
    </select>
    <i class="fa-solid fa-angle-down"></i>
  </div>
  <div class="p-form__flex-input">
    <p>クレジットカード</p>
    <select name="filtering-credit" id="">
      <option value="0">選択してください</option>
      <?php
      $sql = 'SELECT id, name FROM creditcard WHERE user_id = ?';
      $stmt = $db->prepare($sql);
      $stmt->bind_param('i', $user_id);
      sql_check($stmt, $db);
      $stmt->bind_result($id, $name);
      while ($stmt->fetch()) : ?>
        <option value='<?php echo h($id); ?>'><?php echo h($name); ?></option>
      <?php endwhile; ?>
    </select>
    <i class="fa-solid fa-angle-down"></i>
  </div>
  <div class="p-form__flex-input">
    <p>スマホ決済</p>
    <select name="filtering-qr" id="">
      <option value="0">選択してください</option>
      <?php
      $sql = 'SELECT id, name FROM qr WHERE user_id = ?';
      $stmt = $db->prepare($sql);
      $stmt->bind_param('i', $user_id);
      sql_check($stmt, $db);
      $stmt->bind_result($id, $name);
      while ($stmt->fetch()) : ?>
        <option value='<?php echo h($id); ?>'><?php echo h($name); ?></option>
      <?php endwhile; ?>
    </select>
    <i class="fa-solid fa-angle-down"></i>
  </div>
  <input type="submit" class="c-button c-button--bg-blue" name="detail-search" value="絞り込み検索" onclick="onRemoveSearchModal();">
  <input type="submit" class="c-button c-button--bg-gray" name="detail-reset" value="絞り込みリセット" onclick="onRemoveSearchModal();">
</form>「絞り込み検索」と「絞り込みリセット」のボタンに設定しているJavaScriptの関数を作成します。
この関数は、ボタン押下でモダールを出す判定をするアンカーリンク「#modal」を削除する関数です。
const onRemoveSearchModal = () => {
  window.history.replaceState(null, "", location.pathname + location.search);
};絞り込みモーダルの検索押下で絞り込み検索処理を実行し該当データを表示
ここから絞り込み検索の処理を記述していきます。
まずは「絞り込み検索」が押されたときに送信されたデータを受け取るプログラムを、ファイル冒頭のセッションやパラメータ等の処理を記述している箇所の一番下に追記します。
//絞り込み検索送信処理
if (isset($_POST['detail-search'])) :
  $filtering_title = filter_input(INPUT_POST, 'filtering-title', FILTER_SANITIZE_SPECIAL_CHARS);
  $filtering_spendingcat = filter_input(INPUT_POST, 'filtering-spendingcat', FILTER_SANITIZE_NUMBER_INT);
  $filtering_incomecat = filter_input(INPUT_POST, 'filtering-incomecat', FILTER_SANITIZE_NUMBER_INT);
  $filtering_paymentmethod = filter_input(INPUT_POST, 'filtering-paymentmethod', FILTER_SANITIZE_NUMBER_INT);
  $filtering_credit = filter_input(INPUT_POST, 'filtering-credit', FILTER_SANITIZE_NUMBER_INT);
  $filtering_qr = filter_input(INPUT_POST, 'filtering-qr', FILTER_SANITIZE_NUMBER_INT);
endif;続いてデータ一覧を抽出するSQLを修正します。絞り込み検索が行われたときはWHERE文を修正するだけなのですが、どんなデータが送られてくるかによってWHEREで絞り込む要素が変わります。
そこで空欄や「選択してください」以外のデータが送信されたときにWHEREに追加するSQLを返す関数を作成します。
関数はタイトルキーワードとそれ以外の選択項目でSQL文が異なるので、2つ作成します。
//絞り込み検索タイトルキーワード
function add_sql_title($value)
{
  if ($value != null) {
    $wildcard_val = '%' . $value . '%';
    return " AND records.title LIKE '{$wildcard_val}'";
  }
}
//絞り込み検索選択項目
function add_sql_item($column, $value)
{
  if ($value !== null && $value !== '0') {
    return " AND {$column} = {$value}";
  }
}引数$valueがnullでない、選択項目の場合は「0」でないときに、タイトルキーワードの場合は「AND records.title LIKE ‘%タイトルキーワード%’」と、選択項目の場合は「AND カラム名 = 値」というWHERE文に追加する文字列を返す関数です。
この関数を使いながらデータ一覧を抽出するSQLを修正します。
まずは $add_where_month 変数部分を以下のように修正します。
//書き換え前
$add_where_month = 'WHERE records.date LIKE ? AND records.user_id=? ORDER BY date DESC, input_time DESC ';
//書き換え後
$add_where_month = 'WHERE records.date LIKE ? AND records.user_id=? ';
$add_order = ' ORDER BY date DESC, input_time DESC ';WHEREとORDER BYを分けて変数に格納しました。
変数を変えたのでSQLをセットする部分も修正します。
※エクセル出力用やスマホ表示用の部分など$add_where_monthを使用している箇所すべてを下記のように修正します
//書き換え前
$stmt_dataoutput = $db->prepare($sql_dataoutput . $add_where_month . $add_limit);
//書き換え後
$stmt_dataoutput = $db->prepare($sql_dataoutput . $add_where_month . $add_order . $add_limit);続いて絞り込み検索で送られてきたデータをもとにWHERE文を修正するプログラムを追記します。
$add_where_month = 'WHERE records.date LIKE ? AND records.user_id=?';
//以下追記
//絞り込み検索時のSQL条件文追加処理
if (isset($_POST['detail-search'])) :
  //タイトルキーワードのWHERE文作成
  $add_sql_title = add_sql_title($filtering_title);
  //上記関数で返ってきたWHERE文を$add_where_sqlに追加
  $add_where_month .= $add_sql_title;
  //選択項目のカラム配列
  $filter_column = ['records.spending_category', 'records.income_category', 'records.payment_method', 'records.creditcard', 'records.qr'];
  //選択項目の値配列
  $filter_value = [$filtering_spendingcat, $filtering_incomecat, $filtering_paymentmethod, $filtering_credit, $filtering_qr];
  //繰り返し構文で上記2つの配列を使用しながら、選択項目のWHERE分を作成し追加する
  for ($i = 0; $i < count($filter_column); $i++) :
   //各選択項目のWHERE文作成
    $add_sql = add_sql_item($filter_column[$i], $filter_value[$i]);
    //上記関数で返ってきたWHERE文を$add_where_sqlに追加
    $add_where_month .= $add_sql;
  endfor;
endif;上記を追記すると、PC表示用とエクセル出力用、スマホ表示の全表示では絞り込み検索表示ができるようになります。
スマホ表示の日付ごとでは、$add_where_date変数を使っているのでここを修正することで、絞り込み検索結果が表示できるようになります。
まずは$add_where_date変数を$add_where_sqlの下に移動させます。
$add_where_month = 'WHERE records.date LIKE ? AND records.user_id = ?';
//↓↓移動させる↓↓
$add_where_date = 'WHERE records.date = ? AND records.user_id = ?';そして関数を実行したあとに$add_where_sqlに追加するところで、$add_where_dateにも追加します。
//絞り込み検索時のSQL条件文追加処理
if (isset($_POST['detail-search'])) :
  //タイトルキーワードのWHERE文作成
  $add_sql_title = add_sql_title($filtering_title);
  //タイトルキーワードのWHERE文を追加
  $add_where_month .= $add_sql_title;
  $add_where_date .= $add_sql_title; //★★★★追加★★★★
  //選択項目のカラム配列
  $filter_column = ['records.spending_category', 'records.income_category', 'records.payment_method', 'records.creditcard', 'records.qr'];
  //選択項目の値配列
  $filter_value = [$filtering_spendingcat, $filtering_incomecat, $filtering_paymentmethod, $filtering_credit, $filtering_qr];
  //繰り返し構文で上記2つの配列を使用しながら、選択項目のWHERE分を作成し追加する
  for ($i = 0; $i < count($filter_column); $i++) :
    $add_sql = add_sql_item($filter_column[$i], $filter_value[$i]);
    $add_where_month .= $add_sql;
    $add_where_date .= $add_sql; //★★★★追加★★★★
  endfor;
endif;上記まで修正すると、日付ごとの表示にした際に絞り込み検索のデータのみがバナーをクリックした際に表示されますが、該当データがない日付のバナーが残っているので、これが表示されないように修正します。
該当データがある日付を抽出しているSQLを修正します。
//書き換え前
$sql = 'SELECT COUNT(*), date FROM records WHERE user_id = ? AND date LIKE ? GROUP BY date ORDER BY date DESC';
//書き換え後
$sql = 'SELECT COUNT(*), date FROM records WHERE user_id = ? AND date LIKE ?';
if (isset($_POST['detail-search'])) :
  $add_sql_title = add_sql_title($filtering_title);
  $sql .= $add_sql_title;
  for ($i = 0; $i < count($filter_column); $i++) :
    $add_sql = add_sql_item($filter_column[$i], $filter_value[$i]);
    $sql .= $add_sql;
  endfor;
endif;
$sql .= ' GROUP BY date ORDER BY date DESC';GROUP BYの前で一度区切り、先程追記したWHERE文追加のプログラムを挟み、最後に切り取ったGROUP BY以降のSQLを追加します。
以上ですべての表示パターンで絞り込み検索ができるようになりました。
現状ではモーダルフォームに絞り込み中のワードが出力されないので、絞り込み中であればタイトルキーワードをinputの中に出力したり、選択項目をselected状態にします。
<!--タイトルキーワードinput valueを修正-->
<input type="text" name="filtering-title" maxlength="15" value='<?php echo h($filtering_title); ?>'>
<!--支出カテゴリーoption-->
<option value='<?php echo h($id); ?>' <?php echo $filtering_spendingcat == $id ? 'selected' : ''; ?>>
<!--収入カテゴリーoption-->
<option value='<?php echo h($id); ?>' <?php echo $filtering_incomecat == $id ? 'selected' : ''; ?>>
<!--支払い方法option-->
<option value='<?php echo h($id); ?>' <?php echo $filtering_paymentmethod == $id ? 'selected' : ''; ?>>
<!--クレジットカードoption-->
<option value='<?php echo h($id); ?>' <?php echo $filtering_credit == $id ? 'selected' : ''; ?>>
<!--スマホ決済option-->
<option value='<?php echo h($id); ?>' <?php echo $filtering_qr == $id ? 'selected' : ''; ?>>以上で絞り込み検索をした状態でモーダルを開くと、現在どの条件で絞り込みをしているかが出力されます。
絞り込み中のワードを出力
最後に絞り込み検索が行われた際に、今どの条件で絞り込み中なのかを出力する要素を実装します。
絞り込みモーダルフォームの下に以下を追記します。
<?php if (isset($_POST['detail-search'])) : ?>
  <div class="p-search-word">
    <p>絞り込み中:
      <!-- タイトルキーワードが空欄でなければ送信された値を出力 -->
      <?php if ($filtering_title != null) : ?>
        <span><?php echo h($filtering_title); ?></span>
      <?php endif; ?>
      <?php
      //以下1行移動させる
      $filter_value = [$filtering_spendingcat, $filtering_incomecat, $filtering_paymentmethod, $filtering_credit, $filtering_qr];
      //選択項目テーブルリスト配列
      $table_list = ['spending_category', 'income_category', 'payment_method', 'creditcard', 'qr'];
      ?>
      <?php
      for ($i = 0; $i < count($filter_value); $i++) :
        //各選択項目の値が0(選択してください)以外なら以下を実行
        if ($filter_value[$i] !== '0') :
          $sql = "SELECT name FROM {$table_list[$i]} WHERE id=?";
          $stmt = $db->prepare($sql);
          $stmt->bind_param('i', $filter_value[$i]);
          $count = sql_check($stmt, $db);
          $stmt->bind_result($name);
          $stmt->fetch();
          echo '<span>' . h($name) . '</span>';
          $stmt->close();
        endif;
      endfor;
      ?>
    </p>
  </div>
<?php endif; ?>まずif文で要素を囲み、絞り込み検索がされたときのみ表示するようにします。
そしてタイトルキーワードが空欄でないときは、送信された値を表示します。
選択項目の出力はif文で「1」以上の値が渡ってきたとき、各テーブル内の一致するidの選択項目名を出力します。
選択項目の出力方法はすべて同じ方法なので、配列を作成しfor文で回しています。
$filter_value配列は、先程絞り込み処理を記述した際に作成したものを移動させました。
上記を追記すると、絞り込み検索をした際に条件ワードが出力されるようになります。

最後に
今回は月ごとの検索しかできなかったデータ一覧を、カテゴリーや選択項目などで絞り込み検索ができる機能を実装しました。
次回はヘッダーメニューをハンバーガーメニューへ移行し、支出と収入の月別金額推移グラフページを実装する予定です。
最後までお読みいただきありがとうございました。





コメント