• I’ve created a date.php template, where the visitor browse the archive of the custom posts grouped by year. I would like to create a slightly helpful structure, giving the possibility to browse the archive by category.

    Is there a way to retrieve the list of terms of a custom taxonomy for the custom posts in a certain archive? For example if you choose “2016” and you arrive at the archive page of the custom posts written in 2016, is there a way to retrieve the list of the categories of all the posts of that year?

    I’ve searched if existed a plugin that would expand the arguments for wp_list_category, giving the possibility to refer to the date, but I’ve not found nothing.

    If it was found that for that year the categories would be “Cat A” and “Cat B”, if you would click on the link, you would go to the url “www.sitename.com/custom-taxonomy-cat-a” or to “www.sitename.com/2016/cat-a”?

    The page I need help with: [log in to see the link]

Viewing 15 replies - 1 through 15 (of 16 total)
  • Moderator bcworkz

    (@bcworkz)

    The only way to get category terms assigned to a limited set of posts is to loop through the posts and build a collection of assigned terms. The code to do so is fairly straight forward, except you must not add terms to the collection if they are already there, you don’t want duplicates!

    The links associated with these terms can lead to any link you want. example.com/custom-tax/term-a/ would be a valid link. example.com/2016/term-a/ would not be a valid link unless you added a rewrite rule along with the necessary processing code to handle such a link. It’s possible to query for posts in a certain date range that have a particular term assigned, but it’s not a standard WP query, you’d need to develop a custom WP_Query object to get such results.

    That may sound rather complicated, but you will find that the requisite code isn’t all that bad, but it is a bit more than cut and paste style coding.

    Thread Starter fibbo8

    (@fibbo8)

    Hy bcworks,
    thanks for your reply.
    I get an idea of what you’ve explained.
    I’m not a programmer, but I’m giving it a try to reach something.

    Thread Starter fibbo8

    (@fibbo8)

    I’ve made two tries, but in both cases I don’t get what I want.
    I’ve tried to use two different functions, but I’m stuck on how change them.

    With the following snippet I use get_terms and can retrieve all the terms of the custom taxonomy. It seem to ignore the year.

    global $post;
     $archive_year = get_query_var('year');
     $args = array(
      'taxonomy' => 'tassonomia_marca',
      'post_type' => 'marca',
      'orderby' => 'name',
      'order' => 'DESC',
      'date_query' => array(
        'year' => $archive_year
         )
      );
      $posts = get_posts($args);
      if ($posts) {
        $terms = get_terms( array(
          'taxonomy' => 'tassonomia_marca',
          'hide_empty' => true,
        ) );
        foreach($terms as $term) {
          if ($term->parent == 0) { //check for parent terms only
          $term_link = get_term_link( $term );
          echo '<li class="cat-item" style="list-style: none;"><a href="' . esc_url(     $term_link ) . '">' . $term->name . '</a></li>';
        }
      }
     wp_reset_postdata();
    }

    With this second snippet I use wp_get_post_terms anche I can retrieve the terms associated to the posts of a certain year.
    The drawback is that I obtain a list where there are “duplicated”. If there are two posts associated to “Cat A”, “Cat A” appeares two time in the list.

    global $post;
    $archive_year = get_query_var('year');
    $args = array(
      'taxonomy' => 'tassonomia_marca',
      'post_type' => 'marca',
      'orderby' => 'name',
      'order' => 'DESC',
      'date_query' => array(
        'year' => $archive_year
        )
       );
       $posts = get_posts($args);
        if ($posts) {
    	  foreach ( $posts as $post ) {
    	    setup_postdata( $post );
    		  $terms = wp_get_post_terms($post->ID, 'tassonomia_marca', array("fields" => "all"));
    		    foreach($terms as $term) {
    			  if ($term->parent == 0) { //check for parent terms only
    			   $term_link = get_term_link( $term );
    			     echo '<li class="cat-item" style="list-style: none;"><a href="' . esc_url( $term_link ) . '">' . $term->name . '</a></li>';
    			  }
    			}
    		}
        wp_reset_postdata();
       }

    Any hint on how could change the snippet?

    Moderator bcworkz

    (@bcworkz)

    Not a programmer eh? A mighty fine effort then!

    When you get_terms(), the query cannot use date criteria because dates are associated with posts, not terms. get_terms() gets terms irrespective of what posts they may be assigned to. wp_get_post_terms() is what you want, but as you discovered, the duplicates need to be dealt with.

    There are a few ways to deal with this. One deciding factor is if you would like the link output to be ordered somehow, or if output as encountered is fine. I’ll address the ordered approach. Even so, you don’t have to order the links if you don’t want to.

    The general concept is to accumulate terms into a temporary array as a sort of buffer, mainly so that duplicates can be identified and eliminated before output. This buffer array also gives us a chance to reorder the terms before output. Start out by declaring an empty array before looping through posts. You can add elements to such an array with something like $term_buffer[] = $term_object;. The thing is though, the return from wp_get_post_terms() is already an array of term objects. The above assignment would give us an array of arrays of term objects that is difficult to work with. To avoid this, loop through the return from wp_get_post_terms() and assign individual objects to the buffer array.

    But wait! Before adding individual objects to the buffer, let’s be sure there is not an identical term object already in the buffer. You can use in_array() to check for this.

    After all the posts have had their terms extracted and unique ones added to the buffer, you can reorder the buffer if you like. Because you would be ordering objects, you’d need to use usort() with a custom comparison function to do so.

    Finally, loop through the buffer and output your desired links. Lots of loops!

    Thread Starter fibbo8

    (@fibbo8)

    Hi!

    Thanks for your new reply.

    Theory perfectly explained. Now I have a stronger idea of how differently work the two functions and how I can try to use them.

    I’ll give new tries following your advices.

    Thanks again

    Thread Starter fibbo8

    (@fibbo8)

    I think I’ve reached what I need.
    Maybe it isn’t optimizated, but it works.

    <?php
    	global $post;
    	$archive_year = get_query_var('year');
    	$args = array(
    		'taxonomy' => 'tassonomia_marca',
    	    'post_type' => 'marca',
    	    'orderby' => 'name',
    	    'order' => 'DESC',
    	    'date_query' => array(
    	        'year' => $archive_year
    	    )
    	);
    	$posts = get_posts($args);
    	$cachearray = array();
    	if ($posts) {
    		foreach ( $posts as $post ) {
    	        setup_postdata( $post );
    	        $terms = wp_get_post_terms($post->ID, 'tassonomia_marca',array("fields" => "all"));
    			foreach($terms as $term) {
    				if ($term->parent == 0 && is_array($cachearray)) { //check for parent terms only and empty array
    					$cachearray[] = $term;
    				}
    			}
    		}
    		wp_reset_postdata();
    	}
    	$results = array_unique($cachearray, SORT_REGULAR);
    	foreach($results as $result) {
    		$term_link = get_term_link( $result );
    		echo '<li class="cat-item" style="list-style: none;"><a href="' . esc_url( $term_link ) . '">' . $result->name . '</a></li>';
    	}
    ?>
    Moderator bcworkz

    (@bcworkz)

    Nice work, well done! The fact it works is generally all that matters. There is usually room for optimization, but it’s often not worth the effort. The fact it’s well organized, clear, and readable to other coders is often more important than being optimal. There are always exceptions, but good rules of thumb in my mind.

    Thread Starter fibbo8

    (@fibbo8)

    Thanks! ^_^
    But I haven’t really finished all the work -_-
    I’ve retrieve this list of term inside a date.php template, after I’ve chosen a year from a menu.

    If click on cat-A term it brings me (correctly) to mysite/custom-taxonomy/cat-a,
    where I can see all the posts belonging to that category.
    But I need only the posts of cat-a published in that certain year.
    Is there a way to retrieve the reference for the year to set the ‘date_query” argument in the archive.php or in the taxonomy-custom_taxonomy template?

    Moderator bcworkz

    (@bcworkz)

    Ah, OK. I was going on the fact you said it was working and a quick review of the posted code looked really well done. I didn’t analyze it to confirm it met all of your goals ?? No worries, I’m not going anywhere, we’ll get the rest figured out.

    When you follow a link, as far as post queries go, it’s start over anew. The link destination has no idea about what year you want listed unless you tell it. mysite/custom-taxonomy/cat-a tells code nothing about what date it’s supposed to query. There are a number of ways to pass the desired year on to the link destination. The simplest is to just add the year as an URL parameter. Modify your link so it’s similar to mysite/custom-taxonomy/cat-a/?year=2017 .

    Unfortunately, that’s probably not enough. Give it a try anyway. If it does work, you can ignore the rest of this post ?? The destination template’s query doesn’t know that it should be looking for a date argument. You need to explicitly add it to the destination query’s query vars. Any post query can be modified by hooking into the “pre_get_posts” action.

    This is a very important action for modifying anything post query related. There’s much documented about it, but your need is rather simple. But if you’ve not encountered hook coding before, the concept can be very confusing. This section from the Plugin Handbook may help you understand the concept better.

    When you write a callback function for “pre_get_posts”, it’s good practice in most cases to use a conditional if statement that verifies ( ! is_admin() && $query->is_main_query()) (assuming your function collected the passed query object in the $query variable) The other thing you will want to check for is that this is for your taxonomy query and that there is a “year” element in $_GET. This all prevents your code from improperly modifying other post queries.

    Check for your taxonomy argument by trying to get your taxonomy slug query var. $query->get('custom-taxonomy') This should return a term name like “cat-a”. If not, it’s not a taxonomy query and return without changing anything. You can check for the “date” argument in $_GET with array_key_exists().

    If everything checks out, set the “year” query var to the values passed in $_GET[‘year’]`. You could set the “date_query” query var to the appropriate array instead, but setting “year” is simpler. Once “year” query var is set, the resulting post listing will be restricted to posts in that year plus whatever other criteria is used, such as custom taxonomy term “cat-a”. You can also set or unset any other query vars as needed. For example, maybe you need to set the “post_type” query var to ‘marca’. You have complete control over the post query here!

    I’m sure this seems way more complicated than I made it sound at the onset. It’s much harder to explain and understand than it is to code. You will find the resulting code to do all of this will amount to a half dozen lines. It’s not so complicated once you understand the concept.

    One last thing. Don’t worry about the following right now, but before your code is ready for production use, you need to add some security code because of your use of values from $_GET. Such values can be spoofed by hackers, so you need to protect your site from such attempts by <a href=”https://codex.www.ads-software.com/Validating_Sanitizing_and_Escaping_User_Data”>sanitizing and validating</a> any values taken from $_GET. Even though normally the year value comes from links generated by your code, a bad actor can alter the URL, so the value must be treated with suspicion like any other user input.

    Oh, one more last thing. I noticed one problem in your get_posts() arguments in your last posted code. You have 'taxonomy' => 'tassonomia_marca', in the arguments array. This is improper usage and is ignored by the WP query code. The reason it seems to work is coincidental, all of your custom post types apparently have terms assigned from this taxonomy, so the extra taxonomy criteria is moot. To properly use a taxonomy criteria in post queries, it should be in the form of a <a href=”https://codex.www.ads-software.com/Class_Reference/WP_Query#Taxonomy_Parameters”>”tax_query”</a&gt; argument, which is a series of nested arrays. Within your $args array, you want something like this:

    'tax_query' => array(
    	array(
    		'taxonomy' => 'tassonomia_marca',
    		'operator' => 'EXISTS',
    	),
    ),
    Thread Starter fibbo8

    (@fibbo8)

    Hi bcworkz,

    thank again for your reply.
    Don’t worry, your explanations are always good and give me many insights.
    I’ll give also these tries, hoping to create something.

    I’ve also tried for another solution. It roughly works, but I think is not the best from a point of view of SEO and navigation. An archive build the way your post explains would be much more user-friendly and SEO-friendly.
    Basically I’ve continued to use the date.php template. I’ve transformed each element of the list of category in a <h2> element, then I’ve used them as a title for a section where I’ve retrieved the posts belonging to that category.
    I’ve some problems with pagination, but the biggest problem is that the URL
    remain:
    mysite.com/2013/?post_type=marca
    mysite.com/2013/page/2/?post_type=marca

    So I will try to implement this other solution.
    Thanks for your help!

    Moderator bcworkz

    (@bcworkz)

    You definitely want to study the use of “pre_get_posts” action. You can target what ever template you like with different URLs that you think provide the best SEO. Of course, any default template is going to fail to deliver what you want without changing something. “pre_get_posts” lets you change the query run for any default template. Your link could be mysite.com/2013/?term=cat-a . By the existence of $_GET[‘term’], you can set the “tax_query” query var to limit the query by taxonomy term, as well as setting the “post_type” query var to “marca”, plus any other criteria you might need.

    You could target a taxonomy term archive just as easily. The key is that all necessary data needs to be in the URL. Other criteria like “post_type” can be deduced by the presence of specific URL data.

    Thread Starter fibbo8

    (@fibbo8)

    Hi!

    thanks again.

    Finally I try for your first advice. Now on the taxonomy.php I can show the post filtered according year.
    If is used get_posts function to retrieve the posts, WP is able to “interpret” the parameter for the year inserted in the URL. I can’t be able to do the same using WP_query.

    In my opinion this is a better setting from the point of view of navigation.
    Now the URL for the cat-a:
    mysite.com/marca/cat-a?anno-rif=2013
    My doubt is if Google’s crawler is able to “construct” this URL structure or if I need to implement a rewrite rule to give the possibility to indexing these pages.

    <div class="col-md-10 hidden-sm hidden-xs" id="col-post">
    	<?php
    		$anno_imm = get_query_var('anno-rif');
    		$terms_imm = get_term_by( 'slug', get_query_var( 'term' ), get_query_var( 'taxonomy' ) );
    		if(!empty($anno_imm)) { // check if user "arrive from date.php and show the year
    		  echo '<h2 class="text-center title">Auto marca ' .$terms_imm->name. ' immatricolate ' .$anno_imm. '</h2>';
    		}
    		else {
    		 echo '<h2 class="text-center title">Auto marca ' .$terms_imm->name. '</h2>';
    		}
    
    	?>
    	<section>
    		<div class="row">
    			<?php
    			$archive_year = get_query_var('anno-rif');
    			$terms = get_term_by( 'slug', get_query_var( 'term' ), get_query_var( 'taxonomy' ) );
    			$posts_in = get_posts( array (
    				'post_type' => 'marca',
    				'orderby' => 'date',
    				'order' => 'ASC',
    				'tax_query' => array(array(
    					'taxonomy' => 'tassonomia_marca',
    					'field' => 'slug',
    					'terms' => $terms->slug
    				) ),
    				'date_query' => array(
    			        'year' => $archive_year
    			    )
    			) );
    
    			foreach ( $posts_in as $post_object ) {
    
    				setup_postdata( $GLOBALS['post'] =& $post_object );
    
    				?>
    			   <div class="col-md-2 view-arch">
    			        <li>
    						<?php
    						    /* Display the thumbnail only when available. */
    						    if ( has_post_thumbnail() ) :
    						?>
    					        <a class="example-image-link" data-lightbox="example-set" data-title="" title="<?php the_title_attribute(); ?>" href="<?php the_permalink(); ?>">
    					            <?php the_post_thumbnail(); ?>
    					        </a>
    						<?php
    						    /* Remember that we had a conditional check open; close it. */
    						    endif;
    						?>
    					</li>
    					<p><?php the_title() ?></p>
    			    </div>
    				<?php
    			}
    
    			wp_reset_postdata(); // like wp_reset_query() but for $post
    
    			?>
    		</div>
    </section>
    Moderator bcworkz

    (@bcworkz)

    You should be using Google Search Console. Under the “Crawl” menu item, there is an “URL Parameters” page. You can tell Google the meaning of any URL parameters you are using. Whether this is more or less effective than all elements contained in the main URL, I couldn’t say. I think it’s easier than making rewrite rules though.

    Yeah, get_posts() is fine. The end result appears the same. I’m not sure why WP_Query would not work, as get_posts() is actually just a wrapper for WP_Query. No matter, if you’re happy with get_posts() and it works for you, then it’s all good.

    Thread Starter fibbo8

    (@fibbo8)

    I think so too that an URL’s structure with the query args is better to manage.
    In some cases it is used also in big sites, like Amazon or Ebay.

    I’ve tried to used also Wp_Query, but it seems not to work in my code.
    Not insisted that much to understand where I mistaked, because I’ve changed only the function and the code began to work.
    Maybe this setup is more “scratch” and “resource hungry”, but I proceed with small steps ??

    Moderator bcworkz

    (@bcworkz)

    In case you are interested, the same arguments for get_posts() will work in WP_Query, but the setup before the loop is a bit different. It would look something like this:

    $query = new WP_Query( array(
    	'post_type' => 'marca',
    	'orderby' => 'date',
    	'order' => 'ASC',
    	'tax_query' => array(array(
    		'taxonomy' => 'tassonomia_marca',
    		'field' => 'slug',
    		'terms' => $terms->slug,
    	) ),
    	'date_query' => array(
    		'year' => $archive_year,
    	),
    ) );
    foreach ( $query->posts as $post_object ) {
    	//remainder identical to yours

    Comparing your failed WP_Query usage to this proper usage could be instructive.

    If you were to look at the source code for get_posts(), after some preprocessing of the arguments array to set some defaults where needed, the function’s code does this:

    $get_posts = new WP_Query;
    return $get_posts->query($r);

    where $r is the passed arguments array.

    So in using get_posts(), you really are using WP_Query! get_posts() is a procedural approach and WP_Query is an OOP approach. Otherwise they literally do the same thing. Which approach to use is a matter of preference.

Viewing 15 replies - 1 through 15 (of 16 total)
  • The topic ‘Retrieve list of categories inside date.php template’ is closed to new replies.