アルファサード株式会社 代表取締役 野田 純生のブログ


MTEntryNext,Previousの拡張(とか改良?とか)。


公開日 : 2008-11-19 17:24:40


# 何回も書いてる気がするなぁ、これ。

残念ながら...かなりしんどいことになる気がします。エントリーが1,000あって、最初と最後のエントリーだけが「カテゴリーA」に属していて、500番目のエントリーを「カテゴリーA」にしよう、と思ったら(伝わるかな?)

    my $terms;
    $terms->{category_id} = $cat->id;
    my $prev = $e->previous(1, $terms);
    my $res = '';
        while ($prev && !$prev->is_in_category($cat)){
            $terms->{category_id} = $cat->id;
            $prev = $prev->previous(1, $terms);
    }

500番目のエントリーから始まって、ひとつ前エントリーを読み込んで(ここで1回SQL投げる)のカテゴリチェック、違ったら次のエントリーの、ってのを500回程繰り返してエントリー1まで来て、お! あったあった。で、今度はNext処理で500回程...(以下略...ってか、これ以前に500エラーになるかもしれません)

いささか極端な例ですが、いずれにしても効率が良くありません (この件については以前にも書いてますね)。

ちなみに、$terms->{category_id}は効きません(多分(追記:MT2.x?の頃は効いたのではないかな。category_idがmt_entryテーブルのカラムだった時代))。$terms->{by_category} が指定できるっぽいですがMT4.2ではエラーになります(MT4.1ならOK。そもそも4.1ならドキュメントにはありませんが by_category="1" と by_author="1" がテンプレートタグのモディファイアで指定できるようになっています)。

本題。

/lib/MT/Object.pmの sub nextprev が4.1→4.2で変わっていて、

    # Selecting the adjacent object can be tricky since timestamps
    # are not necessarily unique for entries. If we find that the
    # next/previous object has a matching timestamp, keep selecting entries
    # to select all entries with the same timestamp, then compare them using
    # id as a secondary sort column.

ああ、これ考えてたんだ...全く同じタイムスタンプのエントリーが複数あったときにどーすんだっていう件。4.1ではエントリーをループの中で比較して、タイムスタンプが同じだったらidで比較してるみたい。MT4.2ではSQL一発にシンプルに変更されているのですが、おそらくこの部分が関係して<MTEntryNext by_category="1">でウチの環境ではエラーを吐いてしまいます。ドキュメントには(多分)ないのでバグではなく仕様? なのかもしれないけど、標準でサポートされると幸せになれる人が多いかも。

で、もう一つ。not指定してるので確かにそのエントリー自身は出て来ないですが同一タイムスタンプの時の挙動(前後の指定)が (ウチの環境では) おかしい (複数のページで同じエントリーが出てくる)。

レアケースっていえばレアケースだけど、気持ち悪いので作りました。

  • mt:entrynext,mt:entryprevious,mt:pagenext,mt:pagepreviousタグを上書きします。
  • 同じタイムスタンプのエントリーが複数あったときはentry_idで比較して順番を決めます。
  • by_category="1" と by_author="1" が指定できます(同一カテゴリー/投稿者のエントリーの前後を取得できます)。
  • by_field="modified_on"のようにソートのカラムを指定できます(デフォルトはcreated_on)

例:

<mt:entrynext by_author="1" by_category="1" by_field="created_on">
<a href="<mt:entrypermalink>"><mt:entrytitle="escape="html"></a>
</mt:entrynext>

21:00追記
よく考えたら2つのキーで同時にソートできるわ。

$terms->{ id } = { '<' => $entry->id };
$params->{ limit } = 1;

以下修正箇所。これでload_iterでループさせる必要がなくなった。最多でもオブジェクト2つから特定できるようになったな。これでどうでしょう?

     if ( $count > 1 ) {
        if ( $meth eq 'next' ) {
            $terms->{ id } = { '<' => $entry->id };
        } else {
            $terms->{ id } = { '>' => $entry->id };
        }
        $params->{ limit } = 1;
        my $e = $class->load( $terms, $params );
        return $e if defined $e;
        # my $iter = $class->load_iter( $terms, $params );
        # my $match;
        # while ( my $e = $iter->() ) {
        #    return $e if $match;
        #    last if $match;
        #    if ( $e->id == $entry->id ) {
        #        $match = 1;
        #    }
        # }
    }


このブログを書いている人
野田純生の写真
野田 純生 (のだ すみお)

大阪府出身。ウェブアクセシビリティエバンジェリスト。 アルファサード株式会社の創業者であり、現役のプログラマ。経営理念は「テクノロジーによって顧客とパートナーに寄り添い、ウェブを良くする」。 プロフィール詳細へ