📃

【Movable Type】複数のコンテンツタイプをまとめて出力する

に公開

概要

MTにおいて、複数のコンテンツタイプ(のデータ)をマージして出力する方法について解説しています。

新着記事一覧に表示したりサイトのRSSフィードとして配信するなど、複数のコンテンツタイプをまとめて出力したいという場面もあるかと思いますが、現状ではMTLoopを利用することで実装可能です。

コンテンツタイプ(のデータ)の出力については、以下記事をご参考ください。

https://zenn.dev/srkkr/articles/b997acca946650

構造

複数のコンテンツタイプをマージするMTMLは、おおまかに以下の構造です。

<!-- 出力したいコンテンツタイプの数だけ<mt:Contents>〜</mt:Contents>を用意する -->
<mt:Contents content_type="[コンテンツタイプAのID]" limit="[num]">
  <$mt:ContentDate format="%Y%m%d%H%M%S" setvar="key"$>
  <mt:SetVarBlock name="cd{$key}">
    <!-- 出力する内容(HTMLの構造は他のコンテンツタイプの出力内容と合わせる) -->
  </mt:SetVarBlock>
</mt:Contents>

<mt:Contents content_type="[コンテンツタイプBのID]" limit="[num]">
  <$mt:ContentDate format="%Y%m%d%H%M%S" setvar="key"$>
  <mt:SetVarBlock name="cd{$key}">
    <!-- 出力する内容(HTMLの構造は他のコンテンツタイプの出力内容と合わせる) -->
  </mt:SetVarBlock>
</mt:Contents>

<!-- 出力表示部分 -->
<mt:Loop name="cd" sort_by="key reverse">
  <mt:SetVar name="count" value="1" op="+">
  <mt:If name="count" le="[num]"><mt:Var name="__value__"></mt:If>
</mt:Loop>

マージしたいコンテンツタイプの数だけMTContentsを記述します。中身はハッシュ(連想配列)としてデータ単位で格納します。
出力はMTLoop内の処理が実際の出力として表示されます。MTIfle="[num]"で指定した数だけ<mt:Var name="__value__">が出力されます。MTContents内のMTSetVarBlockで定義した中身が出力されます。

MTContents内のsetvar="key"keyname="cd{$key}"{$key}name="cd{$key}"cdMTLoopname="cd"cdがそれぞれ対応しています。これらの値は任意のもので構いません。

これらの処理はPHPなどプログラミング言語にある処理と同等のものですが(ハッシュの他に配列もあります)、MTの場合はMTタグとして記述するので少し複雑になっています。

なお、これらについて解説したものが公式のGitHubリポジトリにあるので、こちらもご確認ください。公開が古い日付のものですが、振る舞いは現在のMTでもおおむね同じです。

コード例

以下はRSSフィードとしての出力例です。このコードを例に、できる限りくわしく解説していきます。

なお、コンテンツタイプはいずれも以下で設定しています。例では同じ名称とフィールドを設定していますが、実際には出力するHTMLが同じであれば、コンテンツタイプごと各フィールド項目は違っていても問題ありません。

  • タイトル(テキスト / 必須)
  • 概要(テキスト(複数行))

この例では、以下のことを行っています。

  • コンテンツタイプAとコンテンツタイプBの記事をそれぞれ最新20件ずつ抽出
  • 抽出した記事をマージし、最新順に並べる
  • 最新20件分を実際に出力する
<mt:SetVar name="num" value="20">

<mt:Contents content_type="[コンテンツタイプAのID]" limit="$num">
  <mt:ContentDate format="%Y%m%d%H%M%S" setvar="key">
  <mt:SetVarBlock name="cd{$key}">
    <item>
      <title><mt:ContentField content_field="タイトル"><$mt:ContentFieldValue$></mt:ContentField></title>
      <description><mt:ContentField content_field="概要"><$mt:ContentFieldValue remove_html="1" trim_to="50+..."$></mt:ContentField></description>
      <link><$mt:ContentPermalink$></link>
      <guid><$mt:ContentPermalink$></guid>
      <content:encoded><mt:ContentField content_field="概要"><$mt:ContentFieldValue encode_xml="1"$></mt:ContentField></content:encoded>
      <pubDate><$mt:ContentDate format_name="rfc822"$></pubDate>
    </item>
  </mt:SetVarBlock>
</mt:Contents>

<mt:Contents content_type="[コンテンツタイプBのID]" limit="$num">
  <mt:ContentDate format="%Y%m%d%H%M%S" setvar="key">
  <mt:SetVarBlock name="cd{$key}">
    <item>
      <title><mt:ContentField content_field="タイトル"><$mt:ContentFieldValue$></mt:ContentField></title>
      <description><mt:ContentField content_field="概要"><$mt:ContentFieldValue remove_html="1" trim_to="50+..."$></mt:ContentField></description>
      <link><$mt:ContentPermalink$></link>
      <guid><$mt:ContentPermalink$></guid>
      <content:encoded><mt:ContentField content_field="概要"><$mt:ContentFieldValue encode_xml="1"$></mt:ContentField></content:encoded>
      <pubDate><$mt:ContentDate format_name="rfc822"$></pubDate>
    </item>
  </mt:SetVarBlock>
</mt:Contents>

<mt:Loop name="cd" sort_by="key reverse">
  <mt:If name="__first__">
    <?xml version="1.0" encoding="UTF-8"?>
    <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
      <channel>
        <title><$mt:WebsiteName$></title>
        <description><$mt:WebsiteName$>のRSSフィード</description>
        <link><$mt:WebsiteURL$></link>
  </mt:If>
  <mt:SetVar name="count" value="1" op="+">
  <mt:If name="count" le="$num"><mt:Var name="__value__"></mt:If>
  <mt:If name="__last__">
      </channel>
    </rss>
  </mt:If>
</mt:Loop>

コード例ではMTSetVarBlock内を見やすいように改行・インデント処理していますが、エラーとなり処理がうまくいかない可能性があるため、実際のテンプレートでは改行やインデントはせず1行で記述するようにしてください。

解説:MTSetVar部分

最初に、MTContentsおよびMTLoop内で出力(処理)する記事の数を<mt:SetVar name="num" value="20">で定義します。変数に定義せず、それぞれ個別に数を指定しても構いません。

<mt:SetVar name="num" value="20">

解説:MTContents部分

以下、MTContentsのMTMLです。

<mt:Contents content_type="[コンテンツタイプAのID]" limit="$num">
  <mt:ContentDate format="%Y%m%d%H%M%S" setvar="key">
  <mt:SetVarBlock name="cd{$key}">
    <item>
      <title><mt:ContentField content_field="タイトル"><$mt:ContentFieldValue$></mt:ContentField></title>
      <description><mt:ContentField content_field="概要"><$mt:ContentFieldValue remove_html="1" trim_to="50+..."$></mt:ContentField></description>
      <link><$mt:ContentPermalink$></link>
      <guid><$mt:ContentPermalink$></guid>
      <content:encoded><mt:ContentField content_field="概要"><$mt:ContentFieldValue encode_xml="1"$></mt:ContentField></content:encoded>
      <pubDate><$mt:ContentDate format_name="rfc822"$></pubDate>
    </item>
  </mt:SetVarBlock>
</mt:Contents>

上記のコードは、以下のハッシュ(連想配列)を生成します(わかりやすいようにPHPなどでの表記に合わせています / 以降同様)

["YYMMDDhhmmss" => "<item>(省略)</items>"],
["YYMMDDhhmmss" => "<item>(省略)</items>"],
["YYMMDDhhmmss" => "<item>(省略)</items>"],
.
.
.

<mt:ContentDate format="%Y%m%d%H%M%S" setvar="key">で日時を変数keyとして定義します。ハッシュのkeyの値になります(setvarの値はkey以外でも問題ありません)
keyの値はかならずしも日付である必要はないですが、数字のフォーマットが扱いやすいため、筆者は日付にすることが多いです。

<mt:SetVarBlock name="cd{$key}">〜</mt:SetVarBlock>がハッシュのvalueの値です(見やすいように改行とインデントを入れています)
name="cd{$key}"$keyは、MTContentDateで定義したsetvarと同じものを$をつけて指定します。

以下の記述は同じです。どちらで記述するかはお好みで。
name="cd{$key}"name="cd" key="$key")のcdの値は任意のもので構いません。

<!-- これと -->
<mt:SetVarBlock name="cd{$key}"></mt:SetVarBlock>

<!-- これは同じ -->
<mt:SetVarBlock name="cd" key="$key"></mt:SetVarBlock>

<mt:SetVarBlock name="cd{$key}">〜</mt:SetVarBlock>の中身は出力する中身にあわせて適宜変更してください。この部分は通常のコンテンツタイプの一覧を出力する際の記述と同じです。

同様に、コンテンツタイプB、コンテンツタイプC、とマージしたいコンテンツタイプのMTMLを記述します。注意事項として、ハッシュのkeyvalueに該当する変数の定義部分であるsetvar="key"name="cd{$key}"name="cd" key="$key")は同じ値を指定します。

<!-- 以下はすべてのコンテンツタイプで共通で指定する -->
<$mt:ContentDate format="%Y%m%d%H%M%S" setvar="key"$>
<mt:SetVarBlock name="cd{$key}">
  <!-- (省略) -->
</mt:SetVarBlock>

コンテンツタイプを複数指定することにより、最終的に以下のハッシュ(連想配列)を生成します。

["(コンテンツタイプAの記事)YYMMDDhhmmss" => "(コンテンツタイプAの記事)<item>(省略)</items>"],
["(コンテンツタイプAの記事)YYMMDDhhmmss" => "(コンテンツタイプAの記事)<item>(省略)</items>"],
["(コンテンツタイプAの記事)YYMMDDhhmmss" => "(コンテンツタイプAの記事)<item>(省略)</items>"],
["(コンテンツタイプBの記事)YYMMDDhhmmss" => "(コンテンツタイプBの記事)<item>(省略)</items>"],
["(コンテンツタイプBの記事)YYMMDDhhmmss" => "(コンテンツタイプBの記事)<item>(省略)</items>"],
["(コンテンツタイプBの記事)YYMMDDhhmmss" => "(コンテンツタイプBの記事)<item>(省略)</items>"],
["(コンテンツタイプCの記事)YYMMDDhhmmss" => "(コンテンツタイプCの記事)<item>(省略)</items>"],
["(コンテンツタイプCの記事)YYMMDDhhmmss" => "(コンテンツタイプCの記事)<item>(省略)</items>"],
["(コンテンツタイプCの記事)YYMMDDhhmmss" => "(コンテンツタイプCの記事)<item>(省略)</items>"],
.
.
.

MTContents部分の記述は以上です。

解説:MTLoop部分

以下はMTLoopのMTMLです。MTContentsで生成したハッシュ(連想配列)データを出力します。

<mt:Loop name="cd" sort_by="key reverse">
  <mt:If name="__first__">
    <?xml version="1.0" encoding="UTF-8"?>
    <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
      <channel>
        <title><$mt:WebsiteName$></title>
        <description><$mt:WebsiteName$>のRSSフィード</description>
        <link><$mt:WebsiteURL$></link>
  </mt:If>
  <mt:SetVar name="count" value="1" op="+">
  <mt:If name="count" le="$num"><mt:Var name="__value__"></mt:If>
  <mt:If name="__last__">
      </channel>
    </rss>
  </mt:If>
</mt:Loop>

MTLoopnameモディファイアの値は、MTContents内のMTSetVarBlockで指定したnameモディファイアの値です。
ソートの基準はkey(今回の例ではYYMMDDhhmmss)です。sort_bykeyだけを指定すると昇順で出力されるので、reverseも指定して逆順(降順)で出力するようにします。

MTLoop内ではMTEntriesHeaderのような独自タグが存在しないため、ループ内で最初あるいは最後だけ出力するにはMTIfの予約変数(__first____last__)を利用します。

MTContentsで定義した出力部分は以下です。

<mt:SetVar name="count" value="1" op="+">
<mt:If name="count" le="$num"><mt:Var name="__value__"></mt:If>

MTSetVarで値1を変数countとして定義します。op="+"を指定することで、ハッシュ(連想配列)がひとつ処理されるたびに値が1つずつ追加され、変数countに格納されます。以下のようなイメージです。

count1: いちばんあたらしい記事
count2: 2番目にあたらしい記事
count3: 3番目にあたらしい記事
count4: 4番目にあたらしい記事
count5: 5番目にあたらしい記事
count6: 6番目にあたらしい記事
.
.
.

処理されたもののうち、MTIfの条件(変数countの値が$num以下)に合致するハッシュのvalue(今回の例では<item>〜</item>)が出力されます。

MTLoop部分の解説は以上です。

公式リファレンスなど

本記事では触れませんでしたが、配列を処理するためのMTForというタグもあります。

また、今回はコンテンツタイプを取り上げたためPowerCMSには触れていませんが、PowerCMSでも配列やハッシュ(連想配列)を扱うことが可能です。

Discussion