WordPress.org

Ready to get started?Download WordPress

Forums

[resolved] Archive List and Page for Custom Post Types - MySQL (9 posts)

  1. luispunchy
    Member
    Posted 3 years ago #

    I'll try to explain the issue and hope somebody can help me figure out the problem. It's a long post - you've been warned ;)

    I'm trying to build an Archive list that will show a list of years in which posts were published in specific custom post type (e.g. post_type = "news").

    And each year in that list needs to link to an archive page that (obviously) displays all published posts in that post_type for that year.

    Now I know that certain built-in WordPress functions don't work natively with Custom Post Types (CPTs) - such as wp_get_archives().

    There is a bit of code that supposedly adds archive functionality to wp_get_archives() for CPTs: http://bajada.net/2010/07/15/adding-custom-post-types-to-wp_get_archives

    However, using that I get errors when I put wp_get_archives('type=yearly') in my templates. So alternatively I tried querying the DB directly to build the archive list, and I get the same (I think) error.

    Let's say I have posts published in 2010 and 2009 in post_type = "news". I put the following in a sidebar include to build a by-year archive list for those posts:

    <?php wp_get_archives('type=yearly'); ?>

    That build a list of years that are links - like this:
    * 2010 with a URL of http://example.com/2010
    * 2009 with a URL of http://example.com/2009

    Then I have a custom page template, called 'archive.php' - I am using this ONLY for the CPT = "news", no other post types on my site need/have archives. I understand the template heirarcy will go to this template when WP is looking for an archive. And that does happen, because I can see that it is indeed using my custom page template.

    So on that archive.php template I have the following to query only my "news" posts:

    // custom query
    $args = array(
    'post_type' => 'news',
    'orderby' => 'date',
    'order' => ASC,
    'year' => $the_year_url
    );
    $cpt_query = new WP_Query( $args );
    
    // the loop
    if ( $cpt_query->have_posts() ) : while ( $cpt_query->have_posts() ) : $cpt_query->the_post();
    // etc... standard template tags

    But here's where I see strange errors.
    1. I only get that archive page for 2010. If I click on the 2009 archive link (built by <?php wp_get_archives('type=yearly'); ?>) I get my 404 error page instead.
    2. I only get that archive page AT ALL if I have at least one post (of post_type = post) stay published.

    So thinking maybe the problem was with wp_get_archives() and the fact that it is "hacked" with that code mentioned earlier, I tried a different approach to build that archive list. Query the SQL database directly:

    <ul>
    <?php
    $years = $wpdb->get_col("SELECT DISTINCT YEAR(post_date) FROM $wpdb->posts WHERE post_type = 'news' AND post_status = 'publish' ORDER BY post_date DESC");
    foreach($years as $year) : ?>
      <li><a href="<?php echo get_year_link($year); ?> "><?php echo $year; ?></a></li>
    <?php endforeach; ?>
    </ul>

    This build an archive list, and the links it generates appear to be exactly the same as those that <?php wp_get_archives('type=yearly'); ?> generated - i.e.
    * 2010 with a URL of http://example.com/2010
    * 2009 with a URL of http://example.com/2009

    The result on the archive page is the same - I get the same errors.

    Perhaps the only real lead I have right now is that if I disable my custom permalinks and use the WP default, both the <?php wp_get_archives('type=yearly'); ?> and sql query generated archive links look like this:
    * 2010 with a URL of http://example.com/?m=2010
    * 2009 with a URL of http://example.com/?m=2009

    "m" would be for month? So that isn't right, is it? The query string variable should be something like "?y=" for year, right? Would that cause the page errors I'm seeing. How would I fix that?

    So... I'm at a loss. I've stripped my code down to just the bare essentials for a test case... I've deactivated all plugins, etc. Can't figure out the problem. I must be missing something fundamental here. Any suggestions would be appreciated. I'll continue hammering away, but hope to get some tips here b/c I'm spinning my wheels right now. If you think it would help, I can put up some pastebin code, just let me know.

    Thanks!

  2. luispunchy
    Member
    Posted 3 years ago #

    Just found another point worth mentioning (if anybody has the desire to read all this!).

    I put a check in the archive.php page for the year variable passed in the query string:

    // what is the founding query string?
    global $wp_query;
    	if (!empty($wp_query->query_vars['year'])) {
    		$the_year_url = $wp_query->query_vars['year'];
    		echo 'The year is: ' . $the_year_url;
    	}

    This echos out the year 2010 when using my permalinks (not the WP default), but it doesn't work when I use WP default permalinks. So another "clue" - if it helps. I'm getting the sense there is something going on with the query string and the year variable specifically... but no idea how to address.

  3. MichaelH
    Member
    Posted 3 years ago #

  4. luispunchy
    Member
    Posted 3 years ago #

    I've since found a number of "custom post type archive" enabling plugins but didn't see or even consider looking for something like this. I'll check this out. Thanks.

  5. luispunchy
    Member
    Posted 3 years ago #

    hmmm.... I might be missing something about how that custom post permalinks plugin works or is intended to be used, but in my tests it isn't compatible with my existing site hierarchy / structure.

    My base "archive" for the "news" custom post type is a custom page template meant to only show news posts for the current year. It's URL permalink structure is: example.com/about/news.

    When I enable that permalink plugin, I don't get the 404's for my desired year-based archives any more (as described in my post above), but now any URL with that permalink of 'example.com/about/news' in the base redirects to the "base" page, that is the "current year" archive. So my year-based archives, while not 404'ing, are still not accessible.

    So... back to my original setup, without the plugin then... to debug (or more apt, learn what what I apparently don't yet understand about how my own code is interacting with WP's hierarchy system).

  6. luispunchy
    Member
    Posted 3 years ago #

    OK... I'll just keep updating here. If anything, its a way for me to keep track of my "progress" -- but of course ideally maybe somebody will catch something that I'm missing.

    My "current year" base archive for "news" custom post types is at 'example.com/about/news' - it uses a custom page template that is grabbing only the current year's "news" posts:

    $today = getdate(); /* we will only want to get current year */
    $args = array(
    	'post_type' => 'news',
    	'orderby' => 'date',
    	'order' => 'ASC',
    	'year' => $today['year'],
    	'posts_per_page' => '-1'
    );
    
    // and now store that custom query
    $cpt_query = new WP_Query( $args );
    
    // and finally, run the custom loop
    if ( $cpt_query->have_posts() ) : while ( $cpt_query->have_posts() ) : $cpt_query->the_post(); ?>
    // use whatever standard template tags...

    This works as expected - I get only the current year "news" posts.

    And then I'm trying to build my "past-years" archives (i.e. not current year but any previous years) using a different custom page template - my 'archive.php' template with the following:

    // grab the year from the founding query string
    global $wp_query;
    if (!empty($wp_query->query_vars['year'])) {
    	$the_year_url = $wp_query->query_vars['year'];
    }
    
    if ( $the_year_url != '' ) : // Query for PAST YEARS archive
    	$args = array(
    		'post_type' => 'news',
    		'orderby' => 'date',
    		'order' => 'ASC',
    		'year' => $the_year_url,
    		'posts_per_page' => '-1'
    	);
    
    // and then run that in a custom loop...

    In theory, to me, that should work. I just need to get there somehow.

    So I build a list of past-years and link to them, using either the wp_get_archives (with custom post enabling hack) or the direct MySQL query described in my original post. Either method gets the same results, which again looks like this:
    * 2010 with a URL of http://example.com/2010
    * 2009 with a URL of http://example.com/2009

    Note that I can see each page's founding query string as I've added echo print_r($wp_query->query_vars); to all my templates for debugging purposes (thanks MichaelH!)

    So my expectation was that those resulting URLs, with the 4 digit year in them (as above) would be automatically directed to my archive.php template (for past-years), with the 4 digit year in the URL picked up as the year value in the query string - i.e. $wp_query->query_vars['year']

    But this only works for http://example.com/2010. I've got "news" published in 2009, but http://example.com/2009 goes to a my index.php template's 404 catchall. The query string array does show [year] => 2009 but archive.php isn't getting used. Why isn't this working?

    What a convoluted story... I am missing something, I know it.

  7. luispunchy
    Member
    Posted 3 years ago #

    Wow, I got it. ;)

    I noticed that if I append "2009" or "2010" (or any value) to my base "current year" page's URL (example.com/about/news), rather than being picked up as 'year' values in the query ($wp_query->query_vars['year']) and automagically triggering the use of an archive.php template via WP's hierarchy scheme (which was a fundamental misunderstanding on my part), those query values are are instead picked up as $wp_query->query_vars['page'] ---- again, I see this when i run echo print_r($wp_query->query_vars); on the page. Which means I can use those values in my custom query / loop on that same base page template.

    Replacing $wp_query->query_vars['year'] in my earlier code with $wp_query->query_vars['page'] and using that value as a variable in my query got my year-based archives.

    In my page template, the main query and loop:

    // check the founding query string for 'YYYY' year value passed as 'page' query var
    global $wp_query;
    if (!empty($wp_query->query_vars['page'])) {
    	$the_year_page = $wp_query->query_vars['page'];
    }
    
    if ( $the_year_page != '' ) : // Query for YEARS PAST archive
    	$args = array(
    		'post_type' => 'news',
    		'orderby' => 'date',
    		'order' => 'ASC',
    		'year' => $the_year_page,
    		'posts_per_page' => '-1'
    	);
    
    else : // Query for CURRENT year archive
    	$today = getdate(); /* we will only want to get current year */
    	$args = array(
    		'post_type' => 'news',
    		'orderby' => 'date',
    		'order' => 'ASC',
    		'year' => $today['year'],
    		'posts_per_page' => '-1'
    	);
    endif;
    
    // store that custom query
    $cpt_query = new WP_Query( $args );
    
    // and run the custom loop

    My archive list meanwhile gets included on the same page (same template) in a sidebar:

    // build archive list via MySQL query
    	<ul>
    	<?php
    	$years = $wpdb->get_col("SELECT DISTINCT YEAR(post_date) FROM $wpdb->posts WHERE post_type = 'news' AND post_status = 'publish' ORDER BY post_date DESC");
    	foreach($years as $year) : ?>
    		<li><a href="<?php echo get_permalink() . '/' . $year; ?>"><?php echo $year; ?></a></li>
    	<?php endforeach; ?>
    	</ul>

    This works for me. It is such a specific scenario, however, that I am not sure this solution can be abstracted from my particular setup in a way to be of much use to any body else... but just in case, I thought I'd share. A learning experience!

  8. gvogelesang
    Member
    Posted 3 years ago #

    I have also trying to get a custom post type year archive work for me (with paging).

    My solution is as following.

    First I created a custom template page (page_id=129) where I queried only the custom type posts, like:

    $my_query = new WP_Query('year='.$myyear.'&post_type=project&paged='.$paged);

    Next I added 2 rewrite rules. Here's the code (inside functions.php):

    // Rewrite rules for Project Year Archive pages
    add_filter('rewrite_rules_array','wp_insertMyRewriteRules');
    add_filter('query_vars','wp_insertMyRewriteQueryVars');
    
    // Adding a new rule
    function wp_insertMyRewriteRules($rules)
    {
    	$newrules = array();
    	$newrules['projects/([^/]+)$'] = 'index.php?page_id=129&myyear=$matches[1]';
    	$newrules['projects/([^/]+)/page/?([0-9]{1,})/?$'] = 'index.php?page_id=129&myyear=$matches[1]&paged=$matches[2]';
    	return $newrules + $rules;
    }
    
    // Adding the id var so that WP recognizes it
    function wp_insertMyRewriteQueryVars($vars)
    {
        array_push($vars, 'myyear');
        return $vars;
    }

    So now I can call for example http://mydomain/projects/2010/ or http://mydomain/projects/2010/page/2/ to get projects from a specific year (and page).

  9. Stefan
    Member
    Posted 3 years ago #

    Thanks for the code luispunchy, exactly what I needed to get my custom post type year archives working!

Topic Closed

This topic has been closed to new replies.

About this Topic