Yes, it’s not hard to do that.
-
Just register your post type and enable rewriting for that post type, i.e. set
rewrite
totrue
. -
Once registered, modify its permalink structure like so:
global $wp_rewrite; $wp_rewrite->extra_permastructs['<post type>']['struct'] = '<your structure here>';
-
Use the
post_type_link
filter to replace rewrite tags like%post_id%
in the permalink URL.If you need to use custom rewrite tags like
%post_date%
in the permalink structure, you can add the custom tags usingadd_rewrite_tag()
, after you set the structure. -
Remember to flush the rewrite rules (i.e. re-save your permalinks) every time you changed the
rewrite
args, including when you simply setrewrite
totrue
orfalse
.You can programmatically flush the rules using
flush_rewrite_rules()
, however, it should only be used when necessary, e.g. upon plugin activation.As for the non-programmatic way, simply visit the Permalink Settings admin page without having to click the “save” button.
Working examples for you
-
This is for the structure
post-type-1-slug/post-name-year-month-day
where a sample URL might look likehttps://example.com/type1/hello-world-2023-02-20/
, and I’m using a custom rewrite tag%post_date%
for theyear-month-day
part. (It’s up to you if you’d rather use multiple tags, e.g.%year%
,%monthnum%
, and%day%
)add_action( 'init', 'my_register_type1_cpt' ); function my_register_type1_cpt() { register_post_type( 'type1', [ 'public' => true, 'label' => 'Type 1', 'rewrite' => true, // other args ] ); global $wp_rewrite; $wp_rewrite->extra_permastructs['type1']['struct'] = 'type1/%type1%-%post_date%'; // Add a structure tag for dates in either of these form: 2023-02-20 or 2023-2-20. add_rewrite_tag( '%post_date%', '(\d{4}-\d{1,2}-\d{1,2})' ); // If you use %year%, %monthnum%, %day% and/or %post_id%, then those are core // structure tags in WordPress, hence you do not need to add them manually. // However, you still need to manually replace them in the permalink URL! } add_filter( 'post_type_link', 'my_type1_post_type_link', 10, 2 ); function my_type1_post_type_link( $post_link, $post ) { if ( $post && 'type1' === $post->post_type ) { $post_date = wp_date( 'Y-m-d', strtotime( $post->post_date ) ); return str_replace( '%post_date%', $post_date, $post_link ); } return $post_link; }
-
This is for the structure
post-type-2-slug/post-name-id
where a sample URL might look likehttps://example.com/type2/hello-world-123/
with123
being the post ID.add_action( 'init', 'my_register_type2_cpt' ); function my_register_type2_cpt() { register_post_type( 'type2', [ 'public' => true, 'label' => 'Type 2', 'rewrite' => true, // other args ] ); global $wp_rewrite; $wp_rewrite->extra_permastructs['type2']['struct'] = 'type2/%type2%-%post_id%'; } add_filter( 'post_type_link', 'my_type2_post_type_link', 10, 2 ); function my_type2_post_type_link( $post_link, $post ) { if ( $post && 'type2' === $post->post_type ) { return str_replace( '%post_id%', $post->ID, $post_link ); } return $post_link; }
Note: %type1%
and %type2%
are the post name/slug and you don’t need to replace it manually, i.e. WordPress will do that for you.