admin管理员组

文章数量:1290981

[Edited]

I changed the purpose of my site into an language learning site.

Now the CPT is setup as following:

{ Language } - { Subject }

  • English speaking (slug: english-speaking )
  • English writing (english-writing )
  • English reading ( english-speaking )
  • English writing (english-writing)

Single CPT setup

Because each CPT might have different taxonomy… so it is preferred this would be taking into consideration of the code.

Say: CPT “English Speaking” has a custom taxonomy of “Speaking Task” (speaking-task) with terms “task-1”, “task-2”, “task-3” … . But: CPT “English Writing” has no taxonomy at all.

Desired URL for single CPT post item:

With custom taxonomy terms:

  • www.domain/{language}/{subject}/{ term-slug }/{custom-question-id}/{post-name}
  • ex. www.domain/english/speaking/speaking-1/32/blah-blah-blah

Without custom taxonomy terms:

  • www.domain/{language}/{subject}/{custom-question-id}/{post-name}
  • ex. www.domain/english/writing/32/blah-blah-blah

The URL rewrite part for Single CPT is not a problem for me now thanks to your teaching and genius code provided in the new answer

What I am struggling with is the rewrite for the CPT Archive page below:


CPT Archive setup

I am trying to create a custom CPT Archive template with the following setup for URL:

  • www.domain/ { language } / { subject } ex. www.domain/english/speaking

  • www.domain/ { language } / { subject } / { terms-slug }

  • ex1. www.domain/english/speaking (instead of listing all posts, I wish to make it default to list out to “speaking-1”)
  • ex2. www.domain/english/speaking/speaking-1 (only list out post under “speaking-1” )
  • ex3. www.domain/english/speaking/speaking-2 (only list out post under “speaking-2” )

———————————————————————————————————

In [archive-english-speaking.php] , I have the following code:

// use add_rewrite_rule in funcitons; query_vars "task" is terms of speaking-task taxonomy
global $wp_query;
$task_no = $wp_query->query_vars['task'];


if (preg_match("/[1-6]/", $task_no)) {
  $task = ( 'speaking-' . $task_no );
} else {
  $task_no = ( '1' );
  $task = ( 'speaking-' . $task_no );
}

$posts_per_page = 10;


$the_query = new WP_Query( array( 
  'post_type' => 'english-speaking',
  'taxonomy' => 'speaking-task',
  'term' => $task,
  /*'paged' => $paged,
  'posts_per_page' => $posts_per_page,*/
  'orderby'   => 'meta_value_num',
  'meta_key' => 'wpcf-question-id',
  'order'   => 'ASC', /* ASC/DESC */
   ) );

———————————————————————————————————

In [functions.php] , I have the following code:

function custom_rewrite_tag() {
    //taxonomy speaking-task =?
    add_rewrite_tag('%task%', '([^/]*)');
}
add_action('init', 'custom_rewrite_tag', 10, 0);



// Custom Question ID from custom field
function cqid() {
    $cqid = get_post_meta( get_the_ID(), 'wpcf-question-id', true );
    return $cqid;
}


function my_questions_cpt_list() {
    return [
    // Format: POST_TYPE   => QUESTION_SUBJECT_SLUG
        'english-listening' => 'listening',
        'english-speaking' => 'speaking',
        'english-reading' => 'reading',
        'english-writing' => 'writing',
        // Add more items as needed, or if and when you register another CPT
        // for questions.
    ];
}


add_action( 'init', 'my_questions_cpt_add_rewrite_rules' );
function my_questions_cpt_add_rewrite_rules() {


// Archive Page 
add_rewrite_rule( '^english/speaking/?$','index.php?post_type=english-speaking&taxonomy=speaking-task&task=$matches[1]', 'top' );
add_rewrite_rule( '^english/speaking/([^/]*)/?$','index.php?post_type=english-speaking&taxonomy=speaking-task&task=$matches[1]', 'top' );


//
$cpt_list = my_questions_cpt_list();
foreach ( $cpt_list as $post_type => $q_subject ) {
    if ( empty( $post_type ) ) {
        continue;
    }

    if ( ! $s = preg_quote( $q_subject ) ) {
        continue;
    }


    add_rewrite_rule('^english/(' . $s . ')/(\d+)/(\d+)/([^/]+)/?','index.php?' . $post_type . '=$matches[4]','top');
}
}



add_filter( 'post_type_link', 'my_questions_cpt_custom_permalink', 10, 2 );
function my_questions_cpt_custom_permalink( $permalink, $post ) {
    $taxonomy = 'speaking-task';
    $cpt_list = my_questions_cpt_list();

if ( false === strpos( $permalink, '?' ) &&
$post && isset( $cpt_list[ $post->post_type ] ) ) {
    $cats = get_the_terms( $post, $taxonomy );
    if ( $cats && ! is_wp_error( $cats ) ) {
        // If assigned to multiple tasks (or categories), then we use just
        // the first task/term.

        $cat_id = $cats[0]->slug; //term_id
        $task = $cat_id[strlen($cat_id)-1];
        $cqid = cqid();

        $s = $cpt_list[ $post->post_type ];
        $permalink = home_url( 'english/' . $s . '/' . $task . '/' . $cqid . '/' . $post->post_name . '/' );
        $permalink = user_trailingslashit( $permalink, 'single' );
    }
}

return $permalink;
}

———————————————————————————————————

@Sally The code above is limited to my programming level, I used your first answer and modified it... it is kind working in localhost but the archive url got 404 or blank page when I moved it to online hosting server...

[Edited]

I changed the purpose of my site into an language learning site.

Now the CPT is setup as following:

{ Language } - { Subject }

  • English speaking (slug: english-speaking )
  • English writing (english-writing )
  • English reading ( english-speaking )
  • English writing (english-writing)

Single CPT setup

Because each CPT might have different taxonomy… so it is preferred this would be taking into consideration of the code.

Say: CPT “English Speaking” has a custom taxonomy of “Speaking Task” (speaking-task) with terms “task-1”, “task-2”, “task-3” … . But: CPT “English Writing” has no taxonomy at all.

Desired URL for single CPT post item:

With custom taxonomy terms:

  • www.domain/{language}/{subject}/{ term-slug }/{custom-question-id}/{post-name}
  • ex. www.domain/english/speaking/speaking-1/32/blah-blah-blah

Without custom taxonomy terms:

  • www.domain/{language}/{subject}/{custom-question-id}/{post-name}
  • ex. www.domain/english/writing/32/blah-blah-blah

The URL rewrite part for Single CPT is not a problem for me now thanks to your teaching and genius code provided in the new answer

What I am struggling with is the rewrite for the CPT Archive page below:


CPT Archive setup

I am trying to create a custom CPT Archive template with the following setup for URL:

  • www.domain/ { language } / { subject } ex. www.domain/english/speaking

  • www.domain/ { language } / { subject } / { terms-slug }

  • ex1. www.domain/english/speaking (instead of listing all posts, I wish to make it default to list out to “speaking-1”)
  • ex2. www.domain/english/speaking/speaking-1 (only list out post under “speaking-1” )
  • ex3. www.domain/english/speaking/speaking-2 (only list out post under “speaking-2” )

———————————————————————————————————

In [archive-english-speaking.php] , I have the following code:

// use add_rewrite_rule in funcitons; query_vars "task" is terms of speaking-task taxonomy
global $wp_query;
$task_no = $wp_query->query_vars['task'];


if (preg_match("/[1-6]/", $task_no)) {
  $task = ( 'speaking-' . $task_no );
} else {
  $task_no = ( '1' );
  $task = ( 'speaking-' . $task_no );
}

$posts_per_page = 10;


$the_query = new WP_Query( array( 
  'post_type' => 'english-speaking',
  'taxonomy' => 'speaking-task',
  'term' => $task,
  /*'paged' => $paged,
  'posts_per_page' => $posts_per_page,*/
  'orderby'   => 'meta_value_num',
  'meta_key' => 'wpcf-question-id',
  'order'   => 'ASC', /* ASC/DESC */
   ) );

———————————————————————————————————

In [functions.php] , I have the following code:

function custom_rewrite_tag() {
    //taxonomy speaking-task =?
    add_rewrite_tag('%task%', '([^/]*)');
}
add_action('init', 'custom_rewrite_tag', 10, 0);



// Custom Question ID from custom field
function cqid() {
    $cqid = get_post_meta( get_the_ID(), 'wpcf-question-id', true );
    return $cqid;
}


function my_questions_cpt_list() {
    return [
    // Format: POST_TYPE   => QUESTION_SUBJECT_SLUG
        'english-listening' => 'listening',
        'english-speaking' => 'speaking',
        'english-reading' => 'reading',
        'english-writing' => 'writing',
        // Add more items as needed, or if and when you register another CPT
        // for questions.
    ];
}


add_action( 'init', 'my_questions_cpt_add_rewrite_rules' );
function my_questions_cpt_add_rewrite_rules() {


// Archive Page 
add_rewrite_rule( '^english/speaking/?$','index.php?post_type=english-speaking&taxonomy=speaking-task&task=$matches[1]', 'top' );
add_rewrite_rule( '^english/speaking/([^/]*)/?$','index.php?post_type=english-speaking&taxonomy=speaking-task&task=$matches[1]', 'top' );


//
$cpt_list = my_questions_cpt_list();
foreach ( $cpt_list as $post_type => $q_subject ) {
    if ( empty( $post_type ) ) {
        continue;
    }

    if ( ! $s = preg_quote( $q_subject ) ) {
        continue;
    }


    add_rewrite_rule('^english/(' . $s . ')/(\d+)/(\d+)/([^/]+)/?','index.php?' . $post_type . '=$matches[4]','top');
}
}



add_filter( 'post_type_link', 'my_questions_cpt_custom_permalink', 10, 2 );
function my_questions_cpt_custom_permalink( $permalink, $post ) {
    $taxonomy = 'speaking-task';
    $cpt_list = my_questions_cpt_list();

if ( false === strpos( $permalink, '?' ) &&
$post && isset( $cpt_list[ $post->post_type ] ) ) {
    $cats = get_the_terms( $post, $taxonomy );
    if ( $cats && ! is_wp_error( $cats ) ) {
        // If assigned to multiple tasks (or categories), then we use just
        // the first task/term.

        $cat_id = $cats[0]->slug; //term_id
        $task = $cat_id[strlen($cat_id)-1];
        $cqid = cqid();

        $s = $cpt_list[ $post->post_type ];
        $permalink = home_url( 'english/' . $s . '/' . $task . '/' . $cqid . '/' . $post->post_name . '/' );
        $permalink = user_trailingslashit( $permalink, 'single' );
    }
}

return $permalink;
}

———————————————————————————————————

@Sally The code above is limited to my programming level, I used your first answer and modified it... it is kind working in localhost but the archive url got 404 or blank page when I moved it to online hosting server...

Share Improve this question edited Aug 21, 2018 at 5:30 Atimmy asked Apr 19, 2018 at 8:56 AtimmyAtimmy 557 bronze badges 3
  • after some research, seems like I need to use "is_front", but I could not find a good example ... – Atimmy Commented Apr 20, 2018 at 4:14
  • Use this answer as a starting point. – Max Yudin Commented Apr 20, 2018 at 7:02
  • Could be done using a placeholder in slug argument in rewrite parameter while registering the post type. Then a str_replace in post_type_link filter or add_rewrite_rule should do it. – Abhik Commented Apr 21, 2018 at 5:11
Add a comment  | 

2 Answers 2

Reset to default 2

(Updated/new answer)

So here I'm going straight to the code you need: (but do read the updated version of the original answer)

Register the CPT's and Set the proper rewrite slug

  1. This is for the "English Speaking" CPT, which is assigned to the custom taxonomy "Speaking Task":

    register_post_type( 'english-speaking', array(
        'label' => 'English Speaking',
        'public' => true,
        'rewrite' => array(
            'slug' => 'english/speaking/%speaking_task%/%question_id%',
        ),
        // Other args here.
    ) );
    
  2. This is for the "English Writing" CPT, which is not assigned to any taxonomy:

    register_post_type( 'english-writing', array(
        'label' => 'English Writing',
        'public' => true,
        'rewrite' => array(
            'slug' => 'english/writing/%question_id%',
        ),
        // Other args here.
    ) );
    

Register the custom taxonomy "Speaking Task"

register_taxonomy( 'speaking-task', array( 'english-speaking' ), array(
    'label' => 'Speaking Tasks',
    'public' => true,
    'rewrite' => array(
        'slug' => 'english/speaking',
    ),
    // Other args here.
) );

Make sure the rewrite slug is set to english/speaking.

The "English Speaking" archive would be accessible at:

  • http://example/english/speaking

  • http://example/english/speaking/{SPEAKING TASK SLUG}

Register the custom rewrite tags

add_rewrite_tag( '%question_id%', '(\d+)' );
add_rewrite_tag( '%speaking_task%', '([^/]+)' );

Add the code for replacing the custom rewrite tags

add_filter( 'post_type_link', 'my_filter_questions_post_type_link', 10, 2 );
function my_filter_questions_post_type_link( $post_link, $post ) {
    // Replaces/rewrites %question_id% in the permalink.
    if ( false !== strpos( $post_link, '%question_id%' ) ) {
        $id = get_post_meta( $post->ID, 'wpcf-question-id', true );

        // A default value is necessary, and the value has to be a 0.
        $id = $id ? $id : '0';

        $post_link = str_replace( '%question_id%', $id, $post_link );
    }

    // Replaces/rewrites %speaking_task% in the permalink.
    if ( false !== strpos( $post_link, '%speaking_task%' ) ) {
        // A default value is necessary, but the term/category doesn't need to
        // actually exists. So you could, for example, use 'all' as the value.
        $slug = 'uncategorized';

        $cats = get_the_terms( $post, 'speaking-task' );
        if ( $cats && ! is_wp_error( $cats ) ) {
            $slug = $cats[0]->slug;
        }

        $post_link = str_replace( '%speaking_task%', $slug, $post_link );
    }

    return $post_link;
}

Flush the rewrite rules

Refer to my other answer if you don't know how to flush the rules..

Use get_query_var() to retrieve the rewrite tag values

  • get_query_var( 'question_id' ) for %question_id%

  • get_query_var( 'task_slug' ) for %task_slug%


The full sample code is available here. :)


UPDATE

If you use a plugin to register the post type, you can filter (set/change) the rewrite slug via the register_post_type_args filter, like so:

add_filter( 'register_post_type_args', 'my_filter_post_type_args', 10, 2 );
function my_filter_post_type_args( $args, $post_type ) {
    if ( ! isset( $args['rewrite'] ) ) {
        $args['rewrite'] = array();
    }

    if ( 'english-speaking' === $post_type ) {
        $args['rewrite']['slug'] = 'english/speaking/%speaking_task%/%question_id%';
    } elseif ( 'english-speaking' === $post_type ) {
        $args['rewrite']['slug'] = 'english/writing/%question_id%';
    }

    return $args;
}

And likewise if you use a plugin to register the custom taxonomy, you can filter (set/change) the rewrite slug via the register_taxonomy_args filter, like so:

add_filter( 'register_taxonomy_args', 'my_filter_taxonomy_args', 10, 2 );
function my_filter_taxonomy_args( $args, $taxonomy ) {
    if ( ! isset( $args['rewrite'] ) ) {
        $args['rewrite'] = array();
    }

    if ( 'speaking-task' === $taxonomy ) {
        $args['rewrite']['slug'] = 'english/speaking';
    }

    return $args;
}

You might need to set a lower priority — i.e. change the 10 to 11 or a higher value (number) — greater number = lower priority, whereas lower number = higher priority.

Alternatively, without custom coding, and if the plugin allows you to manually set the rewrite slug of the custom post type or taxonomy, then just use the appropriate box/field to enter the appropriate rewrite slug. =)

(Updated/new answer)

For the original question

I changed my original answer because there's actually a way simpler solution (without having to use the add_rewrite_rule() function, or hooking to the parse_request filter) in achieving this permalink structure (for your "questions" CPT):

  • /{SLUG}/question/{TASK ID}/{POST SLUG}

For example, you could have these permalinks:

  • /science/question/2/science-question-1
  • /math/question/4/math-question-1
  • /english/question/4/english-question-1

So how to achieve that?

When you register a "question" CPT via the register_post_type() function, set the rewrite slug to {SLUG}/question/%task_id% where {SLUG} is any slug such as science, math, and english.

register_post_type( 'science-question', array(
    'label' => 'Science Questions',
    'public' => true,
    'rewrite' => array(
        // The rewrite slug. You can change 'science' to whatever you like.
        'slug' => 'science/question/%task_id%',
    ),
    // Other args here.
) );

register_post_type( 'math-question', array(
    'label' => 'Math Questions',
    'public' => true,
    'rewrite' => array(
        // The rewrite slug. You can change 'math' to whatever you like.
        'slug' => 'math/question/%task_id%',
    ),
    // Other args here.
) );

Then for the custom taxonomy "Task" (registered via the register_taxonomy() function), set the taxonomy slug to task like this example:

// Set the slug to 'task'.
register_taxonomy( 'task', array( '{POST TYPE}', '{POST TYPE}', ... ), array(
    'label' => 'Tasks',
    'public' => true,
    // Other args here.
) );

(The {POST TYPE} could be science-question, math-question, etc.)

Next, add this code which will register the custom rewrite tag %task_id%:

add_rewrite_tag( '%task_id%', '(\d+)' );

And add this code which will replace the %task_id% tag in the post permalink:

// Replaces %task_id% in the permalink (i.e. $post_link).
add_filter( 'post_type_link', 'my_filter_questions_post_type_link', 10, 2 );
function my_filter_questions_post_type_link( $post_link, $post ) {
    if ( false !== strpos( $post_link, '%task_id%' ) ) {
        $cats = get_the_terms( $post, 'task' );
        $cat_id = 0;

        // If the post was assigned to at least one "task" category.
        if ( $cats && ! is_wp_error( $cats ) ) {
            $cat_id = $cats[0]->term_id;
        }
        // Else, /{SLUG}/question/0/{POST SLUG} should work well. =) But of
        // course, all "question" CPT posts should be assigned to *at least*
        // one "task".

        $post_link = str_replace( '%task_id%', $cat_id, $post_link );
    }

    return $post_link;
}

The full sample code is available here. :)

Don't forget to flush the rewrite rules!

It is necessary and here's how: Just go to the "Permalink Settings" page. That's all.

But if the rewrite rules were not flushed (you can confirm by visiting the "question" page), then on the "Permalink Settings" page, click on the "Save Changes" button without having to make any changes.

How to get the value of task_id

Use get_query_var( 'task_id' ). Example:

$task_id = get_query_var( 'task_id' );
$term = get_term( $task_id );

PS: I know the question has changed, but I updated this answer because it could help someone else. ;)

本文标签: custom post typesPermalink for CPT with taxonomy