wordpress - Add custom param after postname

Viewed 26

I'm using Wordpress permalink with this structure

domain/%postname%/

I want to add a custom param after postname, something like this

domain/%postname%/%custom_var%/

This I want it to show post content and I can access custom_var in code. It means the system will work the same for

domain/postname

and

domain/postname/custom_var
1 Answers

This is somewhat possible and not possible,

First you cannot get a URL structure like domain/postname/custom_var and play with its rewrite rule, the rewrite rule for this is use by wordpress default URL structure and messing with it could cause a lot of problems .e.g

domain/category/cat-1 - for category
domain/taxonomy/tax-1 - for taxonomy
domain/archive/2022 - for archive
and so on.....

If you dump the WP_Rewrite Object, you'll see under the rules property this has already been handled [[^/]+/([^/]+)/?$] => index.php?attachment=$matches[1] and the url with structure domain/sub-1/sub-2/ will do index.php?attachment=$matches[1] request

On the other hand, you could add a rewrite tag e.g.

add_rewrite_tag('%custom_var%', '([^/]+)', 'custom_var=');

then use that tag on your post permalink /%postname%/%custom_var%/

but this set-up would break your first goal and this URL structure domain/postname would no longer work.

However, there's another option you can do, and that is to add a pre-fix on your 2nd level path and add rewrite rule matching that url structure prefix,

Here's a whole example

create a rewrite rule that matches domain/***/s-***/ and set a custom query

add_action('init', function() {
  add_rewrite_rule('([^/]+)?/s-([^/]+)/?$','index.php?pname=$matches[1]&custom_var=$matches[2]','top');
});

(you need flush your rewrite rule after adding that code above, simply go to permalinks, switch it plain, then swtich it back to postname) or just run this function flush_rewrite_rules() you can then dump $wp_rewrite global variable and verify your rule is their

and set those query on wordpress query vars so you can simply access them

add_filter( 'query_vars',  function( $vars ) {
  $vars[] = 'pname';
  $vars[] = 'custom_var';
  return $vars;
});

Now the exciting part; modify the global $post and $wp_query variable when query var pname exist.

Something like this would do

add_action( 'wp', function() {

  global $wp, $wp_query, $post; //define global variable
  //include $wp variable so you can check the url request

  // check if pname var is set
  if (  isset( $wp_query->query['pname'] ) ) {

      // build query argument
      $args = [
          'page' => '', //assuming its a page
          'pagename' => $wp_query->query['pname']
      ];

      // run the query and assign it to $wp_query global variable
      $wp_query = new WP_Query( $args ); 

      // modify is_single wp_query param and tell it its not a post
      $wp_query->is_single = '';


      // modify is_page wp_query param and tell it its a page
      $wp_query->is_page = 1;

      //assign  (1st) found post to global post variable
      $post = $wp_query->posts[0];
      //done
  }

  // Test Dump
  //echo '<pre>', print_r($wp_query, 1), '</pre>';
  //echo '<pre>', print_r($wp_rewrite, 1), '</pre>';
  //echo '<pre>', print_r($wp, 1), '</pre>';
});

So if you try to access.

domain/page-1/s-test

it should display the page as long as page-1 is a post object

Related