Snowflakeにたまっているデータを、BIツール(QuickSight、Looker など)を使わずに、手元のWordPressサイトでさっと表やグラフにして見たい!という検証をしました。本記事では、WordPressプラグインの実装をできる限り薄くする考え方と、実際に書いたコードのポイントを紹介します。
全体の構成
今回は、WordPressからSnowflake SQL APIを呼び出し、取得したデータをWebページ上で表示します。構成を複雑にしないため、WordPress側ではデータの取得までを担当し、表やグラフなどの表示処理はブラウザ側に任せることにしました。そのために、両者の間ではSnowflakeから取得したデータをJSONとして受け渡します。
Snowflake・WordPress・ブラウザをつなぐ設計
今回の実装でポイントになるのが、<script id="sfq-data" type="application/json"> というタグです。このタグを、WordPress側とブラウザ側の責務を分ける境界として使います。
- WordPress(PHP)側: Snowflakeに問い合わせて、結果をJSON文字列としてタグの中に差し込む
- ブラウザ(JavaScript)側: JSONを読み込み、表やグラフとして描画する
この境界を決めておけば、表からグラフに変更したり、利用するJavaScriptライブラリを変更したりしても、PHP側のコードには手を入れる必要がありません。逆にWordPress側は、「Snowflakeにクエリを投げ、結果をJSONとして渡す」ことだけに集中できます。この役割分担によって、プラグイン側の実装もシンプルに保てます。
プラグイン構成
今回作ったプラグイン(snowflake-token-query-viewer)の構成です。認証方式はSnowflakeのPersonal Access Token(PAT、トークン方式)を使っています。
snowflake-token-query-viewer/
├── snowflake-token-query-viewer.php … プラグイン本体(初期化)
├── src/
│ ├── Admin/
│ │ └── PageController.php … 管理画面(接続設定・動作確認クエリ)
│ ├── Snowflake/
│ │ ├── SettingsRepository.php … 接続設定の保存/取得
│ │ ├── QueryClient.php … Snowflake SQL API v2への問い合わせ
│ │ ├── QueryResult.php … 結果の入れ物
│ │ └── SnowflakeApiException.php … 例外
│ └── Frontend/
│ └── InquiryDataShortcode.php … [sfq_inquiry_data] ショートコード(今回の主役)
└── templates/
└── page.php … 管理画面のビュー
ファイル数は少なく、それぞれの役割もはっきり分かれています。
WP側プラグイン
ツールでの設定(Snowflake接続情報)
接続情報(アカウント、ウェアハウス、データベース、スキーマ、ロール、トークン)は、wp-config.phpを編集するのではなく、「ツール」メニュー配下の設定画面から登録できるようにしました。保存先はWordPressのwp_optionsテーブルです。
src/Snowflake/SettingsRepository.php
class SettingsRepository {
private const OPTION_PREFIX = 'sftq_snowflake_';
private const TEXT_FIELDS = array( 'account', 'warehouse', 'database', 'schema', 'role' );
public function get(): array {
$data = array();
foreach ( self::TEXT_FIELDS as $field ) {
$data[ $field ] = (string) get_option( self::OPTION_PREFIX . $field, '' );
}
$data['token'] = (string) get_option( self::OPTION_PREFIX . 'token', '' );
return $data;
}
public function save( array $input ): void {
foreach ( self::TEXT_FIELDS as $field ) {
update_option( self::OPTION_PREFIX . $field, sanitize_text_field( $input[ $field ] ?? '' ), false );
}
// トークン欄が空なら上書きしない。既存のトークンをそのまま維持する
$token = trim( sanitize_text_field( $input['token'] ?? '' ) );
if ( '' !== $token ) {
update_option( self::OPTION_PREFIX . 'token', $token, false );
}
}
}
ショートコードの実装要点
ショートコード名はsfq_inquiry_data、引数は実行するSQLを渡すsql属性の1つだけです。データ取得・出力の本体はこのクラス1つだけです。役割は本当に「Snowflakeに問い合わせて、JSON文字列を返す」だけで、HTMLもCSSも一切生成しません。
src/Frontend/InquiryDataShortcode.php
class InquiryDataShortcode {
public function register(): void {
add_shortcode( 'sfq_inquiry_data', array( $this, 'render' ) );
}
public function render( $atts = array() ): string {
$atts = shortcode_atts( array( 'sql' => self::DEFAULT_SQL ), (array) $atts, 'sfq_inquiry_data' );
$sql = (string) $atts['sql'];
if ( ! $this->settings->isComplete() ) {
error_log( 'SnowflakeTokenQueryViewer: 接続設定が未保存です。' );
return '[]';
}
try {
$result = $this->client->run( $sql );
} catch ( \Throwable $e ) {
// 失敗時も画面を壊さないよう空配列を返し、詳細はログにのみ残す
error_log( 'SnowflakeTokenQueryViewer: クエリ失敗: ' . $e->getMessage() );
return '[]';
}
$rows = array_map( array( $this, 'mapRow' ), $result->rows );
return (string) wp_json_encode( $rows, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG );
}
}
失敗した場合(接続未設定・Snowflake側のエラーなど)も、ページを壊さないよう常に有効なJSON(空配列 [])を返します。詳細なエラーはerror_log()にのみ記録し、フロント側の出力には一切現れません。
クエリを引数にすることで汎用化
sql属性で外から渡せるようにしました。これで同じショートコード1つを使い回して、いろいろなクエリの結果を表示できます。
[sfq_inquiry_data sql='SELECT INQUIRY_ID, TO_VARCHAR(RECEIVED_AT, ''YYYY-MM-DD HH24:MI:SS'') AS RECEIVED_AT,
PRODUCT, CATEGORY, CHANNEL, STATUS, PRIORITY, RESOLUTION_HOURS
FROM "WP_DB"."SAMPLE"."INQUIRY_LOG" ORDER BY RECEIVED_AT']
補足: クエリはQueryClient側でSELECT/WITH文以外を弾くガードが入っているため、sql属性からDELETE文などを渡しても実行されません。ただしこれはあくまで簡易的なクライアント側チェックであり、本来の安全策はSnowflake側で読み取り専用ロールを使うことです。
参照のみなのでHTTPS/WebAPIでシンプル
SnowflakeにはPHP PDO Driverもあります。ただし今回は参照(SELECT)のみのシンプルな用途なので、SQL API v2を使いました。WordPress標準のwp_remote_post()でJSONをPOSTするだけで、ドライバのインストールも常時接続の管理も不要になります。
認証もPAT(トークン)をAuthorization: Bearerヘッダーに載せるだけなので、鍵ペア(JWT署名)方式に比べて実装はかなり薄くなります。
src/Snowflake/QueryClient.php
$response = wp_remote_post(
'https://' . $account . '.snowflakecomputing.com/api/v2/statements',
array(
'timeout' => 30,
'user-agent' => 'SnowflakeTokenQueryViewer/' . SFTQ_PLUGIN_VERSION, // 既定のUAは391903エラーで拒否される
'headers' => array(
'Authorization' => 'Bearer ' . $settings['token'],
'X-Snowflake-Authorization-Token-Type' => 'PROGRAMMATIC_ACCESS_TOKEN',
'Content-Type' => 'application/json',
),
'body' => wp_json_encode( array(
'statement' => $sql,
'warehouse' => $settings['warehouse'],
'database' => $settings['database'],
'schema' => $settings['schema'],
'role' => $settings['role'],
) ),
)
);
画面描画はHTML/JS
PHP側が返すのはJSON文字列だけです。これを<script id="sfq-data" type="application/json">の中にそのまま差し込みます。
<script id="sfq-data" type="application/json">
[sfq_inquiry_data sql='SELECT INQUIRY_ID, TO_VARCHAR(RECEIVED_AT, 'YYYY-MM-DD HH24:MI:SS') AS RECEIVED_AT,
PRODUCT, CATEGORY, CHANNEL, STATUS, PRIORITY, RESOLUTION_HOURS
FROM "WP_DB"."SAMPLE"."INQUIRY_LOG" ORDER BY RECEIVED_AT']
</script>
JavaScriptでこのJSONを読み込んで<table>の行を組み立てるだけです。最小構成だとこれだけで動きます。
<table border="1">
<thead>
<tr>
<th>ID</th><th>受付日時</th><th>製品</th><th>カテゴリ</th>
<th>チャネル</th><th>ステータス</th><th>優先度</th><th>対応時間(h)</th>
</tr>
</thead>
<tbody id="sfq-tbody"></tbody>
</table>
<script id="sfq-data" type="application/json">
[sfq_inquiry_data sql='SELECT INQUIRY_ID, TO_VARCHAR(RECEIVED_AT, 'YYYY-MM-DD HH24:MI:SS') AS RECEIVED_AT,
PRODUCT, CATEGORY, CHANNEL, STATUS, PRIORITY, RESOLUTION_HOURS
FROM "WP_DB"."SAMPLE"."INQUIRY_LOG" ORDER BY RECEIVED_AT']
</script>
<script>
// JSONを読み込んで<tbody>に1行ずつ追加するだけ
var rows = JSON.parse(document.getElementById('sfq-data').textContent);
var tbody = document.getElementById('sfq-tbody');
rows.forEach(function (r) {
var tr = document.createElement('tr');
tr.innerHTML =
'<td>' + r.inquiryId + '</td>' +
'<td>' + r.receivedAt + '</td>' +
'<td>' + r.product + '</td>' +
'<td>' + r.category + '</td>' +
'<td>' + r.channel + '</td>' +
'<td>' + r.status + '</td>' +
'<td>' + r.priority + '</td>' +
'<td>' + (r.resolutionHours === null ? '' : r.resolutionHours) + '</td>';
tbody.appendChild(tr);
});
</script>
実際にWordPressの固定ページに設置した結果です。
この構成の良いところは、表示側をどれだけ作り込んでも(あるいは作り直しても)、PHP側・プラグイン側には一切手を入れなくていい点です。
発展: Chart.js でグラフ化
同じJSONをそのまま使って、<table>の代わりに<canvas>を置きChart.jsに渡せば、棒グラフ表示に切り替えられます。
<canvas id="sfq-chart"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.4/chart.umd.min.js"></script>
<script>
var rows = JSON.parse(document.getElementById('sfq-data').textContent);
// 受付日ごとに件数を集計
var countsByDay = {};
rows.forEach(function (r) {
var day = r.receivedAt.slice(0, 10);
countsByDay[day] = (countsByDay[day] || 0) + 1;
});
new Chart(document.getElementById('sfq-chart'), {
type: 'bar',
data: {
labels: Object.keys(countsByDay),
datasets: [{ label: '問い合わせ件数', data: Object.values(countsByDay) }]
}
});
</script>
まとめ
表とグラフを出すだけなら、Snowflake連携用のWordPressプラグインは
- 接続情報を管理画面から登録する
- 決まったクエリ(または引数で渡されたクエリ)を実行してJSONを返す
の2つだけで十分でした。表示側の作り込みは全部JavaScriptに任せることで、プラグイン本体のコード量を最小限に抑えられます。BIツールを導入するほどでもない「ちょっとしたデータをサイトに出したい」場面では、この構成が手軽です。


