「WordPressで学ぶPHP(2)データ構造(配列・オブジェクト)編」を発売しました。
本書は「WordPressで学ぶPHP(1)変数・制御構造編」の続編にあたり、PHPの「データ構造」(配列とオブジェクト)について解説します。
配列やオブジェクトは、頭の中で考えるだけでは、イメージがつかみにくいです。本書では図を多用して、配列やオブジェクトをなるべく分かりやすく解説することを心がけました。 
Kindle本で、定価250円です。
カテゴリアーカイブページのテーブル表示
MTQに「カテゴリが3階層あるときに、親カテゴリのカテゴリアーカイブページに、子/孫カテゴリの記事をテーブルにして表示したい」といった内容の質問がありました。
この質問に答えます。
1.質問の概要
親カテゴリのアーカイブページを以下のように表示したいということです。
- 子カテゴリごとにテーブルを出力します。
- 個々の表には、子カテゴリとその下の孫カテゴリの記事を出力します。
- 表には「孫カテゴリ」の列を作り、孫カテゴリ名を出力します。ただし、td要素にrowspan属性を指定して、孫カテゴリ名は1つの孫カテゴリにつき1回だけ出力します。
- 子カテゴリの記事は、td要素にcolspan属性を指定して、「孫カテゴリ」の列も使って表示します。
たとえば、カテゴリとブログ記事の構造が以下のようになっているとします。
親カテゴリ ├子カテゴリA │└孫カテゴリA-1 │ ├記事タイトル1 │ └記事タイトル2 └子カテゴリB │├記事タイトル1 │└記事タイトル2 ├孫カテゴリB-1 │ ├記事タイトル1 │ ├記事タイトル2 │ └記事タイトル3 └孫カテゴリB-2 ├記事タイトル1 ├記事タイトル2 └記事タイトル3
この時に、以下のような感じで出力します。
| 孫カテゴリ | タイトル | 日付 | 
| 孫カテゴリA-1 | 記事タイトル1 | YYYY/mm/dd | 
| 記事タイトル2 | YYYY/mm/dd | 
| 孫カテゴリ | タイトル | 日付 | 
| 記事タイトル1 | YYYY/mm/dd | |
| 記事タイトル2 | YYYY/mm/dd | |
| 孫カテゴリB-1 | 記事タイトル1 | YYYY/mm/dd | 
| 記事タイトル2 | YYYY/mm/dd | |
| 記事タイトル3 | YYYY/mm/dd | |
| 孫カテゴリB-2 | 記事タイトル1 | YYYY/mm/dd | 
| 記事タイトル2 | YYYY/mm/dd | |
| 記事タイトル3 | YYYY/mm/dd | |
2.MTSubCategoriesタグの基本
あるカテゴリのコンテキストで、その子以下のカテゴリやブログ記事などの情報を出力したいときには、MTSubCategoriesタグを使います。
たとえば、親カテゴリのアーカイブページに、子や孫のカテゴリやブログ記事の情報を出力するときに、MTSubCategoriesタグを使います。
MTSubCategoriesタグは、基本的には以下のような組み方をします。
<mt:SubCategories>
  <mt:SubCatIsFirst>
    同一階層カテゴリの最初のカテゴリのときだけ出力する内容
  </mt:SubCatIsFirst>
  カテゴリの情報/そのカテゴリに属するブログ記事の情報を出力
  <$mt:SubCatsRecurse$>
  <mt:SubCatIsFirst>
    同一階層カテゴリの最後のカテゴリのときだけ出力する内容
  </mt:SubCatIsFirst>
</mt:SubCategories>
カテゴリに下の階層のカテゴリがあると、MTSubCatsRecurseタグのところで階層を1段下って、MTSubCategoriesブロックの先頭に戻り、下った階層の情報を順に出力できます。
また、下の階層の情報をすべて出力し終わると、階層を1段戻って、MTSubCatsRecurseタグの次の行から処理が再開されます。
2.階層ごとに出力を変える
MTSubCategoriesタグのブロックの中で、カテゴリの階層に応じて出力を変えたい場合もよくあります。
この場合、カテゴリの階層を表す変数を用意して、その変数の値で条件判断するようにします。
具体的には、以下のようにテンプレートを組みます。
なお、第一階層、第二階層・・・は、現在のカテゴリの子が第一階層、孫が第二階層・・・と考えます。
たとえば、親カテゴリのアーカイブページの中では、子カテゴリが第一階層、孫カテゴリが第二階層にあたります。
<$mt:SetVar name="depth" value="1"$>
<mt:SubCategories>
  <mt:SubCatIsFirst>
    <mt:If name="depth" eq="1">
      第一階層カテゴリの最初のカテゴリのときだけ出力する内容
    <mt:Else name="depth" eq="2">
      第二階層カテゴリの最初のカテゴリのときだけ出力する内容
    ・・・
    </mt:If>
  </mt:SubCatIsFirst>
  <mt:If name="depth" eq="1">
    第一階層カテゴリの情報や、その階層のブログ記事の情報を出力
  <mt:Else name="depth" eq="2">
    第二階層カテゴリの情報や、その階層のブログ記事の情報を出力
  ・・・
  </mt:If>
  <$mt:SetVar name="depth" op="++"$>
  <$mt:SubCatsRecurse$>
  <$mt:SetVar name="depth" op="--"$>
  <mt:If name="depth" eq="1">
    第一階層カテゴリの情報や、その階層のブログ記事の情報を出力し終わった後に、1回だけ出力する内容
  <mt:Else name="depth" eq="2">
    第二階層カテゴリの情報や、その階層のブログ記事の情報を出力し終わった後に、1回だけ出力する内容
  ・・・
  </mt:If>
  <mt:SubCatIsLast>
    <mt:If name="depth" eq="1">
      第一階層カテゴリの最後のカテゴリのときだけ出力する内容
    <mt:Else name="depth" eq="2">
      第二階層カテゴリの最後のカテゴリのときだけ出力する内容
    ・・・
    </mt:If>
  </mt:SubCatIsLast>
</mt:SubCategories>
MTSubCategoriesタグのブロックの前で、変数depthに1を代入します。
そして、MTSubCatsRecurseタグの前で変数depthの値を+1し、タグの後で-1します。
このようにすることで、変数depthがカテゴリの深さを表すようになります。
後は、変数depthの値で条件判断して、階層に応じた出力を行うようにします。
3.事例を当てはめる
次に、2.で挙げたテンプレートに、質問の事例を当てはめてみます。
それぞれの箇所で出力する内容をまとめると、以下のようになります。
| 箇所 | 出力する内容 | 
|---|---|
| 第一階層(子)カテゴリの最初のカテゴリのときだけ出力する内容 | なし | 
| 第二階層(孫)カテゴリの最初のカテゴリのときだけ出力する内容 | なし | 
| 第一階層(子)カテゴリの最初のカテゴリのときだけ出力する内容 | テーブルの先頭部分と、子カテゴリのブログ記事の一覧(テーブルの行) | 
| 第二階層(孫)カテゴリの最初のカテゴリのときだけ出力する内容 | 孫カテゴリのブログ記事の一覧(テーブルの行) | 
| 第一階層カテゴリの情報や、その階層のブログ記事の情報を出力し終わった後に、1回だけ出力する内容 | テーブルの閉じタグ | 
| 第二階層カテゴリの情報や、その階層のブログ記事の情報を出力し終わった後に、1回だけ出力する内容 | なし | 
| 第一階層(子)カテゴリの最後のカテゴリのときだけ出力する内容 | なし | 
| 第二階層(孫)カテゴリの最後のカテゴリのときだけ出力する内容 | なし | 
また、孫カテゴリの記事一覧を出力する際には、最初の記事では孫カテゴリ名を表のセルに出力し、「rowspan="孫カテゴリの記事数」の属性を付けます。
こうすることで、孫カテゴリの全部の記事の左に、孫カテゴリ名を1回だけ出力できます。
4.テンプレートを作る
ここまでの話に沿って具体的なテンプレートを作ると、以下のようになります。
<mt:HasNoParentCategory>
<$mt:SetVar name="depth" value="1"$>
<mt:SubCategories>
  <mt:If name="depth" eq="1">
    <table width="100%" border="0">
      <caption>
        <$mt:CategoryLabel$>
      </caption>
      <tr>
        <td>孫カテゴリ</td>
        <td>タイトル</td>
        <td>日付</td>
      </tr>
    <mt:Entries>
      <tr>
        <td colspan="2"><$mt:EntryTitle$></td>
        <td><$mt:EntryDate format="%Y/%m/%d"$></td>
      </tr>
    </mt:Entries>
  <mt:Else name="depth" eq="2">
    <mt:Entries>
      <tr>
      <mt:If name="__first__">
        <td rowspan="<$mt:CategoryCount$>"><$mt:CategoryLabel$></td>
      </mt:If>
        <td><$mt:EntryTitle$></td>
        <td><$mt:EntryDate format="%Y/%m/%d"$></td>
      </tr>
    </mt:Entries>
  </mt:If>
  <$mt:SetVar name="depth" op="++"$>
  <$mt:SubCatsRecurse$>
  <$mt:SetVar name="depth" op="--"$>
  <mt:If name="depth" eq="1">
    </table>
  </mt:If>
</mt:SubCategories>
</mt:HasNoParentCategory>
5~19行目が、子カテゴリとその記事を出力する部分です。
5~13行目でtable要素の先頭部分(caption要素と見出しの行)を出力します。
また、14~19行目で、子カテゴリの記事をテーブルの行として出力します。
21~29行目が、孫カテゴリとその記事を出力する部分です。
23~25行目のMTIfタグのブロックで、最初の記事のときだけ、カテゴリ名の列を出力します。
rowspan属性には、MTCategoryCountタグで、その孫カテゴリの記事数を出力します
そして、34~36行目で、子カテゴリと孫カテゴリの情報を出力し終わった後に、テーブルの閉じタグを出力します。
また、全体をMTHasNoParentCateogoryタグのブロックで囲んでいます。
これによって、親がないカテゴリ(=トップレベルカテゴリ)のときだけ、この部分が再構築されます。

 
  