web-dev-qa-db-ja.com

ユーザー定義のメタ値の順序に従って投稿を順序付けしますか?

私はいくつかのCPTを設定し、それぞれにメタキー/ valを保存しました。これらの投稿を取得するときは、メタ値で並べ替える必要がありますが、メタ値の順序は自分で配列に定義されます 。メタ値のデフォルトの順序はアルファベット順または数値順のいずれかですが、これは望ましくありません。

例で説明します。

Post 1 =>
Meta key: Fruit
Meta value: Apple

Post 2 =>
Meta key: Fruit
Meta value: Melon

Post 3 =>
Meta key: Fruit
Meta value: Guava

Post 4 =>
Meta key: Fruit
Meta value: Banana

私は外部の情報源から注文を受けましたが、その順番で投稿を取得したいと思います。例:配列['Apple', 'Banana', 'Guava', 'Melon']で注文を取得します。 CPTを取得するときは、この順番で取得する必要があります。 (アルファベット順は無視してください。一般的な例を挙げようとしているだけです)。

meta_queryまたは他の解決策を使用してそれを達成するにはどうすればよいですか?

既存のスレッドがある場合は、それを私にリダイレクトしてください。

2

私の提案した解決策はすでに@cybmeta(+1)によって投稿されていますが、それを一般的な方法で扱うのでとにかく追加します; - )

WP_Queryパラメータも同様に、

'post__in' => [1,2,3],
'orderby' => 'post__in'

カスタム定義の順序を使う

ORDERBY FIELD( wp_posts.ID, 1, 2, 3 )

以下を使用して独自のメタ順序を定義できます。

'meta__in' => [ 'Apple', 'orange', 'banana' ],
'orderby' => 'meta__in'

次のSQL部分を取得します。

ORDERBY FIELD( wp_postmeta.meta_value, 'Apple', 'orange', 'banana' )

まず、wp_parse_id_list()ヘルパー関数の独自のバージョンを定義します。

/**
* Note that this function is based on the core wp_parse_id_list() function.
* Clean up an array, comma- or space-separated list of string keys.
*
* @uses sanitize_key()
* @param array|string $list List of keys.
* @return array Sanitized array of keys.
*/
function wpse_parse_meta_list( $list )
{
    if ( ! is_array( $list ) )
        $list = preg_split('/[\s,]+/', $list);

    return array_unique( array_map( 'sanitize_key', $list ) );
}   

デモプラグイン

次に、'meta__in'の順序付けのサポートをWP_Queryに追加するために、次のデモプラグインを構築します。

<?php
/**
 * Plugin Name: Support for order by meta__in in WP_Query
 */

add_filter( 'posts_orderby', function ( $orderby, \WP_Query $q )
{
    if( $meta__in = $q->get( 'meta__in' ) )
    {
        if( is_array( $meta__in ) && ! empty( $meta__in ) )
        {
            global $wpdb;   

            // Transform the meta__in array into a comma separated strings of keys
            // Example [ 'Apple', 'banana' ] --> "Apple", "banana"
            $list = '"' . join( '","', wpse_parse_meta_list( $meta__in ) ) . '"';           

            // Override the orderby part with custom ordering:
            $orderby = " FIELD( {$wpdb->postmeta}.meta_value, {$list} ) ";
        }
    }
    return $orderby;
}, 10, 2 );

/**
 * Note that this function is based on the core wp_parse_id_list() function.
 * Clean up an array, comma- or space-separated list of string keys.
 *
 * @uses sanitize_key()
 * @param array|string $list List of keys.
 * @return array Sanitized array of keys.
 */
function wpse_parse_meta_list( $list )
{
    if ( ! is_array( $list ) )
        $list = preg_split('/[\s,]+/', $list);

    return array_unique( array_map( 'sanitize_key', $list ) );
}   

ここで、プラグインにヘルパー関数を追加しました。

ノート

'meta__in'パラメータの場合のように、'post__in'パラメータは順序付けにのみ使用され、追加のWHERE制限には使用されません。しかし、それは面白い拡張かもしれません;-)

また、get_posts()は単なるWP_Queryラッパーであり、デフォルトで次のパラメータが設定されています。

'suppress_filters' => true 

つまり、デフォルトではposts_*フィルタを受け付けません。

WP_Queryと一緒にsuppress_filters => falseget_posts()を使うことができないのであれば、代わりのアプローチが必要です。 @PieterGoosenが提案しました。

2
birgire

メタフィールドの値が単一値(直列化されていない)の場合、このフィルタは機能します。

add_filter( 'posts_orderby', 'custom_posts_orderby' );
function custom_posts_orderby( $orderby_statement ) {

    $custom_order = array( 'Apple', 'Banana', 'Guava', 'Melon' );

    $custom_order = implode ( ", ", $custom_order );

    $orderby_statement = "FIELD( meta_value, " . $custom_order . " )";

    return $orderby_statement;

}

これはすべてのクエリに影響するため、フィルタを適用するかどうかの条件を定義する必要があります。

ORDER BY FIELD() statemnetを構築するためのより多くの例をこの投稿 で見ることができます

1
cybmeta

私が言ったように、私見、あなたが必要とするもののためのあなたの最善の選択肢は投稿の返された配列をソートするためのusort()になるだろう

これは未検証の理論です。

$sortorder = ['Apple', 'Banana', 'Guava', 'Melon'];
$args = [
    'meta_query' => [
        [
            'key'   => 'Fruit',
            'value' => $sortorder
        ]
    ],
];
$q = new WP_Query( $args );

@usort( $q->posts, function( $a, $b ) use ( $sortorder  ) {
    // Get the meta values from the two posts to be compared
    $array_a = get_post_meta( $a->ID, 'Fruit', true );
    $array_b = get_post_meta( $b->ID, 'Fruit', true );

    // Reverse our $sortorder, key/value becomes value/key
    $flipped_order = array_flip( $sortorder );

    // Let start our sorting
    // If our meta values are the same order by date
    if ( $flipped_order[$array_a] != $flipped_order[$array_b] ) {
        return $flipped_order[$array_a] - $flipped_order[$array_b] // Swop around to reverse ordering
    } else {
        return $a->post_date < $b->post_date; // Change to > if you need oldest posts first
    }
});

// Run your loop normally

編集

私は自分のコードに安全性をもたらしませんでした。 $sortorderが有効な配列であることを確認したいのであれば、悪意のあるコードを回避するために使用する前に値をサニタイズして検証する必要があります。

メインクエリに対してもこれを行うことができます。唯一のことは、usort()関数をthe_postsフィルタでラップしてから$postsの代わりに$q->postsを使用することです。

1
Pieter Goosen