開発情報・ナレッジ

投稿者: SPIRERS ナレッジ向上チーム 2023年8月21日 (月)

PHP/APIで一覧表を作るサンプルプログラム

レコードリスト機能を使うことで、ノンプログラミングで一覧表を作成することができますが、
標準機能以上のフィルタをかけたい場合や標準の検索ブロックで使用できないフィールドタイプを指定したいなどの場合は、 PHP/APIを作ってより高度な一覧を作成することもできます。
この記事では、API/PHPを作ってレコードリストを実装するサンプルプログラムを方法をご紹介いたします。

変更・改定履歴

  • 改定

    $format の行数が誤っていたため、修正

注意点

・レコードリストに使用できないフィールドがある
「ファイルフィールド」「パスワード」「参照先の情報」などは、今回のサンプルプログラムでは対応していません。
・ソートは実装していない
API で ソートを指定することは可能なので、必要な場合は独自で実装をお願いします。
また、サンプルプログラムでは、標準的な検索のみの対応となります。
高度な検索を行いたい場合は、独自で実装をお願いします。
・レコードアイテムに遷移させる場合、閲覧制限を独自で入れる
API/PHPで実装したレコードリスト、レコード公開範囲とアンマッチになっている可能性があります。
レコード公開範囲以上のフィルタ等を実装でかけている場合は、レコードアイテムに不正アクセスできないように、
レコードアイテムで閲覧制限を独自で入れてください。

PHP(API)サンプルプログラム

検索・レコードリストをAPIで取得するサンプルプログラムとなります。
設定値などは、各環境に合わせて変更してください。
サンプルプログラムなので、動作確認は各自お願いいたします。

PHP
<?php
//------------------------------
// API 設定値
//------------------------------
define("API_URL", "https://api.spiral-platform.com/v1");
define("API_KEY", $SPIRAL->getEnvValue("API_KEY"));
define("APP_ROLE", "");
define("APP_ID", "");
define("DB_ID", "");

//------------------------------
// レコードリスト
//------------------------------
// ソート
define("SORT", array('id'=>'_id','sort'=>'asc')); //ソートする対象:_id、フィールド識別名、_createdAt、_updatedAt。"asc"は昇順、"desc"は降順でソート
// 表示するレコード数
define("PAGE_LIMIT", 20); // 最大200件
// レコードリストに表示させるフィールド
$listField = array(
    array('id' => '_id','label' => 'ID'),
    array('id' => 'text','label' => 'テキスト'),
    array('id' => 'textArea','label' => 'テキストエリア'),
    array('id' => 'mail','label' => 'メールアドレス'),
    array('id' => 'select','label' => 'セレクト'),
    array('id' => 'multi','label' => 'マルチセレクト'),
    array('id' => 'integer','label' => '整数'),
    array('id' => 'number','label' => '数値'),
    array('id' => 'phone','label' => '電話番号'),
    array('id' => 'dateTime','label' => '日時'),
    array('id' => 'date','label' => '日付'),
    array('id' => 'days','label' => '月日'),
    array('id' => 'time','label' => '時刻'),
    array('id' => '_createdAt','label' => '登録日時'),
    array('id' => '_updatedAt','label' => '更新日時'),
);
// レコードアイテムへリンクするフィールド設定
$linkField = array(
    'id' => '_id',//
    'url' => '/api_recordList/detail' //相対パスを記載
);
$format = array(
    'phone' => 'tel',
    'dateTime' => 'date-1',
    'date' => 'date-4',
    '_createdAt' => 'date-2',
    '_updatedAt' => 'date-4',
    'integer' => 'num', //数値をカンマ区切りに対応
    'number' => 'num', //数値をカンマ区切りに対応
);

define("PHONE_NUMBER_COUNTRY", "ja");

// 初期条件フィルタ(例)
// テキストフィールドにサンプルが含まれるまたは、セレクトに値が入っている
// $where = "where=".urlencode('(@text LIKE \'%テスト%\' OR @select IS NULL)');

// 検索に表示させるフィールド
$searchField = array(
    array('id' => '_id','label' => 'ID','operator' => '=', "fieldType" => 'integer'),
    array('id' => 'text','label' => 'テキスト','operator' => 'LIKE',"fieldType" => 'text'),
    array('id' => 'textArea','label' => 'テキストエリア','operator' => 'LIKE',"fieldType" => 'text'),
    array('id' => 'mail','label' => 'メールアドレス','operator' => '=','fieldType' => 'email'),
    array('id' => 'select','label' => 'セレクト',"view" => "multi",'fieldType' => 'select',
        'list' => array('1'=>'ラベル1','2'=>'ラベル2','3'=>'ラベル3',),'operator' => 'ANYCONTAINS'
    ),
    array('id' => 'multi','label' => 'マルチセレクト',"view" => "multi",'fieldType' => 'multi',
        'list' => array('1'=>'ラベル1','2'=>'ラベル2','3'=>'ラベル3',),'operator' => 'ANYCONTAINS'
    ),
    array('id' => 'integer','label' => '整数','operator' => '=','fieldType' => 'integer'),
    array('id' => 'number','label' => '数値','operator' => '=','fieldType' => 'integer'),
    array('id' => 'phone','label' => '電話番号','operator' => '=','fieldType' => 'phone'),
    array('id' => 'dateTime','label' => '日時','operator' => '< <= > >=','fieldType' => 'dateTime'),
    array('id' => 'date','label' => '日付','operator' => '< <= > >=','fieldType' => 'date'),
    array('id' => 'days','label' => '月日','operator' => '=','fieldType' => 'monthDay'),
    array('id' => 'time','label' => '時刻','operator' => '=','fieldType' => 'time'),
    array('id' => '_createdAt','label' => '登録日時','operator' => '=','fieldType' => 'dateTime'),
    array('id' => '_updatedAt','label' => '更新日時','operator' => '=','fieldType' => 'dateTime'),
);


//------------------------------
// タイムゾーン/フォーマット
//------------------------------

// タイムゾーン
define("DATE_TIME_ZONE", 'Asia/Tokyo');
// 日付のフォーマット
define("DATE_FORMAT",array(
    "date-1" => "Y/m/d H:i:s",
    "date-2" => "Y年m月d日 H時i分s秒",
    "date-3" => "Y-m-d H:i:s",
    "date-4" => "Y/m/d",
    "date-5" => "Y年m月d日",
    "date-6" => "Y-m-d",
));

//------------------------------
// 検索パラーメータ作成
//------------------------------
$commonBase = CommonBase::getInstance();
if(!isset($where)){
    $where = null;
}
$searchFieldValue = array();
$parm = '';
if($SPIRAL->getParam("action") == "search"){
    if(!preg_match('/^where=/',$where)){
        $where = "where=";
    }else{
        $where = $where.urlencode(' AND ');
    }
    foreach($searchField as $key){
        $searchFieldValue[$key['id']] = $SPIRAL->getQueryParam($key['id']);
        if($SPIRAL->getParam($key['id']) || $SPIRAL->getQueryParam($key['id'].':after') || $SPIRAL->getQueryParam($key['id'].':before')){
            // 数値
            if($key['fieldType'] == 'integer'){
                $where = $where.urlencode("@".$key['id']." ".$key['operator']." ".str_replace(',', '',$SPIRAL->getParam($key['id']))." AND ");    
            }// 電話番号
            if($key['fieldType'] == 'phone'){
                $where = $where.urlencode("@".$key['id']." ".$key['operator']." '".$commonBase->domesticPhoneNum($SPIRAL->getParam($key['id']))."' AND ");    
            }
            // テキスト
            if($key['fieldType'] == 'text'){
                $where = $where.urlencode("@".$key['id']." ".$key['operator']." '%".$SPIRAL->getParam($key['id'])."%' AND ");    
            }
            // セレクト
            if($key['fieldType'] == 'select'){
                if($key['view'] == 'multi'){
                    $searchFieldValue[$key['id']] = $SPIRAL->getQueryParams($key['id']);
                    $list = implode(',', $SPIRAL->getQueryParams($key['id']));
                }else{
                    $list = $SPIRAL->getQueryParam($key['id']);
                }
                $where = $where.urlencode("@".$key['id']." ".$key['operator']."(".$list.") AND ");
            }
            // マルチセレクト
            if($key['fieldType'] == 'multi'){
                if($key['view'] == 'multi'){
                    $searchFieldValue[$key['id']] = $SPIRAL->getQueryParams($key['id']);
                    $list = implode(',', $SPIRAL->getQueryParams($key['id']));
                }else{
                    $list = $SPIRAL->getQueryParam($key['id']);
                }
                $where = $where.urlencode("@".$key['id']." ".$key['operator']."(".$list.") AND ");
            }
            // 日時 / 日付 / 月日 / 日時
            if($key['fieldType'] == 'dateTime' || $key['fieldType'] == 'date'|| $key['fieldType'] == 'time'|| $key['fieldType'] == 'monthDay'){
                $searchFieldValue[$key['id'].':after'] = $SPIRAL->getQueryParam($key['id'].':after');
                $searchFieldValue[$key['id'].':before'] = $SPIRAL->getQueryParam($key['id'].':before');
                if($searchFieldValue[$key['id'].':after']){
                    $where = $where.urlencode("@".$key['id']." >= '".$SPIRAL->getParam($key['id'].':after')."' AND ");    
                }
                if($searchFieldValue[$key['id'].':before']){
                    $where = $where.urlencode("@".$key['id']." <= '".$SPIRAL->getParam($key['id'].':before')."' AND ");    
                }  
            }
        }
    }
    foreach($searchFieldValue as $key => $val){
        if($val){
            if(is_array($val)){
                foreach($val as $val2){
                    $parm .= '&'.$key.'='.$val2;
                }
            }else{
                $parm .= '&'.$key.'='.$val;
            }
        }        
    }
    $parm .= '&action=search';
    $where = substr($where, 0, -5);
}else{
    foreach($searchField as $key){
        $searchFieldValue[$key['id']] = null;
    }
}
$SPIRAL->setTHValue("searchFieldValue", $searchFieldValue);
$SPIRAL->setTHValue("searchParm", $parm);

//------------------------------
// レコードリスト生成処理
//------------------------------
$page = $SPIRAL->getParam("page");
if(!$page){
    $page = 1;
}
$offset = (int)(PAGE_LIMIT * $page) - PAGE_LIMIT;

$resultRecordListSelect = $commonBase->apiCurlAction("GET", "/apps/". APP_ID. "/dbs/". DB_ID. "/records?optionsFormat=label&enableTotalCount=true&limit=".PAGE_LIMIT.'&sort='.SORT['id'].':'.SORT['sort'].'&offset='.$offset.'&'.$where);
//$SPIRAL->setTHValue("debug", print_r($resultRecordListSelect,true));

if(!isset($resultRecordListSelect['items']) || empty($resultRecordListSelect['items'])){
    $resultRecordListSelect['totalCount'] = 0;
}elseif(is_array($format) && $format){
    $dataList = $resultRecordListSelect['items'];
    foreach($dataList as $key => $record){
        foreach(array_keys($record) as $field){ 
            // 配列をテキストに変換
            if(is_array($resultRecordListSelect['items'][$key][$field])){
                // カンマ区切り
                $resultRecordListSelect['items'][$key][$field] = implode(',', $resultRecordListSelect['items'][$key][$field]);
                // カンマ+改行
                //$resultRecordListSelect['items'][$key][$field] = implode(','.PHP_EOL, $resultRecordListSelect['items'][$key][$field]);
            }
            // 電話番号フォーマット変更
            if(isset($format[$field]) && $format[$field] == 'tel'){
                // 日本コード(+81)のみ対応
                $resultRecordListSelect['items'][$key][$field] = $commonBase->domesticPhoneNumJP($resultRecordListSelect['items'][$key][$field]);
            }
            // 日付のフォーマット変更
            if(isset($format[$field]) && preg_match('/^date-/',$format[$field])){
                $resultRecordListSelect['items'][$key][$field] = $commonBase->dateFormat($resultRecordListSelect['items'][$key][$field],$format[$field]);
            }
            // 数値をカンマ区切りに変更
            if(isset($format[$field]) && $format[$field] == 'num'){
                // nullの場合、0が入らないように
                if($resultRecordListSelect['items'][$key][$field]){
                    $resultRecordListSelect['items'][$key][$field] = number_format($resultRecordListSelect['items'][$key][$field]);
                }                
            }
            //
        }
    }
}

if(is_array($linkField)){
    $linkField['url'] = $linkField['url'].'?record='.DB_ID;
}
$SPIRAL->setTHValue("listField", $listField);
$SPIRAL->setTHValue("resultRecordListSelect", $resultRecordListSelect);
$SPIRAL->setTHValue("linkField", $linkField);
$SPIRAL->setTHValue("searchField", $searchField);
// debug用
//$SPIRAL->setTHValue("debug", print_r($resultRecordListSelect,true));


//------------------------------
// ページャの処理
//------------------------------
$pager['def'] = (int)$page;
$pager['limit'] = (int)ceil($resultRecordListSelect['totalCount'] / PAGE_LIMIT);
if($page == 1){
    $pager['firstPageLink'] = 0;
    $pager['prevPageLink'] = 0;
}else{
    $pager['firstPageLink'] = 1;
    $pager['prevPageLink'] = $page - 1;
}
if(($page * PAGE_LIMIT) < $resultRecordListSelect['totalCount']){
    $pager['lastPageLink'] = $pager['limit'];
    $pager['nextPageLink'] = $page + 1;
}else{
    $pager['lastPageLink'] = 0;
    $pager['nextPageLink'] = 0;
}
$in = 0;
$pager['limitOver'] = false;
for($i=-4;$i<=5;$i++){
    $pageNum = $page+$i;
    if($pager['def'] < $pager['limit'] && -2 <= $i){

    }
    if((($pager['def'] < $pager['limit'] && -2 <= $i) || ($pager['def']+1 == $pager['limit'] && -3 == $i) || $pager['def'] == $pager['limit']) && 0<$pageNum && $pageNum<=$pager['limit'] ){
        $pager['pageNum'][] = $pageNum;
        $in++;
        if($in==5){
            if($pageNum < $pager['limit']){
                $pager['limitOver'] = true;
            }
            break;
        }
    }
}
$SPIRAL->setTHValue("pager", $pager);

//------------------------------
// 共通モジュール
//------------------------------
class CommonBase {
    /**
     * シングルトンインスタンス
     * @var UserManager
     */
    protected static $singleton;

    public function __construct() {
        if (self::$singleton) {
            throw new Exception('must be singleton');
        }
        self::$singleton = $this;
    }
    /**
     * シングルトンインスタンスを返す
     * @return UserManager
     */
    public static function getInstance() {
        if (!self::$singleton) {
            return new CommonBase();
        } else {
            return self::$singleton;
        }
    }
    /**
     * V2用 API送信ロジック
     * @return Result
     */
    function apiCurlAction($method, $addUrlPass, $data = null, $multiPart = null, $jsonDecode = null) {
        $header = array(
            "Authorization:Bearer ". API_KEY,
            "X-Spiral-Api-Version: 1.1",
        );
        if($multiPart) {
            $header = array_merge($header, array($multiPart));
        } else {
            $header = array_merge($header, array("Content-Type:application/json"));
        }
        if(APP_ROLE){
			$header = array_merge($header, array("X-Spiral-App-Role: ".APP_ROLE));
		}
        // curl
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_URL, API_URL. $addUrlPass);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        if ($method == "POST") {
            if ($multiPart) {
                curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            } else {
                curl_setopt($curl, CURLOPT_POSTFIELDS , json_encode($data));
            }
            curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
        }
        if ($method == "PATCH") {
            curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
            curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
        }
        if ($method == "DELETE") {
            curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
            curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
        }
        $response = curl_exec($curl);
        if (curl_errno($curl)) echo curl_error($curl);
        curl_close($curl);
        if($jsonDecode){
			return $response;
		}else{
            return json_decode($response, true);
		}
    }

    function domesticPhoneNumJP ($phone) {
        if(PHONE_NUMBER_COUNTRY == "ja"){
            return str_replace('+81 ', '0', $phone);
        }
        return $phone;
        
    }
    function domesticPhoneNum ($phone) {
        if(PHONE_NUMBER_COUNTRY == "ja"){
            return substr_replace($phone, "+81 ", 0, 1);
        }
        return $phone;
    }
    function dateFormat($date,$type){
        if(!$date){
            return;
        }
        $t = new DateTime($date);
        $t->setTimeZone(new DateTimeZone(DATE_TIME_ZONE));
        return $t->format(DATE_FORMAT[$type]);
    }
}
設定値
API 設定値
API_URL 5行目 リクエスト先URLの固定部分です。固定値ですので特に変更する必要はありません。
API_KEY 6行目 発行したAPIキーを設定してださい。別途権限の付与が必要になります。
APP_ROLE 7行目 設定したアプリロールの識別名を入れてください。全権限の場合は値は空で大丈夫です。
APP_ID 8行目 レコード操作を行うDBがあるアプリのIDを設定してください。
DB_ID 9行目 レコード操作を行うDBのIDを設定してください。
操作するDBが複数ある場合は「DB_ID_識別名」など適宜定数を追加してください。
・補足
define("API_KEY", $SPIRAL->getEnvValue("apikey"));
上記はAPIキーの記述を「PHP環境変数設定」を利用した場合のコードとなります。
このように定数を設定する時に、ver.2 機能の「PHP環境変数設定」を利用することで、
本番環境とテスト環境でキーが異なっていても、コードを書き換える必要がなく、保守性が高まります。
APIキーなど複数ページで使用するものは、環境変数設定をしておきましょう。

その他詳しくはサポートサイト「PHP環境変数」をご参照ください。
レコードリスト 設定値
SORT 15行目 ソートを行う場合に指定します。
id:ソートする対象を指定します。_id、フィールド識別名、_createdAt、_updatedAtが指定可能
sort:ソート順を指定します。"asc"は昇順、"desc"は降順
PAGE_LIMIT 17行目 一覧に表示するレコード数を指定します。
設定できる数値は、1から200です。
$listField 20行目

34行目
レコードリストに表示するフィールドを指定します。
id:レコードリストに表示させるフィールドの識別名
label:レコードリストに表示させるフィールドのタイトルを任意で指定
$linkField 38行目

39行目
レコードアイテムへリンクするフィールドを指定します。
id:リンクを設置するフィールドの識別名
url:レコードアイテムのURLの相対パス
設定しない場合は、空文字としてください。
$format 41行目

49行目
表示させるフォーマットを変更する場合に指定します。
電話番号(phone)、日時(date-)、日付(date-)、数値(num)、整数(num)、登録日時(date-)、更新日時(date-)が指定可能です。
日付の表示させ方については、88行目から95行目に記載しているので、環境に合わせて修正してください。
フォーマットを独自に変更する場合は、198行目付近が変更箇所となります。
フォーマットを指定しない場合は、42行目から48行目の設定値のみコメントアウトにしてください。
PHONE_NUMBER_COUNTRY 51行目 電話番号を日本以外に変換する際に指定する箇所となります。
351行目から363行目に記載されている2つの関数に海外の変換処理を追加してください。
検索 設定値
$searchField 59行目

77行目
検索項目として表示するフィールドを指定します。
表示しない項目に関しても指定することが可能です。
id:検索項目に表示させるフィールドの識別名を指定します。一部指定できないフィールドがございます。
label:検索項目に表示させるフィールドのタイトルを任意で指定
operator:比較演算子を指定します。一部指定できない比較演算子がございます。
fieldType:text、integer、email、select、multi、phone、dateTime、date、monthDay、time から指定
演算子やタイプなどを独自に変更させたい場合は、115行目から156行目が変更箇所となります。

Thymeleaf サンプルプログラム

レコードリスト/検索 を表示するためのHTMLとなります。
検索は使用しなくても問題ありません。
レコードリスト/検索 のHTMLは、通常ブロックと同様にフリーコンテンツブロックに設置することを推奨しております。

また、HTMLのタグ構成は、レコードリストブロック/検索ブロックと同じになっています。
CSSに関しては、レコードリストで使えるコピペCSS 「カスタムデザイン」をご利用ください。

レコードリスト - HTML
<div class="sp-record-list-container"> 
  <div class="sp-record-list-parts sp-html-parts" th:inline="none"><p><span style="font-size: 18pt;">レコードリスト</span></p></div> 
  <div class="sp-record-list-parts"> 
    <table class="sp-record-list-table sp-table-hover" th:if="${0 le cp.result.value['resultRecordListSelect']['totalCount']}"> 
      <thead> 
        <tr>
          <!--/* ヘッド */--> 
          <th scope="col" th:each="field, stat : ${cp.result.value['listField']}">
            <span th:text="${field['label']}">12345</span>
          </th>
        </tr> 
      </thead> 
      <tbody> 
        <tr th:each="record, stat : ${cp.result.value['resultRecordListSelect']['items']}">
          <!--/* ボディ */-->
          <td th:each="field, stat : ${cp.result.value['listField']}">
            <th:block th:if="${record[field['id']]} ne null">
              <a th:if="${cp.result.value['linkField']} AND ${field['id']} eq ${cp.result.value['linkField']['id']}" th:href="|${cp.result.value['linkField']['url']}.${record['_id']}|">
                <span th:text="${record[field['id']]}"></span>
              </a>
              <span th:unless="${field['id']} eq ${cp.result.value['linkField']['id']}">
                <th:block th:each="line: ${#strings.toString(record[field['id']]).split('\r\n|\r|\n', -1)}">
                  <th:block th:text="${line}" /><br />
                </th:block>
              </span>
            </th:block>
          </td>
        </tr>
      </tbody> 
    </table> 
    <div class="sp-record-list-no-records" th:if="${0 eq cp.result.value['resultRecordListSelect']['totalCount']}">
      表示できるデータがありません。
    </div> 
  </div> 
  <div class="sp-record-list-parts sp-record-list-pagination"> 
    <div class="sp-record-list-pagination-left"></div> 
    <div class="sp-record-list-pagination-center"> 
    </div> 
    <div class="sp-record-list-pagination-right">
      <ul class="sp-page-navs" th:if="${cp.result.value['resultRecordListSelect']['totalCount'] != 0}"> 
        <li class="sp-page-nav-item" th:if="${0 lt cp.result.value['pager']['firstPageLink']}">
          <a th:href="|?page=${cp.result.value['pager']['firstPageLink']}${cp.result.value['searchParm']}|">&lt;&lt;</a>
        </li> 
        <li class="sp-page-nav-item" th:if="${0 lt cp.result.value['pager']['prevPageLink']}">
          <a th:href="|?page=${cp.result.value['pager']['prevPageLink']}${cp.result.value['searchParm']}|">&lt;</a>
        </li> 
        <li class="sp-page-nav-more" th:if="${3 lt cp.result.value['pager']['def']}">...</li>
        <th:block th:each="pageNum : ${cp.result.value['pager']['pageNum']}">
          <li th:if="${pageNum} eq ${cp.result.value['pager']['def']}" class="sp-page-nav-item sp-page-nav-current">
            <span th:text="${pageNum}">Page Number</span>
          </li> 
          <li th:unless="${pageNum} eq ${cp.result.value['pager']['def']}" class="sp-page-nav-item">
            <a th:text="${pageNum}" th:href="|?page=${pageNum}${cp.result.value['searchParm']}|">Page Number</a>
          </li> 
        </th:block>
        <li class="sp-page-nav-more" th:if="${cp.result.value['pager']['limitOver']}">...</li>
        <li class="sp-page-nav-item" th:if="${0 lt cp.result.value['pager']['nextPageLink']}">
          <a th:href="|?page=${cp.result.value['pager']['nextPageLink']}${cp.result.value['searchParm']}|">&gt;</a>
        </li> 
        <li class="sp-page-nav-item" th:if="${0 lt cp.result.value['pager']['lastPageLink']}">
          <a th:href="|?page=${cp.result.value['pager']['lastPageLink']}${cp.result.value['searchParm']}|">&gt;&gt;</a>
        </li> 
      </ul> 
    </div> 
  </div> 
</div>
検索 - HTML
<div class="sp-form-item sp-form-html"><p style="text-align: center;"><strong style="font-size: 16pt; color: rgb(68, 68, 68);">検索</strong></p></div>
<div class="sp-record-search-container"> 
  <form action="#" method="GET">
  <!--/* 検索項目 */--> 
  <div class="sp-record-search-parts sp-record-search-field" th:each="field, stat : ${cp.result.value['searchField']}">
    <div class="sp-record-search-label" th:text="${field['label']}">Label</div> 
    <!--/* ラジオ */--> 
    <div class="sp-record-search-data" th:if="${field['view']} eq 'radio'"> 
      <div class="sp-form-selection-horizontal">
        <label class="sp-form-selection" th:each="option : ${field['list']}">
          <input type="radio" th:name="${field['id']}" th:value="${option.key}" th:checked="${#strings.toString(cp.result.value['searchFieldValue'][field['id']])} eq ${#strings.toString(option.key)} ? 'checked' : false">
          <span class="sp-form-selection-label" th:text="${option.value}">Item</span>
        </label>
      </div> 
    </div> 
    <!--/* マルチセレクト */--> 
    <div class="sp-record-search-data" th:if="${field['view']} eq 'multi'"> 
      <div class="sp-form-selection-horizontal">
        <label class="sp-form-selection" th:each="option : ${field['list']}">
          <input type="checkbox" th:name="${field['id']}" th:value="${option.key}" th:checked="${cp.result.value['searchFieldValue'][field['id']] != null ? #lists.contains(cp.result.value['searchFieldValue'][field['id']], #strings.toString(option.key)) : false}">
          <span class="sp-form-selection-label" th:text="${option.value}">Item</span>
        </label>
      </div> 
    </div> 
    <!--/* 日時 */--> 
    <div class="sp-record-search" th:if="${field['fieldType']} eq 'dateTime'"> 
      <div class="sp-form-date">
        <input type="text" class="sp-form-control sp-form-datetime" th:name="${field['id']}+':after'" th:value="${cp.result.value['searchFieldValue'][field['id']+':after']}" placeholder="1970/1/1 00:00:00">
        <span class="sp-form-date-separator">〜</span>
        <input type="text" class="sp-form-control sp-form-datetime" th:name="${field['id']}+':before'" th:value="${cp.result.value['searchFieldValue'][field['id']+':before']}" placeholder="1970/1/1 00:00:00">
      </div>
    </div> 
    <!--/* 日付 */--> 
    <div class="sp-record-search-data" th:if="${field['fieldType']} eq 'date'"> 
      <div class="sp-form-date">
        <input type="text" class="sp-form-control sp-form-datetime" th:name="${field['id']}+':after'" th:value="${cp.result.value['searchFieldValue'][field['id']+':after']}" placeholder="1970/1/1">
        <span class="sp-form-date-separator">〜</span>
        <input type="text" class="sp-form-control sp-form-datetime" th:name="${field['id']}+':before'" th:value="${cp.result.value['searchFieldValue'][field['id']+':before']}" placeholder="1970/1/1">
      </div>
    </div> 
    <!--/* 月日 */--> 
    <div class="sp-record-search-data" th:if="${field['fieldType']} eq 'monthDay'"> 
      <div class="sp-form-date">
        <input type="text" class="sp-form-control sp-form-datetime" th:name="${field['id']}+':after'" th:value="${cp.result.value['searchFieldValue'][field['id']+':after']}" placeholder="1/1">
        <span class="sp-form-date-separator">〜</span>
        <input type="text" class="sp-form-control sp-form-datetime" th:name="${field['id']}+':before'" th:value="${cp.result.value['searchFieldValue'][field['id']+':before']}" placeholder="1/1">
      </div>
    </div> 
    <!--/* 時刻 */--> 
    <div class="sp-record-search-data" th:if="${field['fieldType']} eq 'time'"> 
      <div class="sp-form-date">
        <input type="text" class="sp-form-control sp-form-datetime" th:name="${field['id']}+':after'" th:value="${cp.result.value['searchFieldValue'][field['id']+':after']}" placeholder="00:00:00">
        <span class="sp-form-date-separator">〜</span>
        <input type="text" class="sp-form-control sp-form-datetime" th:name="${field['id']}+':before'" th:value="${cp.result.value['searchFieldValue'][field['id']+':before']}" placeholder="00:00:00">
      </div>
    </div> 
    <!-- その他(テキスト) -->
    <div class="sp-record-search-data" th:if="${field['fieldType']} eq 'text' OR ${field['fieldType']} eq 'email'
     OR ${field['fieldType']} eq 'integer' OR ${field['fieldType']} eq 'phone'"> 
      <input class="sp-form-control" th:name="${field['id']}" th:value="${cp.result.value['searchFieldValue'][field['id']]}">
    </div> 
  </div>  
  <div class="sp-record-search-parts sp-form-interaction">
    <button class="sp-search-button" type="submit" name="action" value="search">検索</button>
  </div> 
  </form>
</div>
検索に関しては、fieldType によって入力方法をカスタマイズしてています。
個別でカスタマイズしたい方は条件を追加してください。

最後に

設定後は動作確認を必ず行い、動作に問題がないか確認をしてください。
不具合やほかのやり方が知りたい等あれば、下記の「コンテンツに関しての要望はこちら」からご連絡ください。
解決しない場合はこちら コンテンツに関しての
要望はこちら