元フリーエンジニアライフ

Ruby on Rails とか MovableType とかAWSやってるフリーランスウェブエンジニアの記録でした。現在は法人成りしてIT社長。

MT::Object->search_by_meta() でカスタムフィールド以外での検索条件を追加する

MovableTypeでサイトを構築していると、カスタムフィールドを使ってあれやこれや結構ややこしいことをしたくなります。(JOINまみれになるのであんまりよくないのですが・・・)

その場合、プラグインを作ったりするのですが、カスタムフィールドで検索するのに search_by_meta を使うのが便利です。

Loading any Object (Entry, Page...) Based on a Custom Field Value

そんな search_by_meta ですが、実は第三引数と第四引数で検索条件を追加で指定することができます。(MT5.2で確認)

=item * Class->search_by_meta( $key, $value, [ \%terms [, \%args ] ] )

Returns objects that have a C<$key> metadata value of C<$value>. Further restrictions on the class may be applied through the optional C<%terms> and C<%args> parameters.

メタテーブル(mt_entry_meta)で検索する

注意すべきは検索条件といっても、メタテーブルの検索条件と言うこと。

メタテーブルとはたとえば記事テーブル mt_entry に対する mt_entry_meta のこと。 たとえばオブジェクトのIDで絞り込むなら以下のような感じでOK

my $class = MT->model( 'entry' );
my @ids = (1, 2, 3);
my %terms = ( entry_id => \@ids );
my %args = ();
my @entries = $class->search_by_meta( 'field.field_name',
                                      'somevalue',
                                      \%terms,
                                      \%args );

発行されるSQLはこうなる。

SELECT entry_meta_entry_id
FROM mt_entry_meta, mt_entry
WHERE
    (entry_meta_entry_id IN ('1','2', '3')) AND
    (entry_meta_vchar_idx = 'somevalue') AND
    (entry_meta_type = 'field.field_name')

対象のオブジェクトのテーブルで(mt_entry)検索したい

JOINを使うことで mt_entry のカラムを検索条件に含めることができる。

たとえば、ある期間内に公開された記事に対して、カスタムフィールドで検索をかけるには以下のようにする。

my $class = MT->model( 'entry' );
my $start = '20131001000000'; # 2013/10/01
my $end   = '20131031000000'; # 2013/10/31
my %join_terms = (
    id => \"= entry_meta_entry_id",
    authored_on => [$start, $end],
);
my %join_args = (
    range => {
        authored_on => 1
    }
);
my %terms = ();
my %args = (
    'join' => $class->join_on(undef, \%join_terms, \%join_args)
);
my @entries = $class->search_by_meta( 'field.field_name',
                                      'somevalue',
                                      \%terms,
                                      \%args );

この場合発行されるSQLはこうなる。

SELECT entry_meta_entry_id
FROM mt_entry_meta, mt_entry
WHERE
    (entry_meta_vchar_idx = 'somevalue') AND
    (entry_meta_type = 'field.field_name') AND
    (
        (entry_authored_on > '2013-10-01 00:00:00') AND
        (entry_authored_on < '2013-10-31 00:00:00')
    ) AND
    (entry_id = entry_meta_entry_id)

なお、 entry_meta_entry_id をハードコーディングするのが気持ち悪ければ以下のような感じで無理矢理作ることもできるっぽい。

my $meta_class = $class->meta_pkg;
my $meta_pk    = $meta_class->primary_key_tuple->[0]; # entry_id
my $col = $meta_class->datasource . '_' . $meta_pk;   # entry_meta_entry_id

カスタムフィールドの仕様上JOINだらけになるのはやむを得ない。 が、がんばればこのように生SQLに頼らず検索することはできる。

参考