The problem is the loop:
foreach ( $hadices as $post ) : setup_postdata( $post );
if ( have_posts()) : while (have_posts()) : the_post();
setup_postdata( $post ); ?>
<h2><a href="https://wordpress.stackexchange.com/questions/302904/<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<?php the_content(); ?>
<?php endwhile;
endif;
endforeach;
wp_reset_postdata();
If we fix the indenting and format it properly we get:
foreach ( $hadices as $post ) {
setup_postdata( $post );
if ( have_posts()) {
while (have_posts()) {
the_post();
setup_postdata( $post );
?>
<h2>
<a href="https://wordpress.stackexchange.com/questions/302904/<?php the_permalink(); ?>">
<?php the_title(); ?>
</a>
</h2>
<?php
the_content();
}
}
}
wp_reset_postdata();
Now lets write it out as pseudocode/plain english:
for each hadices post
Set the current post
if the main query has posts
for each post in the main query
Set the current post as the post from the main query
Set the current post as the current post
output the post
After the loop, reset the post data
We can see that for some reason there’s 2 post loops, and the current post is set 3 times. On top of that, the code uses $post
which is the name of a global variable.
So instead, lets rewrite this as a standard WP_Query
loop:
$q = new WP_Query([
'post_type' => 'hadices',
'orderby' => 'rand',
'posts_per_page' => 1,
]);
if ( $q->have_posts() ) {
while( $q->have_posts() ) {
$q->the_post();
// output the post
}
wp_reset_postdata();
} else {
echo '<p>None were found</p>';
}
Notes:
the_post
callssetup_postdata
internally, you don’t have to call it againget_posts
callsWP_Query
internally, cut out the middle man and go straight forWP_Query
WP_Query
makes use of caching mechanisms, making it a little fasterposts_per_page
expects a number, you don’t have to wrap it in quotes,1
will do, it doesn’t have to be"1"
- Indent Indent indent! Your editor should be indenting for you, so ti should be effortless. Use an editor such as SublimeText or Atom if you want a free program, or opt for a more advanced IDE such as PHPStorm. All 3 have packages that can reformat entire files at the press of a button
- Don’t use
<?php ?>
spam, this:echo '<br/>';
is the same as?><?php echo '<br/>';?><?php
but easier to read - Your original code never checked if any posts were found
- You should put the
wp_reset_postdata
call inside the if statement - Your post type is named as a plural,
hadices
, but technically it should behadice
, or I presumehadith
, you can change the URL used when registering the post type via therewrite
option
Improving Performance
rand
isn’t just slow, it’s one of the slowest things you can ask a server to do. Some implementations will create a brand new table with the same data randomly ordered, then perform a query on the new table and destroy it afterwards, which becomes extremely slow with large numbers of posts.
So instead, lets do the random part in PHP, the plan:
- Ask for a single post from the database
- Figure out which post to pick from PHP
First, lets figure out how many hadices
posts there are using wp_count_posts
:
$counts = wp_count_posts( 'hadices', false );
Then, lets generate a number between 0 and the maximum number of posts:
$counts = wp_count_posts( 'hadices', false );
$offset = mt_rand( 1, $counts->publish );
Then, lets tell it to use the number we generated as an offset, so if the offset is 12, then we want the 12th hadices
post, if the number is 212, we want the 212th hadices
post.
So lets plug that into our improved query, by using the offset to request the page, and having 1 post per page:
$q = new WP_Query([
'post_type' => 'hadices',
'posts_per_page' => 1,
'page' => $offset,
]);