Movable Type で「最近のブログ記事一覧」をすべてのページで同じように表示するサブテンプレートを作ってみました。 「最近のブログ記事一覧」を表示しようとする場合、インデックステンプレートにおいてはまさにそのブログの最近のブログ記事一覧が表示されますが、アーカイブテンプレート、例えばカテゴリアーカイブにおいては、そのカテゴリに属する最近のブログ記事一覧が表示されてしまいます。 これをすべて...
Movable Type で「最近のブログ記事一覧」をすべてのページで同じように表示するサブテンプレートを作ってみました。
「最近のブログ記事一覧」を表示しようとする場合、インデックステンプレートにおいてはまさにそのブログの最近のブログ記事一覧が表示されますが、アーカイブテンプレート、例えばカテゴリアーカイブにおいては、そのカテゴリに属する最近のブログ記事一覧が表示されてしまいます。
これをすべてのページでインデックステンプレートと同じものを表示させるには、サーバーサイドインクルード(SSI)を使うのが一般的です。しかし、MT 4.2 から使えるようになったテンプレートモジュールのキャッシュを利用すれば何とかなるかも?って思っていろいろやってみたのですが、それは(へたれ)テンプレートモジュール・キャッシュの使い方 (WolaWola)の記事にあるのと同じ考え方でして、結局ダメでした。
だったらなんとかテンプレートをカスタマイズしてやってみよう、と思ったわけです。また、キャッシュを使えば、回りくどいテンプレートになったとしても、それほど再構築時間に影響は出ないでしょうから少し気楽です。
さて、前置きが長くなりましたが、その「最近のブログ記事一覧」のサブテンプレートを解説してみます。
最初に表示結果を見てみましょう。左が通常の「最近のブログ記事一覧」で、右が今回作った「最近のブログ記事一覧」です。
【メインインデックス(トップページ)での表示】
【「 WordPress 」というカテゴリでの表示】
今回のサブテンプレートでは以下の内容を前提として考えました。
まず、最初に完成したソースです。
<mt:SetVar name="lastNumber" value="7">
<mt:TopLevelCategories>
<mt:SetVarBlock name="listCatLabel"><$mt:CategoryLabel$></mt:SetVarBlock>
<mt:Entries category="$listCatLabel" include_subcategories="1" lastn="$lastNumber">
<mt:SetVarBlock name="recentEntryDate"><$mt:EntryDate format="%Y%m%d%H%M"$></mt:SetVarBlock>
<mt:SetVarBlock name="resentEntry" key="$recentEntryDate"><a href="<$mt:EntryPermalink$>"><$mt:EntryTitle$></a></mt:SetVarBlock>
</mt:Entries>
</mt:TopLevelCategories>
<mt:Loop name="resentEntry" sort_by="key reverse">
<$mt:SetVar name="resentEntryLimit" index="$__counter__" value="$__value__"$>
</mt:Loop>
<dt>Recent Entries</dt>
<dd>
<ul class="recentEntries">
<mt:For var="i" from="1" to="$lastNumber">
<li><$mt:GetVar name="resentEntryLimit" index="$i"$></li>
</mt:For>
</ul>
</dd>
<mt:TopLevelCategories>
<mt:SetVarBlock name="listCatLabel"><$mt:CategoryLabel$></mt:SetVarBlock>
<mt:Entries category="$listCatLabel" include_subcategories="1" lastn="7">
<mt:SetVarBlock name="recentEntryDate"><$mt:EntryDate format="%Y%m%d%H%M"$></mt:SetVarBlock>
<mt:SetVarBlock name="resentEntry" key="$recentEntryDate"><a href="<$mt:EntryPermalink$>"><$mt:EntryTitle$></a></mt:SetVarBlock>
</mt:Entries>
</mt:TopLevelCategories>
<dt>Recent Entries</dt>
<dd>
<ul class="recentEntries"><mt:SetVarBlock name="resentEntryLastn"><mt:Loop name="resentEntry" sort_by="key reverse"><li><$mt:GetVar name="__value__"$></li></mt:Loop></mt:SetVarBlock>
<$mt:GetVar name="resentEntryLastn" regex_replace="/(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(.*)/","$1\n$2\n$3\n$4\n$5\n$6\n$7\n"$>
</ul>
</dd>
では順番に解説していきます。
<mt:SetVar name="lastNumber" value="7">
<mt:TopLevelCategories>
<mt:SetVarBlock name="listCatLabel"><$mt:CategoryLabel$></mt:SetVarBlock>
<mt:Entries category="$listCatLabel" include_subcategories="1" lastn="$lastNumber">
<mt:SetVarBlock name="recentEntryDate"><$mt:EntryDate format="%Y%m%d%H%M"$></mt:SetVarBlock>
<mt:SetVarBlock name="resentEntry" key="$recentEntryDate"><a href="<$mt:EntryPermalink$>"><$mt:EntryTitle$></a></mt:SetVarBlock>
</mt:Entries>
</mt:TopLevelCategories>
1 行目の SetVar タグで、変数 lastNumber に 7 を代入しています。この数字を変更することにより、出力件数を変えることができます。
TopLevelCategories タグで囲むことによって、どのアーカイブにいても最上位のカテゴリからリストアップするようにしています。
3 行目の SetVarBlock タグで、変数 listCatLabel にカテゴリ名を代入します。
4 行目の Entries タグで、2 行目の変数 listCatLabel に代入されたカテゴリに属するエントリーをサブカテゴリに属するものも含めて( include_subcategories="1" )、最新 n 件( 1 行目に定義した変数 lastNumber の値)をリストアップします。
ここで最終的に出力したい件数をリストアップしておかないと、同じカテゴリへの投稿が続いた場合に対応できなくなります。
5 行目の SetVarBlock タグで、リストアップされているエントリーの投稿日時を 2008(年)09(月)01(日)05(時)58(分) の表示形式( format="%Y%m%d%H%M" )で変数 recentEntryDate に代入します。
6 行目の SetVarBlock タグで、リストアップされているエントリーについて、5 行目で取得した投稿日時を「キー」( key="$recentEntryDate" )、ブログ記事へのリンクを「値」として連想配列 resentEntry に代入します。
この動作を、すべてのトップレベルカテゴリについて繰り返します。
<mt:Loop name="resentEntry" sort_by="key reverse">
<$mt:SetVar name="resentEntryLimit" index="$__counter__" value="$__value__"$>
</mt:Loop>
次に、「キー」で変数の値を取得できる連想配列(ハッシュ) resentEntry を、「インデックス(添字)」で変数の値を取得できる配列 resentEntryLimit に代入しなおします。
ここではインデックスに、Loop の繰り返し回数( __counter__ )を代入します。
<dt>Recent Entries</dt>
<dd>
<ul class="recentEntries">
<mt:For var="i" from="1" to="$lastNumber">
<li><$mt:GetVar name="resentEntryLimit" index="$i"$></li>
</mt:For>
</ul>
</dd>
最後にここまでにリストアップしたエントリーを出力します。
4 ~ 6 行目の For タグで、配列 resentEntryLimit のインデックス 1 ~ 7 (変数 lastNumber の値)の値を出力します。
<dt>Recent Entries</dt>
<dd>
<ul class="recentEntries"><mt:SetVarBlock name="resentEntryLastn"><mt:Loop name="resentEntry" sort_by="key reverse"><li><$mt:GetVar name="__value__"$></li></mt:Loop></mt:SetVarBlock>
<$mt:GetVar name="resentEntryLastn" regex_replace="/(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(.*)/","$1\n$2\n$3\n$4\n$5\n$6\n$7\n"$>
</ul>
</dd>
次にここまでにリストアップしたエントリーを出力します。
3 行目の SetVarBlock タグと Loop タグで、先ほど作成した連想配列 resentEntry に格納された値( <$mt:GetVar name="__value__"$> )を「キー(=投稿日時)」を基準にソートして新しいものから順に( sort_by="key reverse" )出力し、変数 resentEntryLastn に代入します。
この代入の際に、余計なスペースやタブ、改行のない 1 行で代入するのがポイントです。そうしておかないと、後の regex_replace の正規表現がうまくいかなくなります。
4 行目の GetVar タグで、変数 resentEntryLastn の値を出力します。
このとき、次のような regex_replace モディファイアによって、最新 7 件分を残し、残りを削除しています。
regex_replace="/(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(<li>.+?<\/li>)(.*)/","$1\n$2\n$3\n$4\n$5\n$6\n$7\n"
出力件数を変更するときは、(<li>.+?<\/li>) の個数と $1\n$2\n$3\n$4\n$5\n$6\n$7\n を変更します。「\n」は正規表現で改行を表します。
正規表現のところで、カッコで囲んであるものにマッチしたものは、後ろで「$1」のように「1 番目のカッコでマッチしたもの」というように取り出すことができます。
最後の正規表現のところはあまりきれいなソースではありません。本当は、変数などを使ってスマートに希望出力件数に絞りたかったのですが、なかなかうまくいかなかったので、正規表現を使った力技にしてしまいました。
でもまあ、結果オーライです。
さて、この「最近のブログ記事一覧」を、ブログ記事を投稿するたびにすべてのページに反映させるには、一度すべてのページを再構築しなければなりません。
しかし、このサブテンプレートをすべてのページで再構築してしまうと、再構築時間がこれまでにプラスしてグッと伸びてしまうでしょう。
そこで、MT 4.2 から使えるようになったモジュールのキャッシュを使います。
まず、ブログでモジュールのキャッシュ機能が使えるように設定します。
「設定」>「公開」>「モジュールオプション」で、「テンプレートモジュールをキャッシュする」にチェックを入れればOKです。
次に、テンプレートモジュールの編集画面の「テンプレートの設定」で「作成または更新後に無効にする:」と「ブログ記事」をチェックします。これで、ブログ記事を新規作成または更新したときにモジュールのキャッシュが無効になります。
ブログ記事を保存(再構築)した後、すべてのページを再構築しましょう。
最初にこのエントリーを投稿した時は、出力件数を制御するのに正規表現を使っていましたが、配列を使ってスマートにできたので文章を修正しました。
ハッシュ変数を配列変数に代入しなおすところがポイントだと思います。
以前のエントリーの正規表現を使っている部分は「display: none;」で非表示にしてあるので、正規表現に興味のある方はこちら(ソース、解説)をご覧ください。大した記事ではありませんが。
以上です。