dTree を Movable Type 4.1/MTOS のネイティブタグで実装する

dTree を Movable Type 4.1/MTOS のネイティブタグで実装する

Posted at December 20,2007 1:55 AM
Tag:[Customize, dTree, MovableType]

Movable Type 4.1/MTOS で変数の算術演算子と配列およびハッシュが実装されたことで、PHPあるいはJavaScriptとの組み合わせが必要だった処理が、ネイティブなタグのみで実装可能になりました。

全てのコードについて移植が可能であるかどうかは不明ですが、どの程度の威力があるか検証してみたかったので、サンプルとして、以前紹介した dTree のカスタマイズについてテンプレートタグで実装してみました。

1.dTree について

機能や設定方法は下記の記事を参照願います(実際にご利用になる場合、この記事の4項までの設定が必要です)。

dTree によるサブカテゴリーリスト for Movable Type

で、元記事で紹介したPHP版のスクリプトは下記の通りです(若干直しました)。

<script type="text/javascript">
d = new dTree('d');
d.config.useCookies=false;
d.add(0, -1,'Categories','javascript: void(0);');
<?php $cat_number = 0; $level = 0; $tree = array(); ?>
<MTTopLevelCategories>
<?php $tree[$level] = ++$cat_number; if(!$level) { ?>
    d.add(<?php echo $cat_number ?>, 0,'<$MTCategoryLabel encode_php="1"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
<?php } else { ?>
    d.add(<?php echo $cat_number ?>, <?php echo $tree[$level - 1] ?>,'<$MTCategoryLabel encode_php="1"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
<?php } $level++; ?>
<MTSubCatsRecurse max_depth="3">
<?php $level--; ?>
</MTTopLevelCategories>
document.write(d);
</script>
<p style="text-align:center"><a href="javascript: d.openAll();">open all</a> | <a href="javascript: d.closeAll();">close all</a></p>

再掲になりますが、dTree によるサブカテゴリーリストの完成イメージは次の通りです。

dTree によるサブカテゴリーリスト

この表示を行うための JavaScript は次の内容になっています(上記のPHPスクリプトから生成されたものです)。

<script type="text/javascript">
d = new dTree('d');
d.config.useCookies=false;
d.add(0, -1,'Categories','javascript: void(0);');
d.add(1, 0,'test1','http://.../test/');
d.add(2, 1,'test1-1','http://.../test/test11/');
d.add(3, 2,'test1-1-1','http://.../test/test11/test111/');
d.add(4, 2,'test1-1-2','http://.../test/test11/test112/');
d.add(5, 1,'test1-2','http://.../test/test12/');
d.add(6, 5,'test1-2-1','http://.../test/test12/test121/');
d.add(7, 5,'test1-2-2','http://.../test/test12/test122/');
d.add(8, 0,'test2','http://.../test2/');
d.add(9, 8,'test2-1','http://.../test2/test21/');
document.write(d);
</script>
<p style="text-align:center"><a href="javascript: d.openAll();">open all</a> | <a href="javascript: d.closeAll();">close all</a></p>

スクリーンショットと JavaScript(のカテゴリー)の対応はなんとなくお分かりになると思いますが、

d.add(...) 

の行で、カテゴリー部分を表示させています。

本エントリーでは、Movable Type のネイティブタグのみで、このカテゴリーを表示する部分の JavaScript を生成します。

2.dTree のカテゴリー表示部分のフォーマット

先程も簡単に紹介しましたが、JavaScript でカテゴリーを表示する部分は add 関数がキモとなっており、関数のパラメータは、

d.add([*1],[*2],[カテゴリー名],[カテゴリーのURL *3]);
*1:カテゴリーに一意に割り当てられたdTree用のカテゴリー番号
*2:そのカテゴリーが属する親カテゴリーの番号
"0"はdTreeのルートに属する場合(=最上位のカテゴリー)
*3:dTreeの生成するa要素のhref属性になります。

となっています。

上記の add 関数部分を、サブカテゴリーリストを出力するサブテンプレートを応用して作成します。以下、add 関数の各パラメータをどのようなロジックで作成するかという解説を交えながら、ネイティブなテンプレートを組み立てていきます。

3.各パラメータ用のデータ生成方法

3.1 第1パラメータ用のデータ生成

第1パラメータはカテゴリーに番号を順に付与していけば良いだけので、リスト1のように、

リスト1

<$mt:setvar name="cat_number" value="1"$>
<MTTopLevelCategories>
  <$mt:setvar name="cat_number" op="++"$>
  d.add(<$mt:getvar name="cat_number"$>,~略~);
  <$MTSubCatsRecurse$>
</MTTopLevelCategories>

とすれば良いことが分かります。

1行目でカテゴリー番号保持用の変数(cat_number)を1で初期化し、MTTopLevelCategories タグのループ内でインクリメントしていきます。
そしてインクリメント後に add 関数の第1パラメータに表示させればOKです。

3.2 第2パラメータ用のデータ生成

第2パラメータの、自カテゴリーの属する親カテゴリー番号を算出する方法は色々なアプローチがあると思いますが、ここでの方法は、まず算出に必要なデータとして、現在処理されているカテゴリーの階層をインデックスとした自カテゴリー番号を、配列の変数として保持します。

「カテゴリーの階層」は、親カテゴリーの階層は0、子カテゴリーの階層は1、孫カテゴリーの階層は2...、という具合に、数値で表すこととします。

「カテゴリーの階層をインデックスとした自カテゴリー番号を、配列の変数として保持」とは、例えば一番最初の「カテゴリー1」を処理している時、このカテゴリーはルート階層(数値で表すと0)に属するので、

tree[0]=1

という変数を作成することを指しています。左辺の変数 tree のインデックス0が階層の番号を示し、右辺の1がカテゴリー番号を示します。

「カテゴリー2」を処理している時、このカテゴリーは親カテゴリー(数値で表すと1)に属するカテゴリーなので、

tree[1]=2

という変数を作成します。

この規則によって、各カテゴリーを処理する時には、刻々と内容が書き換わる、次のような配列が作成されます。

カテゴリー1の処理中:tree[0]=1
カテゴリー2の処理中:tree[1]=2
カテゴリー3の処理中:tree[2]=3
カテゴリー4の処理中:tree[2]=4
カテゴリー5の処理中:tree[1]=5
カテゴリー6の処理中:tree[2]=6
カテゴリー7の処理中:tree[2]=7
カテゴリー8の処理中:tree[0]=8
カテゴリー9の処理中:tree[1]=9

少し話が難しくなりますが、このデータは次回の繰り返し処理で利用するためのものです。つまり、カテゴリー1の処理中に作成された、一番最初の、

tree[0]=1

は(MTTopLevelCategories の)繰り返し処理の中でそのまま保持されており、次のカテゴリー2の処理の時に利用される、ということだけをとりあえず覚えていてください。

前置きが長くなりましたが、上記のデータを作成するサブテンプレートは、リスト2のようになります(青色はリスト1からの追加部分)。

リスト2

<$mt:setvar name="cat_number" value="1"$>
<$mt:setvar name="level" value="1"$>
<MTTopLevelCategories>
  <$mt:setvar name="cat_number" op="++"$>
  <$mt:setvar name="tree[$level]" value="$cat_number"$>
  d.add(<$mt:getvar name="cat_number"$>,~略~);
  <$MTSubCatsRecurse$>
  <$mt:setvar name="level" op="++"$>
  <$MTSubCatsRecurse$>
  <$mt:setvar name="level" op="--"$>
</MTTopLevelCategories>

2行目で配列インデックス用の変数levelを初期化し、5行目で、処理中のカテゴリー番号を、現在の階層をインデックスとした配列データに保持します。

階層の数値の増減は、MTSubCatsRecurse の直前と直後で操作しています。

プログラミングに不慣れな方には分かりにくいのですが、MTSubCatsRecurseはいわゆる再帰処理を行うためのタグで、現在処理しているカテゴリーに子カテゴリーが存在する場合、MTSubCatsRecurse の内容を取り出し、 MTTopLevelCategories の中で処理します。

つまり MTSubCatsRecurse は次の階層を処理するための再帰データとなるので、その直前で変数 level をインクリメントすることで、階層用のデータがインクリメントされます。

また、MTSubCatsRecurse の直後でデクリメントすれば、階層の数値を親カテゴリーの階層に戻せます。
さらに MTSubCatsRecurse の中に孫カテゴリーデータが存在すれば、変数 level はさらにインクリメントされることになります。

これでdTreeに必要なデータが揃いましたので、あとは子カテゴリーが属する親カテゴリーのカテゴリー番号を求めるだけです。
これは、現在処理されているデータの階層からデクリメントした配列の値、つまり自カテゴリーの上位の階層のカテゴリー番号を取得すればいい訳です。

言い換えると、例えば2行目の、

tree[1]=2

の親カテゴリー番号を求める場合、上位の階層は

tree[0]

であり、その中に親のカテゴリー番号が設定されているので、配列のインデックスをデクリメント(-1)、つまり、

tree[1-1]

とすれば、tree[0] が求まります。あとはそこに設定されているカテゴリー番号1を取得すれば良い訳です。

また、4行目の

tree[2]=4

の親カテゴリーを求める場合、上位の階層は

tree[1]

となり、その中にカテゴリー番号が設定されているので、インデックスをデクリメント(-1)、つまり

tree[2-1]

とすれば、tree[1] が求まるので、あとはそこに設定されているカテゴリー番号2を取得すれば良い訳です。

話をまとめると、リスト3のようになります(青色はリスト2からの追加部分)。

リスト3

<$mt:setvar name="cat_number" value="1"$>
<$mt:setvar name="level" value="1"$>
<MTTopLevelCategories>
  <$mt:setvar name="cat_number" op="++"$>
  <$mt:setvar name="tree[$level]" value="$cat_number"$>
  <mt:if name="level" eq="1">
    d.add(<$mt:getvar name="cat_number"$>, 0,'<$MTCategoryLabel escape="js"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
  <mt:else>
    d.add(<$mt:getvar name="cat_number"$>, <$mt:setvar name="tmp" value="$level"$><$mt:setvar name="tmp" op="--"$><$mt:getvar name="tree[$tmp]"$>,'<$MTCategoryLabel escape="js"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
  </mt:if>
  <$mt:setvar name="level" op="++"$>
  <$MTSubCatsRecurse$>
  <$mt:setvar name="level" op="--"$>
</MTTopLevelCategories>

追加した6~10行目のうち、9行目が該当します。
第2パラメータの値に配列変数treeのデータを表示しています。

6行目の MTIf で処理を振り分けている理由ですが、親カテゴリーを処理している場合、さらに上位の階層(つまり dTree のルート階層)データを取得しようとしますが、これまでの処理では設定されていないデータなので、エラーを避けるため、その場合のみ、7行目で第2パラメータに固定で0を設定するようにしています。

3.3 第3パラメータ用のデータ生成

第3パラメータのカテゴリー名には、<$MTCategoryLabel escape="js"$> を記述するだけです。なお、JavaScript として正常に動作するよう、escape モディファイアに "js" を指定します。

3.4 第4パラメータ用のデータ生成

第4パラメータには、リスト4のようにカテゴリーのリンク、

リスト4

<MTIfNonZero tag="MTCategoryCount">
<$MTCategoryArchiveLink$>
<MTElse>
javascript: void(0);
</MTElse>
</MTIfNonZero>

を設定します。

カテゴリーにブログ記事が1件以上投稿されている場合は、カテゴリーアーカイブへのリンクを設定し、投稿されていなければリンクの代わりに "javascript: void(0);" を設定し、リンクをクリックしてもページ遷移が発生しないようにしています。

最後に、第1パラメータと第2パラメータに表示される値をデクリメントします(青色はリスト3からの追加部分)。

リスト5

<$mt:setvar name="cat_number" value="1"$>
<$mt:setvar name="level" value="1"$>
<MTTopLevelCategories>
  <$mt:setvar name="cat_number" op="++"$>
  <$mt:setvar name="tree[$level]" value="$cat_number"$>
  <mt:if name="level" eq="1">
    d.add(<$mt:getvar name="cat_number" op="--"$>, 0,'<$MTCategoryLabel escape="html"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
  <mt:else>
    d.add(<$mt:getvar name="cat_number" op="--"$>, <$mt:setvar name="tmp" value="$level"$><$mt:setvar name="tmp" op="--"$><$mt:getvar name="tree[$tmp]" op="--"$>,'<$MTCategoryLabel escape="html"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
  </mt:if>
  <$mt:setvar name="level" op="++"$>
  <$MTSubCatsRecurse$>
  <$mt:setvar name="level" op="--"$>
</MTTopLevelCategories>

これは変数cat_numberと変数levelの初期値を1にしているため、デクリメントしないと下のように、カテゴリー番号が2から開始されるためです。

d.add(2, 0,'test1','http://.../test/');
d.add(3, 2,'test1-1','http://.../test/test11/');
d.add(4, 3,'test1-1-1','http://.../test/test11/test111/');
d.add(5, 3,'test1-1-2','http://.../test/test11/test112/');
d.add(6, 2,'test1-2','http://.../test/test12/');
d.add(7, 6,'test1-2-1','http://.../test/test12/test121/');
d.add(8, 6,'test1-2-2','http://.../test/test12/test122/');
d.add(9, 0,'test2','http://.../test2/');
d.add(10, 9,'test2-1','http://.../test2/test21/');

変数cat_numberと変数levelの初期値を1にしているのは、初期値0の場合にリスト系のテンプレートタグで正常にインクリメントされない不具合を避けるためです。

これにdTree用の前後のスクリプトを加えれば完成です(青色はリスト5からの追加部分)。

リスト6

<script type="text/javascript">
d = new dTree('d');
d.config.useCookies=false;
d.add(0, -1,'Categories','javascript: void(0);');
<$mt:setvar name="cat_number" value="1"$>
<$mt:setvar name="level" value="1"$>
<MTTopLevelCategories>
<$mt:setvar name="cat_number" op="++"$>
<$mt:setvar name="tree[$level]" value="$cat_number"$>
<mt:if name="level" eq="1">
  d.add(<$mt:getvar name="cat_number" op="--"$>, 0,'<$MTCategoryLabel escape="html"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
<mt:else>
  d.add(<$mt:getvar name="cat_number" op="--"$>, <$mt:setvar name="tmp" value="$level"$><$mt:setvar name="tmp" op="--"$><$mt:getvar name="tree[$tmp]" op="--"$>,'<$MTCategoryLabel escape="html"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
</mt:if>
<$mt:setvar name="level" op="++"$>
<$MTSubCatsRecurse$>
<$mt:setvar name="level" op="--"$>
</MTTopLevelCategories>
document.write(d);
</script>
<p style="text-align:center"><a href="javascript: d.openAll();">open all</a> | <a href="javascript: d.closeAll();">close all</a></p>

よりエレガントな実装があると思いますが、その点はご容赦ください。

関連記事:dtree のサブカテゴリーリストにブログ記事タイトルを表示する for Movable Type

関連記事
トラックバックURL


コメント

MTを始めたばかりで、こちらのサイトや書籍を参考にさせていただいております。
ブログの作成で大変助かっております。

1つご質問があるのですが、
今回紹介していただいているdTree によるサブカテゴリーリストに
各カテゴリ毎の記事数を表示したいのですが可能でしょうか。
この場合、<$MTCategoryCount$>は使えないのかなと思い悩んでおります。

できれば対応方法をご教授いただければ幸いです。

よろしくお願いします。

[1] Posted by backpacker logo : March 24, 2008 12:12 AM

>backpackerさん
こんばんは。
ご返事遅くなりすいません。
ご質問の件ですが、下記で表示されます。

<script type="text/javascript">
d = new dTree('d');
d.config.useCookies=false;
d.add(0, -1,'Categories','javascript: void(0);');
<$mt:setvar name="cat_number" value="1"$>
<$mt:setvar name="level" value="1"$>
<MTTopLevelCategories>
<$mt:setvar name="cat_number" op="++"$>
<$mt:setvar name="tree[$level]" value="$cat_number"$>
<mt:if name="level" eq="1">
  d.add(<$mt:getvar name="cat_number" op="--"$>, 0,'<$MTCategoryLabel escape="html"$> [<$MTCategoryCount$>]','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
<mt:else>
  d.add(<$mt:getvar name="cat_number" op="--"$>, <$mt:setvar name="tmp" value="$level"$><$mt:setvar name="tmp" op="--"$><$mt:getvar name="tree[$tmp]" op="--"$>,'<$MTCategoryLabel escape="html"$>  [<$MTCategoryCount$>]','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
</mt:if>
<$mt:setvar name="level" op="++"$>
<$MTSubCatsRecurse$>
<$mt:setvar name="level" op="--"$>
</MTTopLevelCategories>
document.write(d);
</script>
<p style="text-align:center"><a href="javascript: d.openAll();">open all</a> | <a href="javascript: d.closeAll();">close all</a></p>

それではよろしくお願い致します。

[2] Posted by yujiro logo : March 31, 2008 1:19 AM

お忙しいところコードまで教えていただきありがとうございます!
カテゴリ毎の記事数を表示することができました!

本当にありがとうございました。

[3] Posted by backpacker logo : March 31, 2008 10:53 AM

>backpackerさん
こんにちは。
ご連絡ありがとうございました。
うまくできたようで良かったです。
ではでは!

[4] Posted by yujiro logo : April 4, 2008 3:02 PM
コメントする
greeting

*必須

*必須(非表示)


ご質問のコメントの回答については、内容あるいは多忙の場合、1週間以上かかる場合があります。また、すべてのご質問にはお答えできない可能性があります。予めご了承ください。

太字イタリックアンダーラインハイパーリンク引用
[サインインしない場合はここにCAPTCHAを表示します]

コメント投稿後にScript Errorや500エラーが表示された場合は、すぐに再送信せず、ブラウザの「戻る」ボタンで一旦エントリーのページに戻り(プレビュー画面で投稿した場合は、投稿内容をマウスコピーしてからエントリーのページに戻り)、ブラウザをリロードして投稿コメントが反映されていることを確認してください。

コメント欄に(X)HTMLタグやMTタグを記述される場合、「<」は「&lt;」、「>」は「&gt;」と入力してください。例えば「<$MTBlogURL$>」は「&lt;$MTBlogURL$&gt;」となります(全て半角文字)