MTEntryCategoriesの出力を管理画面の並び順にしたい

公開

PowerCMS 6を利用したプロジェクトでテンプレート実装をしている際、「ここのカテゴリ表示はきっと管理画面の並び順にしてほしいと言われるだろうな…」と考える部分がありました。Googleで検索すると同じ悩みを抱えている人は多そうですが、「MTEntryCategoriesを使用して管理画面の並び順で出力|kobayashi」を拝見すると「テンプレートで頑張るしかしかない」というのがサポートの見解のようです。(サポート=勤務先なのですが)

ただ、今までの経験上、テンプレートで頑張るとどうしてもややこしいテンプレートになってしまいます。MTEntryCategoriesの実装を見てみるとあまり難しくない実装ですし、今更このタグの実装が変わることもないだろうと思い、コピーして独自タグを作成することにしました。

カスタマイズメモ

カスタマイズするのはlib/MT/Template/Tags/Category.pmにある_hdlr_entry_categoriesです。コードをコピーしてブロックタグを作成するので「ブロックタグ プラグインの開発について」が参考になります。タグ名は何でもよいのですがMTSortedEntryCategoriesとし、メソッド名も_hdlr_sorted_entry_categoriesにします。

カテゴリの並び順はblog_metaテーブルにカンマ区切りで保存されているので、これを取得して並び替えをします。Perlに慣れていないとここで手詰まりになるのですが、今ではChatGPTの力を借りることで乗り越えることができます。「変数orderにカンマ区切りで並び順を 1,5,2,3 のように保持しています。数字はIDです。これを利用して@$catsをsortしたい。$cats->idがIDです。」とプロンプトを入力するとそのまま利用できるコードを回答で受け取ることができました。

# EntryCategoriesを管理画面の並び順で出力するようにした独自タグ
# ほとんどMT::Template::Tags::Category::_hdlr_entry_categoriesのコピー
sub _hdlr_sorted_entry_categories {
    my ( $ctx, $args, $cond ) = @_;
    my $e = $ctx->stash('entry')
        or return $ctx->_no_entry_error();
    my $cats;
    if ( 'primary' eq lc( $args->{type} || '' ) ) {
        $cats = [ $e->category ]
            if $e->category;
    }
    else {
        $cats = $e->categories;
    }
    return '' unless $cats && @$cats;
    
    if ($ctx->var( 'preview_template' )) {
        $cats = [sort { $a->label cmp $b->label } @$cats];
    }

    # ここからカスタマイズ
    my $meta_pkg = MT->model('blog')->meta_pkg;
    my @meta_obj = $meta_pkg->search({ blog_id => @$cats[0]->blog_id, type => 'category_order', });
    my @order_list = split /,/, @meta_obj[0]->vclob;
    my %order_map;
    @order_map{@order_list} = (0..$#order_list); # orderの順番をマッピングするハッシュを作成
    my @sorted_cats = sort { $order_map{$a->id} <=> $order_map{$b->id} } @$cats;
    # ここまでカスタマイズ

    my $builder = $ctx->stash('builder');
    my $tokens  = $ctx->stash('tokens');
    my $res     = '';
    my $glue    = $args->{glue};
    local $ctx->{inside_mt_categories} = 1;

    for my $cat (@sorted_cats) { # ループする変数を@sorted_catsに変更
        local $ctx->{__stash}->{category} = $cat;
        defined( my $out = $builder->build( $ctx, $tokens, $cond ) )
            or return $ctx->error( $builder->errstr );
        $res .= $glue if defined $glue && length($res) && length($out);
        $res .= $out;
    }
    $res;
}

なお、Movable Typeもほぼ同一実装なのでそのまま利用できます。