web-dev-qa-db-fra.com

Comment réorganiser (pop et push) des éléments de WP_Query?

J'ai ce morceau de code:

$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 ++;
    }
}

Et je souhaite réorganiser les éléments en fonction de post_type et d'un métakey rw_adversiting_position, puis les afficher normalement. En ce moment, j'obtiens ce résultat:

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 ==>

En effet, seuls les types 0, 1 et 2 sont des types de poste esp-publicitarios. Le nombre à droite 9, 6 et 3 est la position où je devrais pousser l'élément afin que cette information ait le résultat souhaité:

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 ==>

Quelqu'un peut-il me donner des idées pour y parvenir?

UPDATE 1

J'ai trouvé ceci mais je ne sais pas comment l'appliquer.

UPDATE 2

Ceci est le code PHP pour effectuer le tri. sort_position pourrait être rw_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 );

J'ai juste besoin de savoir comment appliquer cela à WP_Query avant d'obtenir le résultat, aucun?

UPDATE 3

Voici un extrait de var_export($normal_query->posts), comme vous pouvez le constater, il n’existe aucune méta-clé pour que je puisse commander en fonction des valeurs d’un méta. Comment alors?

$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 J'ai apporté quelques modifications mineures à votre code et voici à quoi il ressemble:

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;
    }
}

Si je débogue ceci sur le modèle, j'obtiens le résultat suivant:

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 est la bonne valeur, mais la boucle utilise BEFORE et ajoute des éléments vides sur la boucle, pourquoi? Voir cette photo pour plus d'informations.

3
ReynierPM

Vous pouvez utiliser l'API Métadonnées pour récupérer les métadonnées rw_advertising_position de chaque publication, séparer les annonces du contenu, puis les réinsérer aux emplacements appropriés:

/**
 * 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;
}

AVERTISSEMENT

Dans l'exemple ci-dessus, je spécifie un paramètre de fonction &$posts qui indique à PHP d'utiliser une stratégie passage par référence evaluation pour l'argument transmis à la fonction sous la forme $posts. Cela signifie qu'au lieu de faire référence à une copie localisée des données transmises en tant que premier argument, la variable $posts fera référence à la donnée à son emplacement d'origine dans la mémoire .

Ici, j'ai utilisé ce mécanisme pour fournir l'option (par défaut) permettant de réorganiser directement un tableau d'objets post sans avoir à gérer une valeur de retour. La fonction elle-même ne faisant que trier un tableau, j'ai choisi de passer l'argument du tableau par référence afin de fournir un comportement plus cohérent avec les 12 fonctions de tri des tableaux de PHP .

Comme le souligne @Andrei Gheorghiu dans les commentaires, passer par référence peut produire des résultats inattendus si vous ne connaissez pas bien la pratique. Dans un tel scénario, évitez ce qui peut être fait en définissant l'argument $return dans l'exemple sur true ou par mesure de sécurité supprimez entièrement l'option comme Andrei .

Dans votre modèle:

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

wpse_210493_apply_advertising_position( $normal_query->posts );

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

Je n'ai pas testé ce code - c'est juste à des fins d'illustration.

Alternativement, l'utilisation d'une seconde requête pour récupérer les annonces seules pourrait fonctionner un peu mieux.

3
bosco

Ceci est une version révisée de bosco réponse qui répond à la question mais ajoute trop de flexibilité en permettant une modification directe des propriétés de la requête, que je considère personnellement comme une mauvaise pratique.

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;
}

Après avoir exécuté le filtre, nous vérifions si des publications ont été renvoyées et, le cas échéant, nous les utilisons dans une variable foreach qui renvoie notre résultat:

$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
Andrei Gheorghiu