4

I have this piece of 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 ++;
    }
}

And I want to reorder items based on post_type and a metakey rw_adversiting_position and then display them normally. Right now I am getting this result:

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

That's because only the 0, 1 and 2 are esp-publicitarios post types. The number at the right 9, 6 and 3 is the position where I should push the item so having this info the result should be something like:

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

Can any give me some ideas to achieve this?

UPDATE 1

I did found this but I am not sure how to apply it.

UPDATE 2

This is the PHP code for achieve the sorting, sort_position could be 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 );

I just need to know how to apply this to WP_Query before get the result, any?

UPDATE 3

Here is a snippet of var_export($normal_query->posts) as you can see there is no meta-key so I can order based on values of a meta, then how?

$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 I have made some minors changes to your code and now it looks as follow:

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

If I debug this on the template I get the following output:

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

The AFTER is the right value but the loop is using the BEFORE and is adding to empty elements on the loop, why? See this pic for more info.

4
  • 1
    The posts are stored in the query object, $normal_query->posts, you can reorder them there before running the loop.
    – Milo
    Commented Dec 2, 2015 at 18:19
  • @Milo yes, you're almost right, but $normal_query->posts is missing meta-key so I don't know the value of rw_adversiting_position which is the key holding the position for reorder the items. Can you give me some help?
    – ReynierPM
    Commented Dec 2, 2015 at 21:42
  • Wait... so you're not actually trying to order them by post_type, just rw_adversiting_position, right?
    – bosco
    Commented Dec 3, 2015 at 2:19
  • @bosco right, what matter to me rather than post_type is the value of met rw_adversiting_position
    – ReynierPM
    Commented Dec 3, 2015 at 2:20

2 Answers 2

3

You could use the Metadata API to retrieve the rw_advertising_position metadata for each post, seperate the ads from the content, and then re-insert the ads at the proper locations:

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

DISCLAIMER

In the example above, I specify a function parameter &$posts which instructs PHP to use a pass-by-reference evaluation strategy for the argument passed to the function as $posts. This means that instead of referring to a locally-scoped copy of the data passed as the first argument, the $posts variable will refer to the data at it's original place in memory.

Here I've used this mechanism to provide the (default) option to directly re-arrange an array of post objects without needing to handle a return value. The function itself merely sorting an array, I choose to pass the array argument by reference in order to provide behavior more consistent with all 12 of PHP's array sorting functions.

As @Andrei Gheorghiu points out in the comments, passing by reference can produce unexpected results if you're unfamiliar with the practice. In such a scenario you may wish to steer clear of it which can be done by setting the $return argument in the example to true, or to be completely safe remove the option entirely as Andrei has.

In your template:

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

wpse_210493_apply_advertising_position( $normal_query->posts );

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

I haven't tested that code - it's just for illustrative purposes.

Alternately, using a second query to retrieve the ads alone might work a little better.

10
  • I think I will need to change a few things since posts with rw_adversiting_position meta are going to the end of the list and that's wrong but that's not the problem I need to fix right now. I have tested your code and it works it just doesn't display the_title() or any other values for post with meta rw_adversiting_position see this pic what's wrong? I need to fix this before fix the sorting, any help or advise?
    – ReynierPM
    Commented Dec 3, 2015 at 5:03
  • 1
    There were some oversights in my example. It should probably work now. If not, var_dump( $normal_query->posts ); after calling the wpse_210493_apply_advertising_position( $normal_query->posts ); to figure out what's gone wrong. You can write your own sorting function as well - the important part is that you can use the Metadata API to retrieve posts' rw_advertising_position meta-values.
    – bosco
    Commented Dec 3, 2015 at 20:51
  • it's working now, thanks and yes I got what code does, good answer
    – ReynierPM
    Commented Dec 3, 2015 at 21:03
  • can you help me with UPDATE 4? I have not idea why the loop is using the before $normal_query->post instead the modified by the function one, any advise?
    – ReynierPM
    Commented Dec 5, 2015 at 3:03
  • 2
    @Andrei you make a good point. But I'm of the mindset that people shouldn't be copying + pasting code they don't understand; I don't feel it's the community's responsibility to protect people from themselves, but rather guide them along the right path by example and provide opportunities to learn. As such, I won't alter my example code as I believe passing by reference is entirely appropriate in the case of sorting an array - but I did add a disclaimer to elaborate upon it and add a warning.
    – bosco
    Commented Dec 7, 2015 at 23:27
2

This is a revised version of bosco's answer which does answer the question but adds too much flexibility by allowing direct modification of query properties, that I personally consider bad practice.

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

After running the filter, we check to see if any posts were returned and if so, we use them in a foreach that outputs our result:

$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
  • I must said that your mod works perfectly and fix the issue I was having with extra items at end of original array, I will keep @bosco answer as accepted but I upvote yours. If the dupe is removed from the other question feel free to post the same answer there and I will accept the question
    – ReynierPM
    Commented Dec 5, 2015 at 15:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.