Get custom post type slug for an archive page

How do I discover the custom post type slug when I’m on an archive page?

For instance if /products/ fires the archive-products.php template, how (pragmatically) do I get the post type slug?

Read More

Thanks

Related posts

Leave a Reply

7 comments

  1. To get the current post type use get_post_type(). Then ask get_post_type_object() for all the data you need, for example the slug:

    $post_type = get_post_type();
    if ( $post_type )
    {
        $post_type_data = get_post_type_object( $post_type );
        $post_type_slug = $post_type_data->rewrite['slug'];
        echo $post_type_slug;
    }
    
  2. I’m using this outside of the loop on the archive.php template to get which custom post archive I’m on.

    It’s a combo of the methods that both @toscho and @Rarst recommended:

    $post_type = get_queried_object();
    echo $post_type->rewrite['slug'];
    

    Update: @majick pointed out that this only works if you’ve set the rewrite slug for your CPT. Rewrite slug is optional when registering a CPT and defaults to post_type if not set.

  3. The answers get confusing. And maybe Im as well but the headline question is:

    Get custom post type slug for an archive page

    If you mean post type archive landing-page, and when is_post_type_archive() returns true, you want the slug that responing to current viewing archive:

    /* returns /products/ */
    
    $responding_name = str_replace(get_home_url(), '', get_post_type_archive_link(get_query_var('post_type')));
    
    /* continue to get 'products' without slug slashes */
    $responding_name = str_replace('/', '', $responding_name);
    

    — END OF ANSWERING THE QUESTION —

    Explanation:

    You cant rely on the registered slug. WordPress is not either. For example, when calling get_post_type_archive_link() WordPress is checking the current rewrite rules for your install .

    Wherever you are, inside or outside loop, current archive or single post, reverse the get_post_type_archive_link() mechanism. (Permalinks enabled.)

    Considerations:

    As mentioned here, the post type(s) in current query can be an array. You can go futher with your intensions with filter out the post type you look for, example:

    $post_type = get_query_var('post_type'); 
    if(is_array($post_type)) $post_type = reset($post_type);
    

    or

    if(isset($post_types[0])) $post_type = $post_types[0];
    

    Another point of view:

    Woocommerce example, is registered with ‘products’ post type object but in reality uses rewritten rule name (shop):

    /* returns shop */
    $responding_name = str_replace('/', '', str_replace(get_home_url(), '', get_post_type_archive_link('product')));
    

    Mark, Im using $responding_name, because the objectives might vary.
    A post archive does not exists, its just a url.

  4. t should be noted that if has_archive is set to true while registering the Custom Post Type, the post type archive /cptslug/ will be internally rewritten to ?post_type=cptslug. So this would also mean is_post_type_archive() will return true.

    Unfortunately, where the registered rewrite slug is different to the post type, you are not actually reliably getting the post_type. eg. if your post type was myplugin_cars and your rewrite slug was cars and you need to be getting myplugin_cars then even this (to prevent errors if the current queried object is not a custom post type) will still fail:

    $queryobject = get_queried_object();
    if (has_property('rewrite',$queryobject)) {
        if (isset($queryobject->rewrite['slug'])) {
             $posttype = $queryobject->rewrite['slug'];
         }
     }
    

    But because is_post_type_archive is true this is more reliable:

    if (is_post_type_archive()) {
        $posttype = get_query_var('post_type');
        // which is basically the same as:
        // global $wp_query;
        // $posttype = $wp_query->query_vars['post_type'];
    } 
    else ($posttype = 'post';}
    

    But hang on, there’s more… turns out with a little testing it really isn’t that simple either… what if you are on a taxonomy archive page with multiple post types in the taxonomy..? Or assign post tags to a custom post type other than post? Or are on an author archive page? Date archive page? …or even have a complex tax_query or meta_query for WP_Query?

    The only reliable answer (without testing for every possible archive case) is to loop the actual posts in the query… Here is the full function I came up with to work on both singular and archive pages, and allowing you to optionally pass a custom query object (or post object/post ID for singular posts):

    function get_current_post_types($object=null) {
    
        // if a numeric value passed, assume it is a post ID
        if ( ($object) && (is_numeric($object)) ) {$object = get_post($object);}
        // if an object is passed, assume to be a post object
        if ( ($object) && (is_object($object)) ) {return get_post_type($object);}
    
        // standard single post type checks
        if (is_404()) {return '';}
        // update: removed this check, handled by is_singular
        // if (is_single()) {return 'post';}
        if (is_page()) {return 'page';}
        if (is_attachment()) {return 'attachment';}
        if (is_singular()) {return get_post_type();}
    
        // if a custom query object was not passed, use $wp_query global
        if ( (!$object) || (!is_object($object)) ) {
            global $wp_query; $object = $wp_query;
        }
        if (!is_object($object)) {return '';} // should not fail
    
        // if the post_type query var has been explicitly set
        // (or implicitly set on the cpt via a has_archive redirect)
        // ie. this is true for is_post_type_archive at least
        // $vqueriedposttype = get_query_var('post_type'); // $wp_query only
        if (property_exists($object,'query_vars')) {
            $posttype = $object->query_vars['post_type'];
            if ($posttype) {return $posttype;}
        }
    
        // handle all other cases by looping posts in query object
        $posttypes = array();
        if (method_exists($object,'found_posts')) {
            if ($object->found_posts > 0) {
                $queriedposts = $object->posts;
                foreach ($queriedposts as $queriedpost) {
                    $posttype = $queriedpost->post_type;
                    if (!in_array($posttype,$posttypes)) {$posttypes[] = $posttype;}
                }
                if (count($posttypes == 1)) {return $posttypes[0];}
                else {return $posttypes;}
             }
         }
         return ''; // nothin to see here
    }
    

    This will reliably (did I say that?) return an array of post types if more than one is present, or a string with the single post type if there is only one type.
    All you need to do is:

    $posttypes = get_current_post_types();
    // or pass a post ID 
    $posttypes = get_current_post_types($postid);
    // or pass a post object
    $posttypes = get_current_post_types($post);
    // or pass a custom query - that has been run
    $posttypes = get_current_post_types($query);
    

    Example Usage (just for fun):

    add_filter('the_posts','myplugin_fading_thumbnails',10,2);
    function myplugin_fading_thumbnails($posts,$query) {
        if (!is_archive()) {return $posts;}
        $cptslug = 'myplugin_slug'; $dosomethingcool = false;
        $posttypes = get_current_post_types($query);
        if ( (is_array($posttypes)) && (in_array($cptslug,$posttypes)) ) {$dosomethingcool = true;}
        elseif ($cptslug == $posttypes) {$dosomethingcool = true;}
    
        if ($dosomethingcool) {
            global $fadingthumbnails; $fadingthumbnails = $cptslug;
            if (!has_action('wp_footer','myplugin_cpt_script')) {
                add_action('wp_footer','myplugin_cpt_script');
            }
        }
    
        function myplugin_cpt_script() {
            global $fadingthumbnails;
            echo "<script>var thumbnailclass = 'img.thumbtype-".$fadingthumbnails."';
            function fadeoutthumbnails() {jQuery(thumbnailclass).fadeOut(3000,fadeinthumbnails);}
            function fadeinthumbnails() {jQuery(thumbnailclass).fadeIn(3000,fadeoutthumbnails);}
            jQuery(document).ready(function() {fadeoutthumbnails();});
            </script>";
        }
    
        return $posts;
     }
    

    To see the effect, change the custom post type in the code to post, and add a thumbtype-post class attribute to your post thumbnail images…

  5. You can use this code:

    $queried_object = get_queried_object();
    $posttype_slug = $queried_object->query_var;
    echo $posttype_slug;
    

    use $posttype_slug var whatever you need