• Resolved jbwebdev1982

    (@jbwebdev1982)


    We’re having some trouble figuring out WP URL rewrite rules when we have tokens separated by parameters. Very briefly, we’d like to have the following common format for user pages and video libraries:

    /users/steve/videos/2
    …where “users” is the PHP page template that displays details on a user. So if we go to:

    /users/steve/
    …we see Steve’s home page. If we go to:

    /users/steve/videos/
    …we hit the page template for the video library, and we see Steve’s list of videos. And if we go to:

    /users/steve/videos/2
    …we hit a third page template, the video player page, where we see video id 2 displayed.

    So we have three separate page templates, and we would like the child / parent relationship between them to be user page -> video library page -> video player page, for our breadcrumbs display.

    The trick we can’t figure out is that “steve” and “2” are really params inserted into the url, interrupting the parent/child token relationship. And second, for the video player, there really isn’t a token there at all that maps to the video player page template. It’s just the presence of the “videos” token plus the additional numerical parameter that tells us this maps to the video player page template.

    As alternative, we’ve figured out how to get /users/videos/steve/2 working. We’ve added this rewrite rule to our child theme’s functions.php file:

    add_rewrite_rule(
        '^users/videos/([^/]*)/([^/]*)/?',
        'index.php?pagename=users/videos&users_filter_name=$matches[1]&user_video_id=$matches[2]',
        'top');

    (we’ve also defined the variables “users_filter_name” and “user_video_id” and we are successfully parsing them in the template)

    But since what we really would like is /users/steve/videos/2 we tried updating the rule to:

    add_rewrite_rule(
        '^users/([^/]*)/videos/([^/]*)/?',
        'index.php?pagename=users/videos&users_filter_name=$matches[1]&user_video_id=$matches[2]',
        'top');

    That didn’t seem to work (it just loaded the ‘/users/’ page) so we’re obviously doing it incorrectly. Is what we want achievable with this structure?

    Thanks!

    • This topic was modified 3 years, 5 months ago by Jan Dembowski. Reason: Moved to Fixing WordPress, this is not an Developing with WordPress topic
    • This topic was modified 3 years, 5 months ago by Jan Dembowski. Reason: WHOOPS! This is a Developing with WordPress topic. Moving back. ;)
Viewing 7 replies - 1 through 7 (of 7 total)
  • Moderator bcworkz

    (@bcworkz)

    The index.php query vars are the same either way, so should work if the first worked. Did you remember to flush rewrite rules after changing your code? (can be done by visiting the permalinks settings page)

    If you are getting /users/ page, the page name rule is matching before yours, which shouldn’t be with the 'top' arg. Dump out the global $wp_rewrite array to verify your rule is above the page name rule.

    You might also dump out the query vars for the main WP_Query object to confirm your custom vars were correctly passed by the rule WP chose to use.

    Thread Starter jbwebdev1982

    (@jbwebdev1982)

    Thanks for the reply! Yes to flushing the rewrite rules, I made sure I did that.

    I think the nuance I may not have properly explained is that getting to the /users/ page isn’t the problem. I want to load a page named “videos” that is a child of /users/. But instead of accessing it by /users/videos/steve/ (which I’ve figured out how to do) I want to be able to do it like /users/steve/videos/. So I’m trying to insert the url parameter (steve) between the parent and child page in the url path. Does that make sense? Am I trying to do something that’s impossible? It’s a marketing/UX request because it will allow us to have a consistent hierarchy in our urls. We’ll have other pages besides “videos” and we’d like it to all have the same format.

    Maybe we’re going about it incorrectly? Should we just have one page in wordpress (users) and depending on what follows it, load a different template? For example, if we request /users/steve the page is “users” and “steve” is just a parameter. We determine the presence of that parameter (and no others) and then load a wordpress template. Let’s just call it template_A.php

    Then, if we have /users/steve/videos, the page is “users” and “steve” and “videos” are both parameters. We check for the presence of both those parameters and load a different wordpress template. Let’s call it template_B.php

    Is there a way to do this? I think that would accomplish what we’re after.

    • This reply was modified 3 years, 5 months ago by jbwebdev1982.
    Thread Starter jbwebdev1982

    (@jbwebdev1982)

    As an update, I’ve tried again with what I’d previously attempted

    add_rewrite_rule(
        '^users/([^/]*)/videos/([^/]*)/?',
        'index.php?pagename=users/videos&users_filter_name=$matches[1]&user_video_id=$matches[2]',
        'top');

    for whatever reason, it is working. BUT it still doesn’t get me to the finish line because now that I can get
    /users/steve/videos/2
    working, that doesn’t help me with
    /users/steve/videos/

    since that will map to the same template that’s showing the individual videos. I’d like to be able to tell wordpress to use a different template for both of those urls so I can keep the code a little more tidy and separate. Is this where the template_include hook will help me?

    Moderator bcworkz

    (@bcworkz)

    Yes, alternatives are possible, but the rewrite rule approach, including the '^users/([^/]*)/videos/([^/]*)/?' form, should work for you. I think you may not have whitelisted the variables correctly. I tested your rule on my site, substituting users/video pagename for one I already had. Without whitelisting “users_filter_name” and “user_video_id” I had trouble, I got a 404 page. But as soon as I whitelisted them it worked fine.

    // Whitelist query vars
    add_filter( "query_vars", "bc2_query_vars" );
    function bc2_query_vars( $vars ) {
        $vars[] = 'users_filter_name';
        $vars[] = 'user_video_id';
        return $vars;
    }

    But you say with the desired rewrite rule you got the parent /users/ page? Something else is going on with your site, based on my testing you should have gotten a 404 page. There could be some other rewrite rule taking precedence. Vardump the global $wp_rewrite and examine the “rules” element. Your added rule should be one of the first listed without anything conflicting before.

    Alternately, you could have a page “users” where all that follows are query vars. While you could load different templates accordingly via a filter, what I’d do is build a custom page template for “users” and have it conditionally load in template parts based on the passed query vars.

    Thread Starter jbwebdev1982

    (@jbwebdev1982)

    Thanks again for the help @bcworkz I’ve got this working. I’ve also leveraged the template_include hooks. Checking for the presence of the various parameters, I can tell WordPress which of the templates in my child theme I want to use. Here was my test code that made that happen (added to functions.php, after adding rewrite rules and whitelisting variables named ‘user_video_id’, ‘users_filter_name’ and ‘user_page_section_name’)

    add_action('template_include', 'users_template_switch');
    
    /**
    * Dynamically switch templates depending on parameters and authentication
    */
    function users_template_switch($template) {
        global $post;
    	global $wp_query;
        if($post->post_name == 'template-test'):
            $videoIdIsSet = isset($wp_query->query_vars['user_video_id']);
    		$userIsSet = isset($wp_query->query_vars['users_filter_name']);
    		$userPageNameIsSet = isset($wp_query->query_vars['user_page_section_name']);
    		$allParams = array('videoIdIsSet' => $videoIdIsSet, 'userIsSet' => $userIsSet, 'userPageNameIsSet' => $userPageNameIsSet);
    		if($videoIdIsSet && $userIsSet && $userPageNameIsSet){
    			// Search for the new template file either within the parent
    			// or child themes
    			$new_template = locate_template(array('ccmm-template-test3.php'));
    			if ( '' != $new_template ) {
    			    return $new_template;
    			} // if the template doesn't exist, WordPress will load
    			// the default template instead
    		}
    		if($userIsSet && $userPageNameIsSet){
    			// Search for the new template file either within the parent
    			// or child themes
    			$new_template = locate_template(array('ccmm-template-test2.php'));
    			if ( '' != $new_template ) {
    			    return $new_template;
    			} // if the template doesn't exist, WordPress will load
    			// the default template instead
    		}
    		if($userIsSet){
    			// Search for the new template file either within the parent
    			// or child themes
    			$new_template = locate_template(array('ccmm-template-test1.php'));
    			if ( '' != $new_template ) {
    			    return $new_template;
    			} // if the template doesn't exist, WordPress will load
    			// the default template instead
    		}
        endif;
        return $template;
    }

    I found the example here: https://solarisedesign.com/blog/smarter-wordpress-templates-with-template_include-and-template_redirect

    I’ll need to determine how I’d like to handle the hierarchy of parent child pages, but at least now I know I have quite a bit of options.

    I like this approach more than putting this code in the template file itself (and loading page parts dynamically). That definitely would have worked but this keeps things more separate and clean.

    • This reply was modified 3 years, 4 months ago by jbwebdev1982.
    Thread Starter jbwebdev1982

    (@jbwebdev1982)

    One more update, turns out the dynamic template switch is overkill. The add_rewrite_rule hook works just fine where I can specify one page to list all the videos, and another to list the individual video.

    We made a page in WP named “videos” (plural) and made it a child of our “users” page. We made another page called “video” (singular) and made it a child of our “users” page as well. Each of these pages were assigned a different custom template that lives in our child theme folder.

    The rules went like this:

    add_rewrite_rule(
        '^users/([^/]*)/videos/([^/]*)/?',
        'index.php?pagename=users/videos&users_filter_name=$matches[1]&user_video_id=$matches[2]',
        'top');
    
    add_rewrite_rule(
        '^users/([^/]*)/videos/?',
        'index.php?pagename=users/video&users_filter_name=$matches[1]',
        'top');

    The important parts seemed to be
    1) The order we listed them in. If I would have inverted the order, it didn’t work for both scenarios. Entering a url like “/users/steve/videos/1234/” would load the page that shows ALL the videos, instead of the individual video page. I assume that’s because if WP finds a match, it short circuits. So the rules need to be listed in “descending complexity” order in cases like these.
    2) Note the slight difference in the “pagename” query param in both rules. That was the secret to loading the specific page (singular vs plural). I was initially misunderstanding how this works and thinking that parameter needed to always contain the same “pages” that were listed in the regex argument.

    So now we have everything we need. Yay!

    • This reply was modified 3 years, 4 months ago by jbwebdev1982.
    Moderator bcworkz

    (@bcworkz)

    Nice!

    the rules need to be listed in “descending complexity” order

    Exactly. There is no best match algorithm. The redirect happens immediately on the first match found.

Viewing 7 replies - 1 through 7 (of 7 total)
  • The topic ‘Url Rewrite, Multiple Parameters/Templates – Dynamic Path/Parameter Placement’ is closed to new replies.