web-dev-qa-db-ja.com

WP_Queryから項目を並べ替え(ポップおよびプッシュ)するにはどうすればよいですか。

私はこのコードを持っています:

$normal_args  = array(
    'order'               => 'desc',
    'ignore_sticky_posts' => 1,
    'meta_query'          => array(
        array(
            'key'     => 'rw_show_at_position',
            'value'   => '1',
            'compare' => '='
        )
    ),
    'post__not_in'        => $prev_post_ids,
    'post_status'         => 'publish',
    'posts_per_page'      => get_option( 'column_right' ),
    'post_type'           => array(
        'opinion',
        'especiales',
        'clasificados',
        'portadadeldia',
        'anunciantes',
        'post',
        'pages',
        'esp-publicitarios'
    )
);

$normal_query = new WP_Query( $normal_args );

$i = 0;
if ( $normal_query->have_posts() ) {
    while ( $normal_query->have_posts() ) {
        $normal_query->the_post(); ?>

        <?php
        echo $i . ' ==> ';
        ?>

        <?php
        if ( get_post_type( $post->ID ) == 'esp-publicitarios' ) {
            $adv_pos = rwmb_meta( 'rw_adversiting_position', 'type=select', $post->ID );
            echo $adv_pos . EOL;
        } else {
        ?>
            // do something here
        <?php
        }
        $i ++;
    }
}

そして、私はpost_typeとメタキーrw_adversiting_positionに基づいてアイテムを並べ替えて、それからそれらを普通に表示したいです。今私はこの結果を得ています:

0 ==> 183034 ==> 9
1 ==> 183033 ==> 6
2 ==> 183032 ==> 3
3 ==> 183002 ==>
4 ==> 182973 ==>
5 ==> 182971 ==>
6 ==> 182969 ==>
7 ==> 182999 ==>
8 ==> 182997 ==>
9 ==> 182995 ==>
10 ==> 182962 ==>
11 ==> 182948 ==>

これは、0、1、2のみがesp-publicitarios投稿タイプであるためです。右側の9、6、および3の数字は、アイテムをプッシュする位置です。この情報があれば、結果は次のようになります。

0 ==> 183002 ==>
1 ==> 182973 ==>
2 ==> 182971 ==>
2 ==> 183032 ==> 3
4 ==> 182969 ==>
5 ==> 182999 ==>
6 ==> 183033 ==> 6
7 ==> 182997 ==>
8 ==> 182995 ==>
9 ==> 183034 ==> 9
10 ==> 182962 ==>
11 ==> 182948 ==>

これを実現するためのアイデアをいくつか教えてください。

UPDATE 1

私は これ を見つけましたが、それを適用する方法がわかりません。

UPDATE 2

これはソートを実行するためのPHPコードです。sort_positionrw_adversiting_positionにすることができます。

$arr           = [
    '183034' => [ 'sort_position' => 9 ],
    '183033' => [ 'sort_position' => 5 ],
    '183032' => [ 'sort_position' => 3 ],
    '183002' => [ ],
    '182973' => [ ],
    '182971' => [ ],
    '182969' => [ ],
    '182999' => [ ],
    '182997' => [ ],
    '182995' => [ ],
    '182962' => [ ],
    '182948' => [ ]
];

$count         = count( $arr );
$has_sortorder = [ ];
$no_sortorder  = [ ];
krsort( $arr );
foreach ( $arr as $key => $val ) {
    if ( isset( $val['sort_position'] ) ) {
        $has_sortorder[ $val['sort_position'] ] = [ $key, $val ];
    } else {
        $no_sortorder[] = [ $key, $val ];
    }
}


$out = [ ];
for ( $i = 0; $i < $count; $i ++ ) {
    if ( isset( $has_sortorder[ $i ] ) ) {
        $out[ $has_sortorder[ $i ][0] ] = $has_sortorder[ $i ][1];
    } else {
        $element            = array_shift( $no_sortorder );
        $out[ $element[0] ] = $element[1];
    }
}

var_dump( $out );

結果を取得する前に、これをWP_Queryに適用する方法を知っておく必要があるだけです。

UPDATE 3

メタキーがないのがわかるので、ここではvar_export($normal_query->posts)の断片を示します。メタの値に基づいて注文することができます。

$var = array(
    0 => WP_Post::__set_state( array(
        'ID'                    => 183034,
        'post_author'           => '4',
        'post_date'             => '2015-12-01 16:44:35',
        'post_date_gmt'         => '2015-12-01 21:14:35',
        'post_content'          => '',
        'post_title'            => 'Espacio Pub 3',
        'post_excerpt'          => '',
        'post_status'           => 'publish',
        'comment_status'        => 'closed',
        'ping_status'           => 'closed',
        'post_password'         => '',
        'post_name'             => 'espacio-pub-3',
        'to_ping'               => '',
        'pinged'                => '',
        'post_modified'         => '2015-12-01 16:54:38',
        'post_modified_gmt'     => '2015-12-01 21:24:38',
        'post_content_filtered' => '',
        'post_parent'           => 0,
        'guid'                  => 'http://elclarinweb.local/?post_type=esp-publicitarios&p=183034',
        'menu_order'            => 0,
        'post_type'             => 'esp-publicitarios',
        'post_mime_type'        => '',
        'comment_count'         => '0',
        'filter'                => 'raw',
    ) ),
    1 => WP_Post::__set_state( array(
        'ID'                    => 183033,
        'post_author'           => '4',
        'post_date'             => '2015-12-01 16:44:13',
        'post_date_gmt'         => '2015-12-01 21:14:13',
        'post_content'          => '',
        'post_title'            => 'Espacio Pub 2',
        'post_excerpt'          => '',
        'post_status'           => 'publish',
        'comment_status'        => 'closed',
        'ping_status'           => 'closed',
        'post_password'         => '',
        'post_name'             => '183033',
        'to_ping'               => '',
        'pinged'                => '',
        'post_modified'         => '2015-12-01 16:44:21',
        'post_modified_gmt'     => '2015-12-01 21:14:21',
        'post_content_filtered' => '',
        'post_parent'           => 0,
        'guid'                  => 'http://elclarinweb.local/?post_type=esp-publicitarios&p=183033',
        'menu_order'            => 0,
        'post_type'             => 'esp-publicitarios',
        'post_mime_type'        => '',
        'comment_count'         => '0',
        'filter'                => 'raw',
    ) ),
    2 => WP_Post::__set_state( array(
        'ID'                    => 183032,
        'post_author'           => '4',
        'post_date'             => '2015-12-01 15:53:56',
        'post_date_gmt'         => '2015-12-01 20:23:56',
        'post_content'          => '',
        'post_title'            => 'Publicidad 1',
        'post_excerpt'          => '',
        'post_status'           => 'publish',
        'comment_status'        => 'closed',
        'ping_status'           => 'closed',
        'post_password'         => '',
        'post_name'             => 'publicidad-1',
        'to_ping'               => '',
        'pinged'                => '',
        'post_modified'         => '2015-12-01 15:53:56',
        'post_modified_gmt'     => '2015-12-01 20:23:56',
        'post_content_filtered' => '',
        'post_parent'           => 0,
        'guid'                  => 'http://elclarinweb.local/?post_type=esp-publicitarios&p=183032',
        'menu_order'            => 0,
        'post_type'             => 'esp-publicitarios',
        'post_mime_type'        => '',
        'comment_count'         => '0',
        'filter'                => 'raw',
    ) ),
    3 => WP_Post::__set_state( array(
            'ID'                    => 183002,
            'post_author'           => '7',
            'post_date'             => '2015-11-22 00:08:00',
            'post_date_gmt'         => '2015-11-22 04:38:00',
            'post_content'          => 'Borrón y cuenta nueva es lo que han hecho los Bravos de Margarita en este comienzo de la segunda parte de la campaña, en la que ayer sumaron su cuarto triunfo seguido, al vencer a los Tiburones de La Guaira 5 carreras por 3. Margarita, que ganó dos de tres ante los escualos en la semana, madrugó al dominicano Alexis Candelario, quien llegó a dicha cita como el mejor lanzador del campeonato. Los artilleros isleños fabricaron cuatro de sus cinco carreras en las primeras dos entradas, catapultados por un doble impulsor de dos de Eliézer Alfonzo. “No importa quién esté en la lomita contraria, siempre que los muchachos crean en ellos mismos, estos van a ser los resultados”, señaló el dirigente Henry Blanco. Bravos se haría presente en el marcador una vez más en el sexto con doble remolcador del jardinero Junior Sosa y aguantaría un intento de remontada de los litoralenses en la parte final para sellar el lauro. “La mentalidad que tenemos es no pensar en la primera parte, hay que salir a ganar”, indicó Alfonzo, quien cerró el cotejo de 5-2 con un par de impulsadas y ahora acumula cinco rayitas traídas al plato en los últimos dos desafíos. “Estaba bastante perdido cuando comenzó la temporada, pero he hecho el ajuste necesario”.',
            'post_title'            => 'Ahora Bravos es puntero de la LVBP',
            'post_excerpt'          => 'Las curiosidades del nuevo sistema de puntos del torneo coloca al antiguo colero de puntero',
            'post_status'           => 'publish',
            'comment_status'        => 'open',
            'ping_status'           => 'open',
            'post_password'         => '',
            'post_name'             => 'ahora-bravos-es-puntero-de-la-lvbp',
            'to_ping'               => '',
            'pinged'                => '',
            'post_modified'         => '2015-11-22 00:08:00',
            'post_modified_gmt'     => '2015-11-22 04:38:00',
            'post_content_filtered' => '',
            'post_parent'           => 0,
            'guid'                  => 'http://elclarinweb.local/?p=183002',
            'menu_order'            => 0,
            'post_type'             => 'post',
            'post_mime_type'        => '',
            'comment_count'         => '0',
            'filter'                => 'raw'
        )
    )
);

UPDATE 4

@bosco私はあなたのコードにいくつかのマイナーな変更を加えました、そして今それは以下のように見えます:

function wpse_210493_apply_advertising_position( &$posts, $return = false ) {
    $ad_posts = array();

    // Seperate $posts into "Ads" and "Content" arrays based on whether or not they have 'rw_adversiting_position' meta-data
    foreach ( $posts as $post ) {
        $position      = intval( get_post_meta( $post->ID, 'rw_adversiting_position', true ) );
        $post_date     = $post->post_date;
        $post_modified = $post->post_modified;

        if ( ! empty( $position ) ) {
            if ( ! empty ( $ad_posts ) ) {
                if ( $post_date > $ad_posts[ $position ]->post_date || $post_modified > $ad_posts[ $position ]->post_modified ) {
                    $ad_posts[ $position ] = $post;
                }
            } else {
                $ad_posts[ $position ] = $post;
            }
        } else {
            $content_posts[] = $post;
        }
    }

    // Sort the ads from smallest position index to greatest such that re-insertion properly factors in all ads
    ksort( $ad_posts );

    // Add the ads back into the content at their specified positions
    foreach ( $ad_posts as $position => $ad ) {
        array_splice( $content_posts, $position, 0, array( $ad ) );
    }

    // If $return is true, return the resulting array. Otherwise replace the original $posts array with it.
    if ( $return ) {
        return $content_posts;
    } else {
        $posts = $content_posts;
    }
}

テンプレートでこれをデバッグすると、次のような出力が得られます。

echo '<pre> BEFORE';
echo count($normal_query->posts);
echo '</pre>';

wpse_210493_apply_advertising_position( $normal_query->posts );

echo '<pre> AFTER';
echo count($normal_query->posts);
echo '</pre>';

// Output
BEFORE25
AFTER23

AFTERは正しい値ですが、ループはBEFOREを使用しており、ループの空の要素に追加されています。なぜですか?詳しくは この写真 をご覧ください。

3
ReynierPM

Metadata API を使用して各投稿のrw_advertising_positionメタデータを取得し、コンテンツから広告を分離してから、適切な場所に広告を再挿入できます。

/**
 * Extracts from an array posts with positional metadata and re-inserts them at the proper
 * indices. See https://wordpress.stackexchange.com/questions/210493
 **/
function wpse_210493_apply_advertising_position( &$posts, $return = false ) {
    $ad_posts      = array();
    $content_posts = array();

    // Seperate $posts into "Ads" and "Content" arrays based on whether or not they have 'rw_adversiting_position' meta-data    
    foreach( $posts as $post ) {
        $position = get_post_meta( $post->ID, 'rw_adversiting_position', true );

        if( ! empty( $position ) )
            $ad_posts[ intval( $position ) ] = $post;
        else
            $content_posts[] = $post;  
    }

    // Sort the ads from smallest position index to greatest such that re-insertion properly factors in all ads
    ksort( $ad_posts );

    // Add the ads back into the content at their specified positions
    foreach( $ad_posts as $position => $ad ) {
        array_splice( $content_posts, $position, 0, array( $ad ) );
    }

    // If $return is true, return the resulting array. Otherwise replace the original $posts array with it.
    if( $return )
        return $content_posts;
    else
        $posts = $content_posts;
}

免責事項

上記の例では、関数に渡される引数に対して pass-by-reference 評価戦略を&$postsとして使用するようにPHPに指示する関数パラメータ$postsを指定します。これは、最初の引数として渡されたデータのローカルスコープのコピーを参照するのではなく、$posts変数がメモリ内の元の場所でdata を参照することを意味します

ここでは、このメカニズムを使用して、戻り値を処理する必要なしにpostオブジェクトの配列を直接並べ替える(デフォルト)オプションを提供しました。関数自体は単に配列をソートするだけです。 PHPの12の配列ソート関数のすべて とより一貫した動作を提供するために、参照によって配列引数を渡すことにしました。

コメントの中で @Andrei Gheorghi が指摘しているように、慣習に慣れていない場合は参照渡しで予期しない結果が生じる可能性があります。このようなシナリオでは、例の$return引数をtrueに設定するか、完全に安全な Andreiが持っているように完全にオプションを削除する を実行することでそれを回避できるかもしれません。

あなたのテンプレートで:

// [...]
$normal_query = new WP_Query( $normal_args );

wpse_210493_apply_advertising_position( $normal_query->posts );

if ( $normal_query->have_posts() ) {
// [...]

私はそのコードをテストしていません - それは単に説明目的のためです。

あるいは、広告だけを取得するために2番目のクエリを使用すると、もう少しうまくいく場合があります。

3
bosco

これは bosco's answerの修正版です。 質問に答えます しかし、クエリプロパティを直接変更することで柔軟性が高まります。個人的に悪い習慣を考えなさい。

function wpse_210493_apply_advertising_position( $posts ) {
    $ad_posts      = array();
    $content_posts = array();

    // Seperate $posts into "Ads" and "Content" arrays based on whether or not they have 'rw_adversiting_position' meta-data    
    foreach( $posts as $post ) {
        $position = get_post_meta( $post->ID, 'rw_adversiting_position', true );

        if( ! empty( $position ) )
            $ad_posts[ intval( $position ) ] = $post;
        else
            $content_posts[] = $post;  
    }

    // Sort the ads from smallest position index to greatest such that re-insertion properly factors in all ads
    ksort( $ad_posts );

    // Add the ads back into the content at their specified positions
    foreach( $ad_posts as $position => $ad ) {
        array_splice( $content_posts, $position, 0, array( $ad ) );
    }

    return $content_posts;
}

フィルタを実行した後、投稿が返されたかどうかを確認し、返された場合は結果を出力するforeachでそれらを使用します。

$normal_query = new WP_Query( $normal_args );

$filtered_posts = wpse_210493_apply_advertising_position( $normal_query->posts );

if ( count($filtered_posts) ) :
    foreach ($filtered_posts as $post) :
        setup_postdata($post);
        /* run any functions available in WP loop here 
         * (the_title(), the_content(), etc...)
         *
         */
    endforeach;
    wp_reset_postdata();
endif;
1