PowerCMS Xのmt:archivelistタグで追加の抽出条件を含めてアーカイブリンクを作成する

公開
更新

タイトルを具体的に説明すると、記事の中に「一覧に表示する」というチェックボックスがあり、「一覧に表示する」にチェックが入っている記事を基に年度別記事リストへのアーカイブリンクリストを作成したいということです。(ちなみに年別・月別等も同じ。)つまり、2020年の記事オブジェクトに「一覧に表示する」にチェックが入った記事がなければアーカイブリンクリストに2020年は表示しません。

mt:archivelistタグの属性でカラム名="値"の形式で条件を付けることはできないためmt:archivelistタグのソースコードを確認したところ、pre_archive_listコールバックがあることが分かったのでプラグインを作成しました。テンプレートを判別する方法だけが分からず社長の野田さんに質問したところ、「$ctx->stash( 'current_template' )にテンプレートオブジェクトが入っている」とのヒントを頂き、現在再構築対象のテンプレートのベースネームを取得することができました。

以下サンプルコードです。

/**
 * pre_archive_listコールバックでの処理(記事)
 */
public function pre_archive_list_entry ( $cb, $app, &$wheres ) {
    $target_templates = [
        'culture_entry_list',
        'japanese_entry_list',
    ];
    $current_template = $app->ctx->stash( 'current_template' );

    if ( array_search( $current_template->template_basename, $target_templates ) !== false ) {
        $model = $cb[ 'model' ];
        $wheres[] = "{$model}_information_display_flag = 1";
    }
}

後は普通にmt:archivelistタグを利用してテンプレートを書くだけです。PHPでオブジェクトの取得条件を制御する方法を覚えると作業が捗ります。

カテゴリ別・年度別アーカイブの場合

カテゴリ別かつ年度別アーカイブの場合、$wheres[] = "{$model}_id IN (...)";で対象の記事IDを指定すると良さそうですがIN句が膨大になる可能性があります。MySQLはIN句の上限はないようですがPADOを利用して一時テーブルを作成することができるか試したところ意図した結果を得ることができました。同一カテゴリで年度違いの場合は一時テーブルが使い回されます。(ただしインデックステンプレートの編集画面で「保存と再構築」を押した場合は使い回しできない模様)

$category_id = $app->ctx->vars[ 'current_object_id' ];

if ( $app->param( '_model' ) !== 'template' && array_key_exists( $category_id, $this->temporary_table_nums ) ) {
    $table_num = $this->temporary_table_nums[ $category_id ];
} else {
    $table_num = rand( 100000, 999999 );
    $this->temporary_table_nums[ $category_id ] = $table_num;

    $app->db->db->query( "CREATE TEMPORARY TABLE `mt_tmp_{$table_num}` (entry_id INT);" );
    $sth = $app->db->db->prepare( "INSERT INTO `mt_tmp_{$table_num}` (entry_id) SELECT `relation_from_id` AS entry_id FROM `mt_relation` WHERE `relation_from_obj` = 'entry' AND `relation_to_obj` = 'category' AND `relation_to_id` = :category_id" );
    $sth->execute( [ 'category_id' => $category_id ] );
}

$wheres[] = "entry_id IN (SELECT entry_id FROM mt_tmp_{$table_num})";

mt:archivelistと上記コードの組み合わせた場合、そしてmt:entriesで対象記事を全件回して処理する場合をmt:speedmeterで比較したところ、mt:archivelistを使用した方が速いという結果になりました。(表の単位は秒・M2 MacBook Airにて計測・カレントリンクの処理をテンプレート側でやる体でmt:cacheblockは使用せず)

mt:archivelist mt:entries
1年目 0.0156 0.0487
2年目 0.0038 0.0185
3年目 0.0034 0.0156
4年目 0.0034 0.0205
5年目 0.0036 0.016
6年目 0.0071 0.0159
7年目 0.0033 0.0158
8年目 0.0031 0.0156
9年目 0.0034 0.0201
10年目 0.0033 0.0157
11年目 0.0034 0.0162
12年目 0.0033 0.0156
13年目 0.0035 0.0197
合計 0.0602 0.2539