web-dev-qa-db-ja.com

すでにソートされたクエリ内のランダムソート

パッケージID(ASC)順に並べられたカスタム投稿のセットを返す実用的なクエリがあります。 9件の結果を含むクエリの場合、これは例えば以下のように返されます。

パッケージID 1の4件の投稿(投稿ID 1,2,3,4)

パッケージID 2を持つ3件の投稿(投稿ID 5,6,7)

パッケージID 3の投稿2件(ポストID 8,9)

これらの投稿が表示されるとき、それらは常に同じ順序です。つまり、投稿ID 1から投稿ID 9の順に表示されます。

私が達成しようとしているのは、各サブセット(パッケージIDで定義)の結果をランダムにソートすることです。このようにして、投稿はそのように表示されます。

1から4までの投稿IDのランダム表示(例:2,1,4,3)

5から7までの投稿IDのランダム表示(例6,5,7)

投稿IDの8から9までのランダム表示(例:8,9)

そのため、投稿はまだパッケージIDでグループ化されていますが、各パッケージ内の投稿はランダムに表示されます。

これが私の現在のクエリの外観です。

$args = array( 
    'post_type' => 'custom_post_type',
    'post_status' => 'publish',
    'posts_per_page' => 9,
    'meta_key' => 'pkg_id',
    'orderby' => 'pkg_id',
    'order' => 'ASC'
);

私は「うまくいくだろう」と思ったがうまくいかない:

$args = array( 
    'post_type' => 'custom_post_type',
    'post_status' => 'publish',
    'posts_per_page' => 9,
    'meta_key' => 'pkg_id',
    'orderby' => array( 'pkg_id' => 'ASC', 'Rand' )
);

私は完全に困惑しているので、任意の提案!

6
David Clough

これを実現するために過大​​な量のクエリを実行する必要はありません。 only oneクエリ、the_postsフィルタ、および必要に応じてコードをソートするためのPHPを使用することもできます。

私はこれが私があなたの質問で読んだものからのカスタムクエリであると思います、それで我々は以下をすることができます:

  • まず、カスタムのWP_Queryパラメータを導入して、そのパラメータをthe_postsフィルタのトリガとして使用できるようにします。このパラメータをwpse_custom_sortと呼び、フィルタを起動するためにtrueの値を取ります。

  • 次に、the_postsフィルタを使用してカスタムフィールドに従って投稿を並べ替え、次にカスタムフィールドごとにそれらの投稿をランダムに並べ替え、最後に並べ替えられた投稿を返します。

コード

ちょっとだけメモ

  • コードはテストされておらず、少なくともPHP 5.4が必要です。

  • ACFを使用しているので、投稿ごとのカスタムフィールド値を返すためにget_post_meta()を使用しました。get_field()を使用するにはこれを調整する必要があるかもしれません。私はACFに慣れていないので、その部分をソートする必要があります。論理はまだ同じままです

  • 覚えておいてください、カスタムフィールドをクエリしてもキャッシュされるので余分なクエリは発生しないので、これは最終目標を達成するための非常に無駄のない最適化された方法です。

これがカスタムクエリのコードです。あなたが基本的にする必要があるのはあなたのカスタムページテンプレートのあなたのクエリ引数に追加のパラメータを追加することだけです。

// Run your query normally
$args = [
    'wpse_custom_sort' => true, // This parameter will trigger or the_posts filter
    // Your query args here
];
$q = new WP_Query( $args );

// Your loop as normal

それでは、フィルタを実行します( これはfunctions.phpに、またはできればカスタムプラグインに入ります

/**
 * Function to flatten a multidimentional array (for later use)
 * 
 * Credit to zdenko
 * @link https://Gist.github.com/kohnmd/11197713
 */
function flatten_array( $arg ) 
{
    return is_array( $arg ) 
        ? 
        array_reduce( $arg, function ( $c, $a ) 
            { 
                return array_merge( $c, flatten_array( $a ) ); 
            }, [] ) 
        : 
        [$arg];
}

// The the_posts filter
add_filter( 'the_posts', function ( $posts, $q )
{
    // First we need remove our filter
    remove_filter( current_filter(), __FUNCTION__ );

    // Check if our custom parameter is set and is true, if not, bail early
    if ( true !== $q->get( 'wpse_custom_sort' ) )
        return $posts; 

    // Before we do any work, and to avoid WSOD, make sure that the flatten_array function exists
    if ( !function_exists( 'flatten_array' ) )
        return $posts;

    // Our custom parameter is available and true, lets continue
    // Loop through the posts
    $major_array = [];
    foreach ( $posts as $p ) {
        $meta = get_post_meta(
            $p->ID,
            'pkg_id',
            true
        );

        // Bail if we do not have a $meta value, just to be safe
        if ( !$meta )
            continue;

        // Sanitize the value
        $meta = filter_var( $meta, FILTER_SANITIZE_STRING );

        // Lets build our array
        $major_array[$meta][] = $p;
    }

    // Make sure we have a value for $major_array, if not, bail
    if ( !$major_array )
        return $posts;

    // Lets randomize the posts under each custom field
    $sorted = [];
    foreach ( $major_array as $field ) 
        $sorted[] = shuffle( $field );

    // Lets flatten and return the array of posts
    $posts = flatten_array( $sorted );

    return array_values( $posts );
}, 10, 2 );
6
Pieter Goosen

単一のクエリでは不可能なことですが、それぞれ別の「パッケージID」をターゲットにしてそれらを3つのRandとして並べ替えるために、それぞれ3つの別々のクエリを実行する必要があります。

$args1 = array(
    'post_type' => 'custom_post_type',
    'orderby' => 'Rand',
    'meta_query' => array(
        array(
            'key' => 'pkg_id',
            'value' => 1, // you will have to check if this is correct
        )
    )
);

$args2 = array(
    'post_type' => 'custom_post_type',
    'orderby' => 'Rand',
    'meta_query' => array(
        array(
            'key' => 'pkg_id',
            'value' => 2, // you will have to check if this is correct
        )
    )
);

$args3 = array(
    'post_type' => 'custom_post_type',
    'orderby' => 'Rand',
    'meta_query' => array(
        array(
            'key' => 'pkg_id',
            'value' => 3, // you will have to check if this is correct
        )
    )
);

これにより、投稿が「パッケージID」に分けられ、各セットがランダムに並べられます。

1
ngearing

我々が使用するならば:

$args = array( 
    'post_type'      => 'custom_post_type',
    'post_status'    => 'publish',
    'posts_per_page' => 9,
    'meta_key'       => 'pkg_id',
    'orderby'        => array( 'pkg_id' => 'ASC', 'Rand' => 'DESC' )
);

それから、注文部分を次のように取得します。

ORDER BY wp_postmeta.meta_value ASC, Rand() DESC

これはうまくいくようですが、DESCは必要ありません。

しかし、Rand()による順序付けはうまくスケーリングできないことに注意してください。その場合は@PieterGoosenによる方法がより適しています。

1
birgire

Pieter Goosenの回答に対する1つの追加。

Shuffle($ field)はシャッフルされた配列そのものではなくブール値を返すので、最後に彼のコードを変更しなければなりませんでした。だから私は(後)配列の配列ではなく、ブール値の配列を得ました。期待される結果を得るために、私は以下をしました:

  //Instead of 
    foreach ( $major_array as $field ) 
        //Shuffle returns boolean, so $sorted will be a
        $sorted[] = shuffle( $field );  

 // I went with
    foreach ( $major_array as $field ) {
        shuffle( $field );   // 1. Shuffle $field, which is an array
        $sorted[] = $field;  // Then add this 
    }
0
chrwald