I've been working on a CPT named "product" that has custom permalinks. Here is a part of this CPT :

'rewrite' => array(
            'slug' => 'catalog/%product-cat%',
            'with_front' => false

Then, I add a filter to properly rewrite the links the way I want :

add_filter('post_type_link', 'product_type_permalink', 10, 4);

where product_type_permalink behaves like this : it replaces %product-cat% by a custom post field defined in the CPT "product", then it returns the link. Here is its code :

function product_type_permalink($post_link, $id=0) {

     // current post
     global $post;

    // if it is not a product
    if(!is_object($post) || $post->post_type!= 'product') {

        return $post_link;


    else {

        // retrieves the product category (which is a custom taxonomy)
        if($terms = wp_get_object_terms($post->ID, 'category-product'))

        // must be done in order to avoid warnings when creating a new product
        if (!empty($terms[0])) {

            //retrieves the slog of "category-product"
            $term = $terms[0]->slug;

            // rewrites the permalink
            $permalink = str_replace('%categorie-produit%', $term, $post_link);

            return $permalink;




Hence, the permalinks finally look like this : www.mysite.com/catalog/lectures/a-custom-post or www.mysite.com/catalog/anythingelse/another-custom-post.

All the operations mentioned above work properly : they are defined in the functions.php file of a child theme.

Now I've created a custom archive page for another CPT named "session", and in this archive page I need to get the URL of some products. Every "session" has a custom field "product".

So the session-archive.php file has a WP Query that retrieves all the sessions, and somewhere in this file I wrote :

//session_product is the custom field in the session CPT that references a product
if (!empty(get_post_meta( $id, 'session_product', true ))) {

  //gets the product id
  $id_product = get_post_meta( $id, 'session_product', true );

  //gets the product title
  $title_product = get_the_title($id_product);

  //gets the permalink
  $permalink_product = get_post_permalink($post = $id_product, $leavename = false);


I can retrieve everything but the permalink, in spite of $leavename = false (which is, anaway, set to false by default) remains under the "%%" form :


Obviously, this comes from the fact that the custom archive file is different from the functions.php file where I defined the rewriting of the permalinks.

But what can I do to solve this ? Is there a way to take into account the post_type_link filter of the functions.php file in order to return the proper permalinks ?


EDIT I used a trick to solve this but my answer still remains : why the post_type_link filter located in the functions.php is not called/taken into account when visiting the custom archive page ?

Anyway here is the trick : in the archive page, I rewrote the permalink for the products

$permalink_product = str_replace('%category-product%', 'product', $permalink_product);
  • Show us the product_type_permalink() function.
    – Z. Zlatev
    Commented Apr 19, 2016 at 14:29
  • Thanks for your help. I added the code. Once again, the permalinks work fine, the problem comes when I try to get them from my archive PHP file, they are not rewritten as I expected
    – Fafanellu
    Commented Apr 19, 2016 at 14:37

1 Answer 1


The problem must be how you define your post variable. You assume it's the global $post but that's not always the case especially in non-singular pages like archives.

The filter itself provides the correct post instance that's it's been called on as 2nd parameter.

function product_type_permalink( $post_link, $post ) {

     // current post is already defined.

Also don't get confused by the fact your code is in functions.php. It gets loaded on every WP page load, incl. in admin.

  • Thanks a lot but my function works fine : it rewrites the URL for the product CPT. I don't expect to rewrite a post of CPT session, I expect to display a sessions archive page, where I need to grab the URL of many products. The problem is that the function above doesn't seem to be called from my archive page...hence the products don't have a proper URL. However I tested your code and the problem remains the same.
    – Fafanellu
    Commented Apr 19, 2016 at 14:48
  • You are right. I've updated my answer
    – Z. Zlatev
    Commented Apr 19, 2016 at 14:54
  • Yes it must be called on every page, that is the reason why I was wondering why it wasn't :)
    – Fafanellu
    Commented Apr 19, 2016 at 14:57
  • I tried to change the parameters as you stated but I still have the same problem. The only solution for now is to force again the rewriting in the archive page. Maybe my problem comes from some kind of priority in the filter ?
    – Fafanellu
    Commented Apr 19, 2016 at 15:00
  • @Fafanellu did you remove global $post?
    – Milo
    Commented Apr 19, 2016 at 15:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.