MODxでJSON形式のデータを扱う

MODxで複数ドメイン・複数サイトを管理する方法と問題点」の最後で書いたチャンクを連想配列のように扱う方法を改善して、汎用性の高いJSONを使うようにしてみました。

大量のデータには専用のデータベーステーブルやファイルを用意するのが便利ですが、ちょっとしたデータの場合には、MODxマネージャやテキストエディタからでも編集できるJSONのようなデータ形式の方が便利です。JavaScriptでもそのまますぐに使い回せます。

今回は、JSON形式のデータをチャンクとして用意しました。

チャンク: json.product
{
  "apple"  : { "name": "りんご", "price": "150" },
  "gorilla": { "name": "ゴリラの鼻くそ", "price": "525" },
  "rakko"  : { "name": "らっこの鼻くそ", "price": "525" },
  "emotion": { "name": "この感動は", "price": "priceless" }
}

これをそのままJSONとして出力する場合は、新しいドキュメントを作成し、「ページ設定」の「コンテンツタイプ」を「application/json」にして、次のようにチャンクタグを書きます。

{{json.product}}

あとは、このドキュメントにアクセスするだけでJSONが得られるので、JavaScriptなどから使うことができます(外部からの利用を禁止する場合は、利用を制限するためのスニペットを通します)。

JSONXHTMLに展開するには、テンプレートとなるチャンクと簡単なスニペットを作ってやります(作ってみたスニペットは最後にあります)。

チャンク: tpl.product.item
<tr>
  <th scope="row">[+product+]</th>
  <td>[+product.name+]</td>
  <td>[+product.price+]</td>
</tr>
ドキュメント
<table>
  <caption>商品詳細</caption>
  <thead>
    <tr>
      <th scope="col">キー</th>
      <th scope="col">名称</th>
      <th scope="col">価格</th>
    </tr>
  </thead>
  <tbody>
<!-- 全てのデータを出力する場合 -->
[[JSON? &json=`json.product` &tpl=`tpl.product.item` &prefix=`product`]]

<!-- 単一のデータを出力する場合 -->
<!--
[[JSON? &json=`json.product` &key=`emotion` &tpl=`tpl.product.item` &prefix=`product`]]
-->
  </tbody>
</table>

手軽にまとめて編集できる点や、もっとよい方法が見つかった場合でもデータの移行が簡単にできる点がいいです(たぶん)。

ついでに、次のようなこともできると便利そうなので、今、試しています(結構、面倒くさそうな感じですが…)。最上位のキーのみですができるようにしてみました(@json.seller 808.nameとかはできません)。

チャンク: json.product
{
  "apple": { "name": "りんご", "price": "150", "seller": "@json.seller 808" }
}
チャンク: json.seller
{
  "808": { "name": "八百屋" }
}
チャンク: tpl.product.item
<tr>
  <th scope="row">[+product+]</th>
  <td>[+product.name+][+phx:if=`[+product.seller.name+]`:ne=``:then=`([+product.seller.name+])`+]</td>
  <td>[+product.price+]</td>
</tr>

素直にデータベースを使った方がいいかもしれませんが、いちいちブラウザで編集するのは面倒です。JSONだとすぐに編集できるというメリットが大きいし、データをExcelなどで管理している場合にも、コピー&ペーストするだけなので軽いし、早いです :|

データ同士を関連付ける必要がないのであれば、json.productの中で{{json.seller}}と書いて、チャンクを入れ子にするようにしておけば、JSONデータを結合することができます。

スニペット: JSON

$json_sJson = isset($json) ? trim($json) : '';
$json_sKey = isset($key) ? trim($key) : '*';
$json_sTpl = isset($tpl) ? trim($tpl) : '';
$json_sPrefix = isset($prefix) ? trim($prefix) : $json_sKey;
unset($json, $key, $tpl, $prefix);

// 連想配列を再帰的に走査
if (!function_exists('json_makeHash')) {
    function json_makeHash($aData = '', $sKey = '', $sPrefix = 'data') {
        global $modx;

        $_hash = array();

        $_hash[$sPrefix] = $sKey;    // キー名をセット

        foreach ($aData as $_key => $_value) {
            $_prefix = $sPrefix . '.' . $_key;

            if (is_array($_value)) {    // 多次元配列
                $_hash = array_merge($_hash, json_makeHash($_value, $_key, _prefix));
            } elseif (strpos($_value, '@') === 0
                      && false !== $_separator = strpos($_value, ' ')) {    // バインディング
                $_bind_json = substr($_value, 1, $_separator - 1);
                $_bind_key = trim(substr($_value, $_separator + 1));
                $_bind_data = json_decode($modx->getChunk($_bind_json), true);

                if (isset($_bind_data[$_bind_key])) {
                    $_hash = array_merge($_hash, json_makeHash($_bind_data[$_bind_key], $_bind_key, $_prefix));
                } else {
                    $_hash[$_prefix] = '';
                }
            } else {
                $_hash[$_prefix] = $_value;
            }
        }

        return $_hash;
    }
}

// 連想配列からデータを検索
if (!function_exists('json_find')) {
    function json_find($aData = array(), $sKey = '', $sTpl = '', $sPrefix = '') {
        global $modx;

        $_output = $modx->getChunk($sTpl);

        if ($sKey == '*') {    // 全てのデータ
            $_output = '';

            foreach ($aData as $_key => $_value) {
                $_hash = json_makeHash($_value, $_key, $sPrefix);

                // [+prefix+] -> $_value or $_key
                // [+prefix.key+] -> $_value['<key>'] or <key>
                // [+prefix.key.key2+] -> $_value['<key>']['<key2>'] or <key2>
                // ...
                $_output .= $modx->parseChunk($sTpl, $_hash, '[+', '+]');
            }
        } elseif (isset($aData[$sKey])) {    // 単一のデータ
            $_hash = json_makeHash($aData[$sKey], $sKey, $sPrefix);

            // [+prefix+] -> $aData[$sKey] or $sKey
            // [+prefix.key+] -> $aData[$sKey]['<key>'] or <key>
            // [+prefix.key.key2+] -> $aData[$sKey]['<key>'][<key2>'] or <key2>
            // ...
            $_output = $modx->parseChunk($sTpl, $_hash, '[+', '+]');
        }

        return $_output;
    }
}

// チャンクのJSONをデコード(PHP5)
// PHP4の場合はJSONモジュール/ライブラリを使う。
$json_aData = json_decode($modx->getChunk($json_sJson), true);

return json_find($json_aData, $json_sKey, $json_sTpl, $json_sPrefix);

JSONといいつつ、実際にはほぼ連想配列を処理するスニペットですが、JSONファイルを読み込んだり、JavaScriptからJSONを取得したり、反対にJSONを出力する機能を付けても面白いかもしれません。使い道は少なそうですが… :p

コメント (0)

この記事へのコメントはまだありません。

コメントフォーム

トラックバック (0)

この記事へのトラックバックはまだありません。

この記事のトラックバックURI
http://dxd8.com/archives/112/trackback/
この記事のURI
http://dxd8.com/archives/112/