今担当しているPowerCMS案件で「イベントカレンダーを実装したい」との要望がありました。1日1記事であればシンプルですが、繰り返しの予定に対応させる必要があり、まずはテンプレートタグの設計だけで実現できるのかを探りました。
繰り返しの予定の登録は以下のように2通りの条件があります。
- 予定の終了日まで毎日繰り返し予定を表示する(カスタムフィールドEntryEndDateに終了日を指定する)
- 予定の終了日までの間のうち、指定の曜日だけ繰り返し表示する(カスタムフィールドEntryTargetDayOfWeekで該当の曜日にチェックを入れる)
カレンダーは今回MTCalendarタグと月別アーカイブテンプレートを用いて静的に出力しました。(最近だとJSONで予定を出力して、JavaScriptで表示処理をするというのが多いかもしれませんね。)
結論としては、以下のようなテンプレートで予定のHTMLを組み上げることができました。
<mt:Entries>
<mt:EntryDate format="%Y" setvar="year" />
<mt:EntryDate format="%m" setvar="month" />
<mt:EntryDate format="%e" setvar="day" />
<mt:Var name="day" replace=" ","" setvar="day" />
<mt:Ignore>記事公開日の予定を追加</mt:Ignore>
<mt:SetVarBlock name="$eventdata_varname" key="$day" append="1"><p><a href="<mt:EntryPermalink absolute="1" escape="html" />" class="linkUnderline"><mt:EntryTitle escape="html" /></a></p></mt:SetVarBlock>
<mt:Ignore>連続した予定・繰り返しの予定の追加</mt:Ignore>
<mt:SetVar name="exist_cf_value_check" value="" /><mt:Ignore>曜日指定の有無をチェックするため、一旦変数に入れる為の準備</mt:Ignore>
<mt:EntryTargetDayOfWeek><mt:SetVar name="exist_cf_value_check" value="$__value__" append="1"></mt:EntryTargetDayOfWeek>
<mt:If name="exist_cf_value_check">
<mt:Ignore>曜日指定の予定の処理</mt:Ignore>
<mt:EntryEndDate format="%e" setvar="last_day" />
<mt:Var name="last_day" replace=" ","" setvar="last_day" />
<mt:If name="last_day" eq="">
<mt:Ignore>終了日の指定がない場合は31日までループを回す</mt:Ignore>
<mt:SetVar name="last_day" value="31" />
</mt:If>
<mt:Var name="day" op="++" setvar="from_day" />
<mt:Ignore>月末までループを回し曜日が一致したら予定のHTMLを追加</mt:Ignore>
<mt:For var="x" from="$from_day" to="$last_day">
<mt:SetVarBlock name="check_date"><mt:Var name="year" /><mt:Var name="month" /><mt:If name="x" lt="10">0<mt:Var name="x" /><mt:Else><mt:Var name="x" /></mt:If></mt:SetVarBlock>
<mt:Date ts="$check_date" format="%a" setvar="day_of_week">
<mt:EntryTargetDayOfWeek>
<mt:If name="__value__" eq="$day_of_week">
<mt:SetVarBlock name="$eventdata_varname" key="$x" append="1"><p><a href="<mt:EntryPermalink absolute="1" escape="html" />" class="linkUnderline"><mt:EntryTitle escape="html" /></a></p></mt:SetVarBlock>
</mt:If>
</mt:EntryTargetDayOfWeek>
</mt:For>
<mt:ElseIf tag="EntryEndDate">
<mt:Ignore>連続した予定の処理</mt:Ignore>
<mt:EntryEndDate format="%e" setvar="last_day" />
<mt:Var name="last_day" replace=" ","" setvar="last_day" />
<mt:Var name="day" op="++" setvar="from_day" />
<mt:For var="x" from="$from_day" to="$last_day">
<mt:SetVarBlock name="$eventdata_varname" key="$x" append="1"><p><a href="<mt:EntryPermalink absolute="1" escape="html" />" class="linkUnderline"><mt:EntryTitle escape="html" /></a></p></mt:SetVarBlock>
</mt:For>
</mt:If>
</mt:Entries>
変数に格納した予定のHTMLはMTCalendarの中で展開します。日付がキーになっているので簡単ですね。
テンプレートの解説
日付をキーにしたハッシュに予定のHTMLを足していくのがポイントです。その上で次のようなロジックを考えました。
- 記事公開日で指定した日の予定をハッシュに格納する
- ループを回し繰り返しの予定をハッシュに格納する
- 曜日指定の予定
- 連続した予定
あとは、上記ロジックをテンプレートタグに落とし込んでいきました。ループを回したり曜日を判定したりするために日付の処理が必要で、それに関係する変数のタグが多くなりました。
懸念点
- 予定(記事)が増えるほどループの回数が増えます。入力の簡便性を考え曜日指定の予定を組み立てる処理において終了日の指定がない場合は31日までループを回すようにしていますが、終了日は必ず入力するという仕様にした方が再構築の負荷が最小限に抑えられるのではないかと思います。
- MTCalendarにはキャッシュ機能が備わっており、アーカイブマッピング設定と再構築の仕方によっては意図しない結果が返ることがあります。(monthモディファイアとcategoryモディファイアを使ってキャッシュのキーを生成している)