Support » Developing with WordPress » get_next_post options

  • When

    $in_same_term = true

    when using the get_next_post and get_previous_post functions, and the current post has several parent categories and several child catgeories:

    – will the function pick the next/previous post only from the current posts parent categories or will it also consider child categories?

    – if the post has several categories, let’s say: first, second and third category (both parent and child categories), will it choose a post from all 3 categories, or only from the first?

    And if the current post has categories A and B, then will an other post that has the categories A and B have priority in the next/prev choice, even if its publish date falls further away than let’s say an other post, that has categories A and C?

    And is there any difference in the choices of the functions in comparison, when using get_next_post & get_previous_post, get_adjacent_post and get_{$adjacent}_post_where ?

Viewing 15 replies - 46 through 60 (of 63 total)
  • Moderator bcworkz

    (@bcworkz)

    Any output that is sourced from the DB or external input should somehow be escaped. How depends on the nature of the content.
    https://developer.wordpress.org/plugins/security/securing-output/

    The ::before and ::after bits are insertions made by the dev tool to indicate content added by CSS. They do not occur in normal HTML, you should remove those. A better source for HTML for use in code is the page source view, reached with Ctrl/Cmd+U in most browsers.

    Class prefixes don’t matter to me, but for the right CSS to be applied you need to use the proper prefixes in the code you actually use.

    Unless a space occurs within quotes; it, as with most whitespace, has no special meaning in code. Whitespace is a way to visually organize code so it’s more readable. We generally place spaces before and after concatenation dots so it’s more clear they are there. Some coders drop the space if it would be preceded/followed by a quote character. Because quotes in code are “up in the air”, there is already visual space between the dot and other characters. A stylistic choice, it’s neither always right or wrong to do so.

    Your code has a number of syntax errors. Do you have WP_DEBUG defined as true in wp-config.php? When you do, PHP tells you about syntax errors it encounters, usually making it easier to correct. Sometimes the error message is cryptic, but at least you know where to start looking for some kind of problem. Sometimes the root cause of the error occurs well above where PHP finally realizes there’s a problem.

    For one thing, the function name declaration must always be followed by () before the {//rest of the code}
    function in_same_term_prev_next_post() {

    You’re not concatenating the URL and title very well. You have to close out the current quoted text before introducing PHP. You essentially have

    $output .= '<a href="https://previouspost.link">
       esc_url( get_the_title( $nextpost->posts ). ); 
    </a>';

    There are a number of problems here.
    1. $output doesn’t exist yet, so you cannot concatenate to it. At the first appearance of $output, drop the dot in .=. Subsequent occurrences are fine with .=.

    2. Close out quotes before concatenating in PHP
    $output = '<a>' . esc_url('http://foo.com') . '</a>;

    3. The esc_url() statement should not terminate with a ; if you are concatenating more text. The final terminating ; belongs to the initial $output =, it mustn’t occur anywhere else in between.

    4. You have extra concatenation dots within esc_url() at the start and end. They belong outside the function call as shown in 3. Instead of a very long sequence of concatenation, for readability you might want to break it up into a series of much shorter $content .= 'foo';-like statements.

    5. Separate the URL and title. Your <a> link has a fixed, invalid URL as href attribute. This is where the next permalink belongs. Only the title belongs between <a> and </a> tags.

    Elsewhere in your code, 'operator' => 'AND', does not belong in the general args. It’s only for within various sub-queries like ‘date_query’.

    You should probably add 'ignore_sticky_posts'=>'false', just in case stickies are used some time in the future.

    The post(s) found by the query are always in an array, even if there’s only one post found. To get to the actual post object you need to do $nextpost->posts[0].

    Right now your code has the same links twice. You need another new query to get the previous post. Change both ‘order’ and the date query args to get previous post data. There’s a way to get both next and previous in the same query, but that’s probably best left for a future exercise.

    Thread Starter berry metal

    (@erikalleman)

    I made a screenshot:

    https://i.imgur.com/TgOhXAs.png

    In the HTML, the title is B, the link is C.

    But in the code (A), the title and the link is concatenated.

    So should I split the code A into 2 parts, title and link, and insert it in the place of B and C respectively?

    Or should I insert the whole A code replacing B only, or replacing C only?

    I got cunfused about this, and I cannot move on from this point…

    Thread Starter berry metal

    (@erikalleman)

    I took the code A as an example from an other link output from my functions.php.

    But that is only 1 string.

    On the other hand, in my HTML of my current links, the link and the title is shown at 2 different places.

    Thread Starter berry metal

    (@erikalleman)

    I know that I must output the whole HTML, I just don’t know how to integrate code A into the HTML and exactly where. I know that the string need to be replaced in the HTML, I just don’t know what and where.

    Thread Starter berry metal

    (@erikalleman)

    Thanks for the suggestions. The question if I should use a shortcode…

    I am afraid I have no choice, because my post template is a page builder template (wpBakery / Templatera), and it’s shortcode based.

    I like using page builders for templates because they make the process easier and there is no binding because global changes can easily be made (like changes in the template or change of template software), and I can change to text view anytime. If I change the view to text mode, then it’s text based like a real text template but it’s still shortcode based.

    Maybe the text mode of my page builder template accepts PHP code, within the WP text editor, instead of a shortcode? I don’t know.

    That surprises me that shortcodes are meant for post content, because if shortcodes are used in post content, global changes in post structure are very difficult to make because then every post need to be individually edited.

    That is why I rather use a grid in my post template to dynamically fetch post content with a dynamic query in my posts, instead of using shortcodes in my posts. So it’s only text in my posts / post types.

    That’s why I must use do_shortcode to output my links, something is yet to figure out after I sorted out the HTML issue.

    Thread Starter berry metal

    (@erikalleman)

    How did you imagine to do it without a shortcode?

    That would be PHP code in the template, right?
    My template is shortcode based, and if it does accept plain PHP code, are there any advantages over the shortcode?

    Thread Starter berry metal

    (@erikalleman)

    Next/prev links are not part of my post content, but they are in my post template.
    The post content is provided by a grid in my post template that is fetching post types and other content and assembles it into a post.

    Moderator bcworkz

    (@bcworkz)

    The link/title HTML output code should be roughly like this:

    $output = '<a href="'. esc_url( get_permalink( $nextpost->posts[0])) .
       '" ><span class="ticon ticon-chevron-right prefix-ml-10"></span>' .
       get_the_title( $nextpost->posts[0]) . '</a>';

    Then make a new query for the other direction and concatenate its HTML to $output using the .= operator. I’ve omitted the containing divs in this example for simplicity and to focus on the link tags alone.

    Pay close attention to quotes, they’re easy to get confused and mismatched. Using a programming editor with syntax highlighting is a big help. Such editors also typically highlight matching characters like {} [] () when the cursor in on one of them. If there’s no match, there’s almost always a syntax error somewhere.

    In you screen grab, note that in “C”, the href attribute value is invalid. It needs to be the destination post’s permalink. “B” is a static value. You need it to be the destination post’s title. “A” are the correct values to use in B and C, but you’re running them together. Permalink goes with the href attribute and title goes between the a tags.

    Editors don’t normally allow PHP code. Some plugins alter that restriction, but tend to introduce security vulnerabilities by doing so. Shortcodes are better IMO. Putting nav links within page content is a little odd. You’re essentially making a page into a custom single page template by way of shortcodes. Without decent PHP skills, I understand why one would do that. The problem is you become constricted to what shortcodes are available. To go beyond that, you still need to code PHP.

    If you can code PHP, it’s better to create a custom theme template. You’re currently stuck in a transition limbo where you’ve yet to develop confidence to build a custom PHP template, yet you have a need for custom shortcodes. If that’s working for you, it’s all good ๐Ÿ™‚ Thanks for explaining, it helps me understand where you’re at.

    I think we’re talking about two different kinds of templates. You mean a page editor template (I think) and I mean a theme template. No PHP in editor templates, theme templates are all PHP (and HTML)

    Thread Starter berry metal

    (@erikalleman)

    Here is the original HTML, and it’s not vertically symmetric, I don’t know why:

    <div class="plugin-post-next-prev textright postnavigation" style="font-size:1.4em;">
    
      <div class="plugin-col prefix-inline-block prefix-mr-0">
        <a href="https://previouspost.link" class="theme-button graphical blue prefix-text-center prefix-max-w-100">
       		<span class="ticon ticon-chevron-left prefix-mr-10">
       			
       		</span>
       	         Previous Post Title
        </a>
      </div>
    
      <div class="plugin-col prefix-inline-block prefix-ml-0">
       	<a href="https://nextpost.link" class="theme-button graphical blue prefix-text-center prefix-max-w-100">
       		     Next Post Title
       	    <span class="ticon ticon-chevron-right prefix-ml-10">
       	    	
       		</span>
       	</a>
      </div>
    
    </div>

    So there is a wrapper, and the 2 columns containing each link, next and prev.

    And this is my new code:

    
     add_shortcode( 'in-same-term-prev-next-post', function in_same_term_prev_next_post() {
    
    $post_object = get_queried_object();
    $terms = wp_get_post_terms( $post_object->ID, 'category', array( 'fields' => 'ids' ) );
    $date_obj = get_post_datetime( $post_object );
    $next_args = ['after' => $date_obj->format('d-M-Y H:i:s'),];
    $prev_args = ['before' => $date_obj->format('d-M-Y H:i:s'),];
    
    $next_args = array(
    
        'post_type' => 'post',
        'ignore_sticky_posts'=>'false',
        'posts_per_page' => 1,
        'order' => 'ASC',
        'category__and' => $terms,
        'date_query' => array(
            'operator'  => 'AND',
            array(
                'after' => $post_object->post_date,  // Get the post after the current post, use current post post_date
                'inclusive' => false, // Don't include the current post in the query
            )
        ) 
    );
    
    $prev_args = array(
    
        'post_type' => 'post',
        'ignore_sticky_posts'=>'false',
        'posts_per_page' => 1,
        'order' => 'DESC',
        'category__and' => $terms,
        'date_query' => array(
            'operator'  => 'AND',
            array(
                'before' => $post_object->post_date,  // Get the post after the current post, use current post post_date
                'inclusive' => false, // Don't include the current post in the query
            )
        ) 
    );
    
    $nextpost = new WP_Query( $next_args );
    $prevpost = new WP_Query( $prev_args );
    
    $output = ' <div class="plugin-post-next-prev textright postnavigation" style="font-size:1.4em;">
    
      <div class="plugin-col prefix-inline-block prefix-mr-0">
        <a href="'. esc_url( get_permalink( $nextpost->posts[0])) .
       '" class="theme-button graphical blue prefix-text-center prefix-max-w-100">
            <span class="ticon ticon-chevron-left prefix-mr-10">
               
            </span>
    
              ' .get_the_title( $nextpost->posts[0]) . '</a>';
    
        
      </div>
    
    $output .=  <div class="plugin-col prefix-inline-block prefix-ml-0">
        <a href="'. esc_url( get_permalink( $prevpost->posts[0])) .
       '" class="theme-button graphical blue prefix-text-center prefix-max-w-100">
    
               ' .get_the_title( $prevpost->posts[0]) . '
    
            <span class="ticon ticon-chevron-right prefix-ml-10">
                
            </span>
    
        </a>';
    
      </div>
    
    </div> ';
    
    return $output;
    
    } );

    I can use the [in-same-term-prev-next-post] shortcode, and I don’t need do_shortcode, if I just want to put this shortcode into my shortcode based page builder template (not text based theme template), can’t I?

    I made 2 separate args for the queries, I think that was necessary: $nextargs and $prevargs.

    I added $prevpost.

    And I added
    $prev_args = ['before' => $date_obj->format('d-M-Y H:i:s'),];

    But I am not sure that the line above is correct.

    Since my code is both PHP and HTML, I am not usre that the text editor can highlight both correctly in the same time, because only one format can be defined for one file.

    The syntax highlighting is still inconsistent between the next and prev PHP/HTML.

    The next seems to be the correct highlighting, but not the prev.

    The output starts with the wrapper HTML, and there is one concatenation at the beginning of the previous link.

    I tought maybe I should insert a concatenation operator in front of both previous and next links, or is that not necessary?

    But if I made different args and different nex/prev variables, is concatenation still necessary, or I could just define output for the whole HTML without any concatenation?

    I am not sure why is the concatenation necessary, because the CSS takes care of the alignment of the 2 links.

    I was looking for ‘before’ and ‘after’ in PHP docs, I didn’t find anything.
    Are those hard coded by WordPress?

    Whey do they come from?

    Thank you!

    • This reply was modified 8 months, 3 weeks ago by berry metal.
    • This reply was modified 8 months, 3 weeks ago by berry metal.
    Moderator bcworkz

    (@bcworkz)

    If by vertically asymmetric you mean the span tag positioning varies, that’s by design. After removing the whitespace, as browsers normally do, you get bilaterally symmetric output like so:
    ยซ prev title next title ยป

    There’s little reason to use do_shortcode() on your own shortcode. Instead just call the handler function directly and echo the return. With shortcode from elsewhere, the handler function is often difficult to determine so we let do_shortcode() figure it out for us. Since normal PHP doesn’t work in a page builder, it’s not an option anyway. You then use the shortcode directly in page content.

    Correct, different args for different queries.

    The $date_obj = get_post_datetime( $post_object ); line and the two that follow with the date obj format code serve no purpose and can be removed. You overwrite the assigned values below this code. In that code you use $post_object->post_date instead of formatting a date obj, which is fine, two ways to accomplish the same thing.

    If your code editor has trouble syntax highlighting a mix of PHP and HTML, it’s not very useful for PHP! Before blaming the editor though, be sure you’re not the problem by introducing some syntax error ๐Ÿ™‚ Failure to close out quotes or ?> or put them in the wrong place can cause the effect you describe. In fact, your latest code does contain syntax errors, more on this below.

    Your output code consists of a mix of strings and variables. You certainly need concatenation in places to get all of that into a single $output variable. Do you need it where you’re asking about? IDK, it’s unclear to me where you are referring to. It may not matter, I have found syntax errors in your code. I don’t think it’s a concatenation issue as much as mis-located quotes.

    The closing </div> tag of the previous link block occurs outside of quotes, hence gets directly output. Move it into the quoted string above:

              ' .get_the_title( $nextpost->posts[0]) . '</a>
      </div>';

    The next $output line is missing an opening quote. Do this:
    $output .= '<div class="plugin-col prefix-inline-block prefix-ml-0">

    The next link’s closing </a> has '; that doesn’t belong, remove them. That’s taken care of after the final closing </div>.

    That should get your code working as expected, however there is some need of refinement to handle first/last conditions where there is no previous or next post to be found. If you don’t you’ll get a bunch of undefined index 0 warnings logged. The page will look OK (if WP_DEBUG is false), the warnings are non-fatal. Each direction’s $output statement needs to be wrapped in a conditional if () : ... endif; block where the returned posts array is checked for emptiness. For example:
    if ( ! empty( $nextpost->posts )) :
    Then $prevpost for the other one.

    But now the initial $output assignment might not happen, causing subsequent undefined variable errors later on. To resolve, before any conditional, do $output = ''; so that it’s always defined. Then change the current assignment to the concatenation form .=.

    Yes ‘before’ and ‘after’ date query args (and all WP_Query args) are a WP thing. They are not part of formal PHP.

    Thread Starter berry metal

    (@erikalleman)

    I had to put ;' before the if ( ! empty( $prevpost->posts )) :, because otherwise the syntax highlighting of if ( ! empty( $prevpost->posts )) : was not correct.

    There are two “if”s so I am not sure where to put the first “endif”.

    After I inserted $output = '';, the syntax highlighting is correct!

    Yes I meant the vertical assymmetry of the tag positioning.

    Oh I got it now, the chevron span is assymmetrically placed just to position the chevron before and after the link. Thanks!

    So the code is correctly highlighted.

    I checked the code in phpcodechecker.com, and there is an error in row nr. 3:

    https://i.imgur.com/gFUNmSV.png

    What could that error be?

    Otherwise do you think the code is done?

    Thanks!

    Moderator bcworkz

    (@bcworkz)

    Ah, that error is kind of a special case regarding function names. When you pass a procedural function name (anonymous functions and class method names are handled a little differently) to another function as a parameter; when passed to functions like do_shortcode(), add_action(), etc., you pass it as a string literal, so there is no () for parameters.

    add_shortcode( 'in-same-term-prev-next-post', 'in_same_term_prev_next_post');
    function in_same_term_prev_next_post() {
       // the rest of the function's code here
    }

    The way to handle if/endif in this particular case is like so:

    if ( ! empty( $prevpost->posts )) :
      // prev link code here
    endif;
    if ( ! empty( $prevpost->posts )) :
      // next link code here
    endif;

    With the above and previous adjustments, your code should be ready to go! Woohoo! ๐Ÿ™‚ Of course, there’s always a possibility of introducing some silly syntax error when making even minor edits. But then it’ll be a minor fix, if you (or I) are able to find it.

    Thread Starter berry metal

    (@erikalleman)

    Still one more error until the testing:

    The closing div tags highlighting is not consistent yet in the code:

    https://i.imgur.com/pRO65sl.png

    So something is not correct there.

    This is the current code:

    add_shortcode( 'in-same-term-prev-next-post', 'in_same_term_prev_next_post' );
    function in_same_term_prev_next_post() {
    
    $post_object = get_queried_object();
    $terms = wp_get_post_terms( $post_object->ID, 'category', array( 'fields' => 'ids' ) );
    
    $next_args = array(
    
        'post_type' => 'post',
        'ignore_sticky_posts'=>'false',
        'posts_per_page' => 1,
        'order' => 'ASC',
        'category__and' => $terms,
        'date_query' => array(
            'operator'  => 'AND',
            array(
                'after' => $post_object->post_date,  // Get the post after the current post, use current post post_date
                'inclusive' => false, // Don't include the current post in the query
            )
        ) 
    );
    
    $prev_args = array(
    
        'post_type' => 'post',
        'ignore_sticky_posts'=>'false',
        'posts_per_page' => 1,
        'order' => 'DESC',
        'category__and' => $terms,
        'date_query' => array(
            'operator'  => 'AND',
            array(
                'before' => $post_object->post_date,  // Get the post after the current post, use current post post_date
                'inclusive' => false, // Don't include the current post in the query
            )
        ) 
    );
    
    $nextpost = new WP_Query( $next_args );
    $prevpost = new WP_Query( $prev_args );
    
    $output = '';
    
    if ( ! empty( $nextpost->posts )) :
    $output .= ' <div class="plugin-post-next-prev textright postnavigation" style="font-size:1.4em;">
    
      <div class="plugin-col prefix-inline-block prefix-mr-0">
        <a href="'. esc_url( get_permalink( $nextpost->posts[0])) .
       '" class="theme-button graphical blue prefix-text-center prefix-max-w-100">
            <span class="ticon ticon-chevron-left prefix-mr-10">
               
            </span>
    
              ' .get_the_title( $nextpost->posts[0]) . '</a>
    
        
      </div>;'
      
    endif;
    
    if ( ! empty( $prevpost->posts )) :
    $output .=  '<div class="plugin-col prefix-inline-block prefix-ml-0">
        <a href="'. esc_url( get_permalink( $prevpost->posts[0])) .
       '" class="theme-button graphical blue prefix-text-center prefix-max-w-100">
    
               ' .get_the_title( $prevpost->posts[0]) . '</a>
      </div>';
    
            <span class="ticon ticon-chevron-right prefix-ml-10">
                
            </span>
    
        endif;
    
    </div> ';
    
    return $output;
    
    }

    After I corrected the top part of code as you suggested, I removed the ); part after the return $output; }, I think that was correct.

    But now when I run the code through the checker, my first “endif” is unexpected:

    https://i.imgur.com/KyUcxwq.png

    What could be wrong?

    Thanks!

    Moderator bcworkz

    (@bcworkz)

    Ah, I think I misled you some about the if() logic. Apologies. I forgot there’s an overall div containing both links. That needs to be outside of the if() logic since we always want that div. You’re going to need to break the initial concatentation to $output into two. Move the overall opening div into the initial $output = ''; statement so the first if() only contains the previous link HTML alone.

    Also, the final part of code with the span tags is still a bit messed up. They belong within the next link’s anchor and div structure, right in front of the closing </a> tag of the link. This way when one clicks the right chevron (placed there by the span’s CSS) instead of the title, the link is still followed. This behavior will match that of the previous post link.

    Finally, the last </div> tag that’s part of the always there overall div needs its own $output .= '</div>'; statement. It cannot hang out down there on its own ๐Ÿ™‚ It is already correctly outside of any if() logic, but needs to be made into proper PHP syntax.

    I believe those will finally correct all remaining issues, but there’s always the possibility of some silly blunder being introduced with any edit. Been there, done that ๐Ÿ™‚

    Thread Starter berry metal

    (@erikalleman)

    It works perfectly.
    Thanks!

    One question remains: what is going to happen if the theme developer will update the original html or css?

    Then I will have to update my code, is that right?

    Since this is not a filter, it’s not going to be theme update proof, so far as I know?

Viewing 15 replies - 46 through 60 (of 63 total)
  • The topic ‘get_next_post options’ is closed to new replies.