- 2010-02-13 (土) 17:29
- MovableType


- 「最近の」言うなぁ! - Not Recent TagCloud -
MTDDCのLightningTalkで取り上げた、「最近の言うなぁ」の完全解説版(いや、そんな大仰なものではないはず)です。
ちなみに当日の資料は以下になります。といっても約半分は「自己紹介」だったりします。
View more documents from naoaki onozaki.
やりたい事は、ブログ記事に表示するタグクラウドを「最近の」ではなく「当時の」にする事。
具体的には「そのブログ記事から遡って15エントリーに付けたタグを集計し、登場回数順に30番までのタグをタグクラウドにする」と言う事をやります。
なお、このコードは「Recent(最近の)言わんといて! - WolaWola」での月別・カテゴリー別タグクラウドを作る元になったものだったりします。
と言う訳で遡って15件の解説
「そのブログ記事から遡って」は意外と簡単
実はこれは特別な事は少しも無くて、MT3.xの頃から出来ます。例えばタイトルだったら、こんな感じです。
<p><$MTEntryTitle$></p>
<MTEntryPrevious>
<p><$MTEntryTitle$></p>
<MTEntryPrevious>
<p><$MTEntryTitle$></p>
<MTEntryPrevious>
<p><$MTEntryTitle$></p>
<MTEntryPrevious>
<p><$MTEntryTitle$></p>
</MTEntryPrevious>
</MTEntryPrevious>
</MTEntryPrevious>
</MTEntryPrevious>
これで、遡って合計5件分の記事のタイトルが表示できます。
でも、もう少しスマートに書きたい
でも、さすがに15件分これをやるのはシンドイですし、打ち間違えとかありそうです。
ここで、MTSetVarTemplateタグを使います。
<MTSetVarTemplate name="PreviousTitle">
<MTEntryPrevious>
<p><$MTEntryTitle$></p>
<$MTVar name="PreviousTitle"$>
</MTEntryPrevious>
</MTSetVarTemplate>
自分の中で自分を呼び出しています。MTSetVarTemplateは呼び出された時に、MTタグを処理します。なので、呼び出されるたびにMTEntryPreviousで記事を遡ります。だからこのままだと無限ループになります。
カウンター変数でループ数を制限する
遡る回数をカウンター変数を使って指定します。よくある「i」をカウントダウン方式でやってみます。
<$MTSetVar name="i" value="15"$>
<MTSetVarTemplate name="PreviusTitle">
<MTIf name="i" eq="15">
<$MTEntryTitle$>
</MTIf>
<MTEntryPrevious>
<$MTEntryTitle$>
<MTSetVar name="i" op="--" />
<MTIf name="i" gt="1"><$MTVar name="PreviusTitle"$></MTIf>
</MTEntryPrevious>
</MTSetVarTemplate>
<$MTVar name="PreviusTitle"$>
最初に15回の時を判断しているのは、呼び出しているエントリー自体が対象に入らなくなってしまうからです。この部分をきれいに書きたくて、コードをいじってみたのですが、MTEntryPreviousの処理を再帰呼び出し部分に持っていったら、「MTSetVarTemplateの閉じタグがおかしい」と怒られてしまったので、まわりくどい書き方しています。
実はほとんどの部分は、MTSetVarTemplateしているだけなので、書いただけでは何も出力されません。実際に中身を出力するのは最後の一行。MTVarでMTSetVarTemplateした内容を、呼び出している部分になります。
エントリータグの時のコード
説明を簡単にする為に、MTEntryTitleを使いましたが、エントリータグで書き直します。
ちなみにタグ出力部分に関しては、最新10件の記事で使われているタグのうち5つのタグをタグクラウドで表示 | MovableType.jpを参考にしています。
<MTSetVar name="i" value="15" />
<MTSetVarTemplate name="EntryTagsHash">
<MTEntryTags>
<MTTagName setvar="tagname" />
<MTUnless name="seen{$tagname}">
<MTSetVar name="seen" key="$tagname" value="1" />
<MTSetVarBlock name="tag_htmls{$tagname}">
<li class="rank-<$mt:TagRank max="10"$> widget-list-item"><a href="<$mt:TagSearchLink$>"><$mt:TagName$></a></li>
</MTSetVarBlock>
</MTUnless>
</MTEntryTags>
</MTSetVarTemplate>
<MTSetVarTemplate name="PreviusEntryTag">
<MTIf name="i" eq="15">
<$MTVar name="EntryTagsHash"$>
</MTIf>
<MTEntryPrevious>
<$MTVar name="EntryTagsHash"$>
<MTSetVar name="i" op="--" />
<MTIf name="i" gt="1"><$MTVar name="PreviusEntryTag"$></MTIf>
</MTEntryPrevious>
</MTSetVarTemplate>
<$MTVar name="PreviousEntrytag"$>
<div class="widget-tag-cloud widget">
<h3 class="widget-header">タグクラウド</h3>
<div class="widget-content">
<ul class="widget-list">
<MTLoop name="tag_htmls">
<$MTVar name="__value__"$>
</MTLoop>
</ul>
</div>
</div>
出現頻度30位以下は表示しない方法
出力時にフィルターすれば簡単
このままだと、該当するエントリータグ全てが表示されます。エントリー内での出現回数をカウントするハッシュを追加し、MTLoopでの出力時にフィルター処理して、上位30番までが表示されるようにします。
<MTSetVar name="i" value="15" />
<MTSetVarTemplate name="EntryTagsHash">
<MTEntryTags>
<MTTagName setvar="tagname" />
<MTUnless name="seen{$tagname}">
<MTSetVar name="seen" key="$tagname" value="1" />
<MTSetVar name="cloudcount" key="$tagname" value="1" />
<MTSetVarBlock name="tag_htmls{$tagname}">
<li class="rank-<$mt:TagRank max="10"$> widget-list-item"><a href="<$mt:TagSearchLink$>"><$mt:TagName$></a></li>
</MTSetVarBlock>
<MTElse>
<MTSetVar name="cloudcount{$tagname}" op="++" />
</MTUnless>
</MTEntryTags>
</MTSetVarTemplate>
<MTSetVarTemplate name="PreviusEntryTag">
<MTIf name="i" eq="15">
<$MTVar name="EntryTagsHash"$>
</MTIf>
<MTEntryPrevious>
<$MTVar name="EntryTagsHash"$>
<MTSetVar name="i" op="--" />
<MTIf name="i" gt="1"><$MTVar name="PreviusEntryTag"$></MTIf>
</MTEntryPrevious>
</MTSetVarTemplate>
<$MTVar name="PreviousEntrytag"$>
<div class="widget-tag-cloud widget">
<h3 class="widget-header">タグクラウド</h3>
<div class="widget-content">
<ul class="widget-list">
<MTLoop name="cloudcount" sort_by="value numeric reverse">
<MTIf name="__counter__" le="30">
<$MTVar name="tag_htmls{$__key__}"$>
</MTIf>
</MTLoop>
</ul>
</div>
</div>
cloudcountというタグの出現回数が格納されたハッシュを、値順に並べ替えて上位30個を処理します。出力はハッシュのキー(タグ名です)を、HTMLが格納されている別なハッシュ(tag_htmls)に入れて、値を取り出しています。
でも順番は変えたくないので、ハッシュから削除
上の方法で出力した場合、タグが出現頻度で並び、不自然に見えます。さらに一ひねりして、タグを登録した順番に出力されるようにします。
<MTLoop name="cloudcount" sort_by="value numeric reverse">
<MTIf name="__counter__" gt="30">
<MTSetVar name="delete(cloudcount)" key="$__key__">
</MTIf>
</MTLoop>
<MTLoop name="cloudcount">
<MTIf name="__first__">
<div class="widget-tag-cloud widget">
<h3 class="widget-header">タグクラウド</h3>
<div class="widget-content">
<ul class="widget-list">
</MTIf>
<$MTVar name="tag_htmls{$__key__}"$>
<MTIf name="__last__">
</ul>
</div>
</div>
</MTIf>
</MTLoop>
最初にMTLoopで並べ替えを行い、結果の31個目より先の要素を、ハッシュから削除しています。処理後のハッシュには並べ替えが行なわれていない状態で、出現頻度30位までのものが残ります。
やっとコードの全体
エントリーループの回数を変数化して、変更しやすくします。
<MTSetVar name="MaxEntries" value="15">
<MTSetVarTemplate name="EntryTagsHash">
<MTEntryTags>
<MTTagName setvar="tagname" />
<MTUnless name="seen{$tagname}">
<MTSetVar name="cloudcount" key="$tagname" value="1" />
<MTSetVar name="seen" key="$tagname" value="1" />
<MTElse>
<MTSetVar name="cloudcount{$tagname}" op="++" />
</MTUnless>
<MTSetVarBlock name="tag_htmls{$tagname}"><a href="<$mt:TagSearchLink$>"><$mt:TagName$></a></MTSetVarBlock>
</MTEntryTags>
</MTSetVarTemplate>
<MTSetVarTemplate name="PreviusEntryTag">
<MTIf name="i" eq="$MaxEntries">
<$MTVar name="EntryTagsHash"$>
</MTIf>
<MTEntryPrevious>
<MTVar name="EntryTagsHash" />
<MTSetVar name="i" op="--" />
<MTIf name="i" gt="1"><$MTVar name="PreviusEntryTag"$></MTIf>
</MTEntryPrevious>
</MTSetVarTemplate>
<$MTSetVar name="i" value="$MaxEntries"$>
<$MTVar name="PreviusEntryTag"$>
<MTLoop name="cloudcount" sort_by="value numeric reverse">
<MTIf name="__first__"><MTSetVar name="maxcount" value="$__value__"></MTIf>
<MTIf name="__counter__" gt="30">
<MTSetVar name="delete(cloudcount)" key="$__key__">
<MTElse>
<MTSetVar name="mincount" value="$__value__">
</MTIf>
</MTLoop>
<MTLoop name="cloudcount">
<MTIf name="__first__">
<div class="widget-tag-cloud widget">
<h3 class="widget-header">タグクラウド</h3>
<div class="widget-content">
<ul class="widget-list">
</MTIf>
<li class="level<?php
echo 6-floor((log10(<$MTVar name="__value__"$>)-log10(<$MTVar name="mincount"$>))/(log10(<$MTVar name="maxcount"$>)-log10(<$MTVar name="mincount"$>))*5);
?>"><$MTVar name="tag_htmls{$__key__}"$></li>
<MTIf name="__last__">
</ul>
</div>
</div>
</MTIf>
</MTLoop>
実はタグのサイズ決定のみPHPで行なっている
一箇所だけここまでで説明していない部分があります。タグのサイズ決定に該当エントリー範囲でのみの出現頻度を使っている部分です。<$MTTagRank$>を使うと、ブログ全体でのタグの出現頻度を元にサイズ(厳密にはサイズ指定の為のクラス)が決定されます。
最後のMTLoop内での値(<$MTVar name="__value__"$>)が出現回数です。これと出現する最大値<$MTVar name="maxcount"$>・最小値<$MTVar name="mincount"$>からサイズを決定します。これについてはMTの変数処理のみで処理する方法もあると思いますが、試行錯誤した(実際に出力して様子を見た)結果、PHPのスクリプトで処理する事にしました。いろいろ解決方法はあると思うので、この部分はよりよい方法があったら教えてもらいたいなと思います。きっと突っ込みどころはイッパイあると思うので、バシバシツッコミよろしくお願いいたします。
という訳で、ここのブログ記事でのタグクラウドには、上記のコードが使われています。もしよかったら古いブログ記事を確認してみてください。その記事を書いた時によく使っていたタグが表示されているはずです。
- Newer >: iMTとCMSContext
- < Older: MTDDCのまじめなレポート
ping a Trackback
- TrackBack URL for this entry.(1)




スクリプトの読み込みが完了していません。
メールフォーム(Javascriptを使用していません)や、
Twitter経由のCommentでもReplyいたします。ただし返信はブログのコメントとして行う事もあります。