開発情報・ナレッジ

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

【Thymeleafを使いこなそう】第4回 PHP APIを用いた実装解説

Thymeleaf(タイムリーフ)はPHPと組み合わせることで、より幅広いカスタマイズが可能となります。
本記事ではAPIを用いた実装について解説いたします。
まず、APIを扱うには事前にAPIエージェントの発行等の準備が必要となりますので
SPIRAL WebTools API導入ガイドをご参照のうえ、APIを利用するための設定を行ってください。

記事内のAPIコードにつきましては、APIメソッドごとのサンプルコード&権限設定特集を参考に記載しております。
APIメソッドごとのサンプルコード・権限設定についての解説がございますので、ぜひご確認ください。
Thymeleafの記法や動作についてはSPIRAL ver.2 サポートサイト Thymeleaf記法をご参照ください。
また、Thymeleaf関連記事をまとめた Thymeleaf特集 も是非ご覧ください。
関連記事はこちら

PHP設定値に関して

共通で使用する値に関しては、定数として初めに設定します。
具体的には下記のような値を設定してください。

API_URL https://api.spiral-platform.com/v1
リクエスト先URLの固定部分です。固定値ですので特に変更する必要はありません。
API_KEY 発行したAPIキーを設定してださい。別途権限の付与が必要になります。
APP_ROLE 設定したアプリロールの識別名を入れてください。全権限の場合は値は空で大丈夫です。
DB_ID レコード操作を行うDBのIDを設定してください。
操作するDBが複数ある場合は「DB_ID_識別名」など適宜定数を追加してください。
APP_ID レコード操作を行うDBがあるアプリのIDを設定してください。

定数を設定する時に、WebTools 機能の「PHP環境変数設定」を利用することで、
本番環境とテスト環境でキーが異なっていても、コードを書き換える必要がなく、保守性が高まります。
APIキーなど複数ページで使用するものは、環境変数設定をしておきましょう。

その他詳しくはサポートサイト「PHP環境変数」をご参照ください。

エラーを取得する方法について

PHPの実行結果をThymeleafで判別するためには、PHPの値をThymeleafに渡す必要があります。
APIエラーを取得する例については、以下となります。
例を参考に、使用するAPIにより内容を調整ください。

APIエラーコードについては、APIエラーコードをご確認ください。
PHPのコード自体がエラーとなっているかは、【isSuccess】にて確認可能です。
Thymeleaf側では、この【isSuccess】にて、コードのエラーなのかAPIのエラーかを判別しています。
※確認やデバック用としてご使用ください。
APIエラーを取得
▼APIエラーを取得:PHPコード
<?php
if(array_key_exists('status', $data)){//APIのエラーが発生した場合、APIのレスポンスにstatusが200以外で返ってくるので、statusをチェック
    if($data["status"] != 200){
        $SPIRAL->setTHValue("APIERROR", $data["message"]);//APIのエラーをThymeleafにセット
    }
} else {
    $SPIRAL->setTHValue("data", $data);//データをThymeleafにセット
}
▼APIエラーを取得:Thymeleafコード
<div th:if="${cp.result.isSuccess}">
    <p th:text="${cp.result.value['APIERROR']}"></p><!-- APIエラー文言を出力 -->
    <p th:text="${cp.result.value['data']}"></p>
</div>
<div th:if="${!cp.result.isSuccess}">
    <p th:text="${cp.result.errorMessage}">error message</p>
</div>

レコードアイテムの表示ページでデータ取得

レコードアイテムは、URLのクエリパラメータに【record=DBID.レコードID】が付与されていますので、
該当のレコードを取得して画面表示を制御することが可能です。
本コードではレコードアイテムのデータをAPIで取得し、取得したデータをThymeleafにセットする動作をいたします。

本コードを応用することで、以下のようなことが可能となります。
●お問い合わせ管理フローで利用し、お問い合わせが【完了】のステータスの時に「お問い合わせコメント登録」非表示
レコードアイテムの表示ページでデータ取得
▼レコードアイテムの表示ページでデータ取得:PHPコード
<?php
//------------------------------
// 設定値
//------------------------------
define("API_URL", $SPIRAL->getEnvValue("API_URL"));
define("API_KEY", $SPIRAL->getEnvValue("API_KEY"));
define("APP_ROLE", $SPIRAL->getEnvValue("APP_ROLE"));
define("APP_ID", $SPIRAL->getEnvValue("APP_ID"));

// Thymeleafでのエラー表示だし分け用
$SPIRAL->setTHValue("error", false);

// クエリパラメータからDBID・レコードIDの取得(クエリパラメータは record=DBID.recordID)
$getParam_record = $SPIRAL->getParam("record");
$getParam_recordValues = GET_DBID_recordID($getParam_record);

// クエリパラメータが取得できている場合にAPI実行
if($getParam_recordValues){
    //------------------------------
    // API実行
    //------------------------------
    $commonBase = CommonBase::getInstance();
    // レコード取得メソッド
    $resultRecordSelect = $commonBase->apiCurlAction("GET", "/apps/". APP_ID. "/dbs/". $getParam_recordValues['DBID']. "/records/". $getParam_recordValues['recordID']);

    if(array_key_exists('status', $resultRecordSelect)){//APIのエラーが発生した場合、APIのレスポンスにstatusが200以外で返ってくるので、statusをチェック
        if($resultRecordSelect["status"] != 200){
            // Thymeleafでのエラー表示だし分け用
            $SPIRAL->setTHValue("error", true);
            $SPIRAL->setTHValue("errorDetail", $resultRecordSelect);
        }
    } else {
        //データをThymeleafにセット
        $SPIRAL->setTHValue("data", $resultRecordSelect["item"]);
    }
} else {
    // Thymeleafでのエラー表示だし分け用
    $SPIRAL->setTHValue("error", true);
    $SPIRAL->setTHValue("errorDetail", "getParamで値が取得できていません");
}

// クエリパラメータからDBID・レコードIDの取得関数
// 値がなければnull
function GET_DBID_recordID($getParam_record){
    if($getParam_record!=null){
        // 「.」区切りで分割
        $getParam_recordValues = explode(".", $getParam_record);
        $DBID = $getParam_recordValues[0];
        $recordID = $getParam_recordValues[1];
        // 値がnullではなく、整数であるかチェック
        if($DBID == null or $recordID == null or ctype_digit($DBID) or ctype_digit($recordID)){
            return;
        } else {
            return array("DBID" => $DBID, "recordID" => $recordID);
        }
    } else {
        return;
    }
}

//------------------------------
// 共通モジュール
//------------------------------
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);
		}
    }
}
▼レコードアイテムの表示ページでデータ取得:Thymeleafのコード
<!-- 正常動作 -->
<th:block th:unless="${cp.result.value['error']}">
    <p>正常動作</p>
    <p th:text="${cp.result.value['data']}"></p>
</th:block>
<!-- PHP/APIにエラーが発生した場合 -->
<th:block th:if="${cp.result.value['error']}">
    <p>ERROR</p>
    <p th:text="${cp.result.value['errorDetail']}"></p>
</th:block>

フォーム上に参照フィールドをプルダウン表示

登録・更新フォームでは、ソース設定にすることで参照フィールドを入力項目として設定することが可能です。
入力項目で設定した場合にはキーフィールドを元に登録となるため、参照フィールドにどのような値があるのかを確認して登録することができません。
本コードでは、登録フォームの参照フィールドを参照DBの項目プルダウンで選択できる動作となります。
操作対象のDBは、参照先フィールドのDBとなります。
フォーム上に参照フィールドをプルダウン表示
下記部分は、変更必要な個所になります。
記載内容に合わせてコードを変更ください。
【PHP】
■$data["items"][] = array("_id"=>$item["_id"],"text"=>$item["表示するフィールド識別名"]);
【Thymeleaf】
■f0xx:参照フィールドのフィールドID
▼フォーム上に参照フィールドをプルダウン表示:PHPコード
<?php
//------------------------------
// 設定値
//------------------------------
define("API_URL", $SPIRAL->getEnvValue("API_URL"));
define("API_KEY", $SPIRAL->getEnvValue("API_KEY"));
define("APP_ROLE", $SPIRAL->getEnvValue("APP_ROLE"));
define("APP_ID", $SPIRAL->getEnvValue("APP_ID"));
define("DB_ID", $SPIRAL->getEnvValue("DB_ID"));

$commonBase = CommonBase::getInstance();

$resultRecordListSelect = $commonBase->apiCurlAction("GET", "/apps/". APP_ID. "/dbs/". DB_ID. "/records");
if(array_key_exists('status', $resultRecordListSelect)){//APIのエラーが発生している場合
    if($resultRecordListSelect["status"] != 200){
        $SPIRAL->setTHValue("APIERROR", $resultRecordListSelect["message"]);//APIのエラーをThymeleafにセット
    }
    }else{
    $data = array();
    foreach($resultRecordListSelect["items"] as $item){ //データを取得
        // データセット(本コードでは、登録用にID、表示用にテキストをセット)
        $data["items"][] = array("_id"=>$item["_id"],"text"=>$item["表示するフィールド識別名"]);
    }
    $SPIRAL->setTHValues($data); //データをThymeleafにセット
}

//------------------------------
// 共通モジュール
//------------------------------
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;
        }
    }
    /**
     * WebTools用 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);
        }
    }
}
?>
▼フォーム上に参照フィールドをプルダウン表示:Thymeleafコード
<!--/* 参照フィールド(ref) */-->
<sp:input-field name="f0xx"></sp:input-field> 
<div class="sp-form-item sp-form-field"> 
  <div class="sp-form-label">
    <th:block th:text="${fields['f0xx'].label}">
      Label
    </th:block>
    <span class="sp-form-required" th:if="${fields['f0xx'].required}" th:text="${fields['f0xx'].requiredIndicator}">*</span>
  </div> 
  <div class="sp-form-data"> 
    <div th:if="${cp.result.isSuccess}">
      <div class="sp-form-dropdown">
        <select class="sp-form-control" th:name="${fields['f0xx'].name}">
          <option value="" selected="selected">----- 選択してください -----</option>
          <option th:each="val : ${cp.result.value['items']}" th:value="${val['_id']}" th:text="${val['text']}" th:selected="${val['_id']} == ${inputs['f0xx']}">Item</option>
        </select>
        <span class="sp-form-dropdown-icon"></span>
      </div> 
      <p th:text="${cp.result.value['APIERROR']}"></p>
    </div>
    <div th:if="${!cp.result.isSuccess}">
      <p th:text="${cp.result.errorMessage}">error message</p>
    </div>
    <span class="sp-form-noted" th:if="${fields['f0xx'].help != null}" th:text="${fields['f0xx'].help}">Help text</span>
    <span class="sp-form-error" th:if="${errors['f0xx'] != null}" th:text="${errors['f0xx'].message}">Error message</span>
  </div> 
</div>

フォームの完了画面でAPIの処理

独自クラスを用いることで、登録フォームなどで現在のステップ状況を取得することが可能です。
本コードでは、登録フォームの完了ステップにて、APIを動作させる処理を行います。
APIでは登録フォームにて登録したデータに対し更新する処理を行い、更新したレコードのデータをThymeleafにセットする動作をいたします。

DBの非同期アクションからでもPHP実行は可能ですが、DB1つにつき非同期アクションの設定は5件までの設定となるので、設定上限に達する場合にご利用してみてください。
また、非同期アクションの場合には、アクションの実行結果を画面に返すことができないため、
APIの結果により画面表示を変更したい場合にもご利用ください。
フォームの完了画面でAPIの処理
下記部分は、変更必要な個所になります。
記載内容に合わせてコードを変更ください。
■$registForm = $SPIRAL->getRegistrationForm("登録フォーム識別名");
■"フィールド識別名" => "更新する値
▼フォームの完了画面でAPIの処理:PHPコード
<?php
//------------------------------
// 設定値
//------------------------------
define("API_URL", $SPIRAL->getEnvValue("API_URL"));
define("API_KEY", $SPIRAL->getEnvValue("API_KEY"));
define("APP_ROLE", $SPIRAL->getEnvValue("APP_ROLE"));
define("APP_ID", $SPIRAL->getEnvValue("APP_ID"));
define("DB_ID", $SPIRAL->getEnvValue("DB_ID"));
// Thymeleafでのエラー表示だし分け用
$SPIRAL->setTHValue("error", false);

// 登録フォームの場合
$registForm = $SPIRAL->getRegistrationForm("登録フォーム識別名");
// 完了ステップの場合
if ($registForm->isCompletedStep()){
    //------------------------------
    // API実行
    // 登録したレコードに対し更新
    //------------------------------
    $commonBase = CommonBase::getInstance();
    
    // 登録したデータのidを取得
    $record = $SPIRAL->getRecordValue(); 
    $recordID = $record['item']['_id']; 
    // 更新するデータを指定
    $UpdateData = array(
        "フィールド識別名"  => "更新する値",
        "フィールド識別名"  => "更新する値",
        "フィールド識別名"  => "更新する値"
    );
    // 更新実行
    $resultRecordUpdate = $commonBase->apiCurlAction("PATCH", "/apps/". APP_ID. "/dbs/". DB_ID. "/records/". $recordID, $UpdateData);
    if(array_key_exists('status', $resultRecordUpdate)){ //APIのエラーが発生した場合、APIのレスポンスにstatusが200以外で返ってくるので、statusをチェック
        if($resultRecordUpdate["status"] != 200){
            // Thymeleafでのエラー表示だし分け用
            $SPIRAL->setTHValue("error", true);
            $SPIRAL->setTHValue("errorDetail", $resultRecordUpdate);
        }
    } else {
        //データをThymeleafにセット
        $SPIRAL->setTHValue("data", $resultRecordUpdate["item"]);
    }
}

//------------------------------
// 共通モジュール
//------------------------------
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);
		}
    }
}
▼フォームの完了画面でAPIの処理:Thymeleafコード
<!-- 正常動作 -->
<th:block th:unless="${cp.result.value['error']}">
    <p>正常動作</p>
    <p th:text="${cp.result.value['data']}"></p>
</th:block>
<!-- PHP/APIにエラーが発生した場合 -->
<th:block th:if="${cp.result.value['error']}">
    <p>ERROR</p>
    <p th:text="${cp.result.value['errorDetail']}"></p>
</th:block>

その他 API Thymeleaf連携記事

本記事での紹介は以上となりますが、そのほかにもThymeleafとAPIを組み合わせることで、標準機能よりも自由度の高いレコードリストを作成することや、動的検索プルダウンを作成などより自由度の高い開発が可能となります。
難易度が高いものとなりますが、ぜひ実装のご検討ください。
PHP/APIで一覧表を作るサンプルプログラム
カスタムAPIを使った動的社員検索プルダウンを作成するサンプルプログラム
カスタムAPIでAIチャットウィンドウを作ってみた
関連記事はこちら
解決しない場合はこちら コンテンツに関しての
要望はこちら