admin管理员组

文章数量:1291144

How can I add a custom rewrite rule so I can catch paths like these:

/products/imports/filters
/products/imports/elements
/products/imports/parts

And point them to a template file, for examples:

sector.php

My code:

function sector_rewrite_rule() {
    add_rewrite_rule(
        '(.?.+?)/([^/]*)/([^/]*)/?$',
        'index.php?pagename=$matches[1]/$matches[2]&sector=$matches[3]',
        'top'
    );
}
add_action('init', 'sector_rewrite_rule', 10, 0);

But I get 404 when I tried with the URLs above.

I have created a standard page called imports created under products already:

products/
   imports/

So I want to catch any word right after this page:

/products/imports/

How can I add a custom rewrite rule so I can catch paths like these:

/products/imports/filters
/products/imports/elements
/products/imports/parts

And point them to a template file, for examples:

sector.php

My code:

function sector_rewrite_rule() {
    add_rewrite_rule(
        '(.?.+?)/([^/]*)/([^/]*)/?$',
        'index.php?pagename=$matches[1]/$matches[2]&sector=$matches[3]',
        'top'
    );
}
add_action('init', 'sector_rewrite_rule', 10, 0);

But I get 404 when I tried with the URLs above.

I have created a standard page called imports created under products already:

products/
   imports/

So I want to catch any word right after this page:

/products/imports/
Share Improve this question edited Jun 10, 2021 at 22:45 Run asked Jun 10, 2021 at 22:19 RunRun 3315 silver badges20 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 3

You need to create query_var for sector and you need to create a rewrite rule with RegEX. I created a plugin for this. Create a file and add into the wp-content/plugins folder and activate it.

<?php

/*
Plugin Name: Custom Rewrite
Plugin URI: https://serkanalgur.tr
Description: Custom Rewrite
Version: 1
Author: Serkan Algur
Author URI: https://www.serkanalgur.tr
License: GPLv2 or later
License URI: http://www.gnu/licenses/gpl-2.0.html
*/

class FilterRewriteRule
{
    public function init()
    {
        $this->InitRewriteRules();
        add_filter('query_vars', [$this,'add_query_for_sector']);
        add_filter('template_include', array($this, 'filter_template'), 99);
    }

    private function add_query_for_sector($vars)
    {
        $vars[] = 'sector';
        return $vars;
    }

    private function InitRewriteRules()
    {
        add_rewrite_rule(
            'products/import/([^/]*)/?$',
            'index.php?pagename=products/import&sector=$matches[1]',
            'top'
        );

        add_rewrite_rule(
            'products/import/page/([0-9]{1,})/([^/]*)/?$',
            'index.php?paged=$matches[1]&pagename=products/import&sector=$matches[2]',
            'top'
        );
    }

    public function filter_template($template)
    {
        global $wp;
        if (get_query_var('sector') && is_page('import')) {
            $custom_template = locate_template('sector.php');
            if ($custom_template) {
                return $custom_template;
            }
        }
        return $template;
    }
}

register_activation_hook(__FILE__, function () {
    $writing_ideas = new FilterRewriteRule();
    $writing_ideas->init();
    flush_rewrite_rules();
});

add_rewrite_rule add the rewrite rule with pagination support. filter_template locate your sector.php file and redirect the request to this file. query_vars adds sector query to WordPress system.

An alternate Solution

If you're concerned about the Page slug being changed later on, then you could instead use add_rewrite_endpoint(), but this means the URL would need an extra path, e.g. /sector as in example/products/imports/sector/filters.

So the steps are identical, except that you no longer need to manually hook on query_vars because add_rewrite_endpoint() will, by default, do that automatically for you:

  1. Register the sector endpoint for all Pages:

    add_action( 'init', 'my_sector_init' );
    function my_sector_init() {
        add_rewrite_endpoint( 'sector', EP_PAGES );
    }
    

    Remember to remove the add_action('init', 'sector_rewrite_rule', 10, 0); in your code.. and as I already said before, flush the rewrite rules! :)

  2. Load the sector.php template on those custom endpoint pages:

    add_filter( 'page_template', 'my_sector_page_template' );
    function my_sector_page_template( $template ) {
        if ( get_query_var( 'sector' ) ) {
            $template = locate_template( 'sector.php' );
        }
    
        return $template;
    }
    

Other Option when using add_rewrite_rule() with a specific Page slug or path

Store the Page ID in a variable (or database option if you want to) and the previous path in a post metadata, and whenever the Page or its parent is updated, specifically if the slug is changed, then you can programmatically flush the rewrite rules.

So based on my original answer, in step 1, we'll call flush_rewrite_rules() only if applicable:

function sector_rewrite_rule() {
    $page_id = 123;

    // Ensure that the post exists.
    if ( ! get_post( $page_id ) ) {
        return;
    }

    $post_ancestors = get_post_ancestors( $page_id );
    $previous_slug  = get_post_meta( $page_id, '_prev_path', true );

    // Build the hierarchical path.
    $post_ancestors[] = $page_id;
    $current_slug = implode( '/', array_map( function ( $id ) {
            return get_post_field( 'post_name', $id );
    }, $post_ancestors ) );

    add_rewrite_rule(
        '^' . preg_quote( $current_slug ) . '/([^/]+)/?$',
        'index.php?page_id=' . $page_id . '&sector=$matches[1]',
        'top'
    );

    global $pagenow; // this is defined on admin pages
    if ( 'options-permalink.php' !== $pagenow && $current_slug !== $previous_slug ) {
        update_post_meta( $page_id, '_prev_path', $current_slug );
        flush_rewrite_rules();
    }
}

And step 2 remains the same, but step 3 would be (note the $page_id):

add_filter( 'page_template', 'my_sector_page_template' );
function my_sector_page_template( $template ) {
    $page_id = 123;

    if ( is_page( $page_id ) && get_query_var( 'sector' ) ) {
        $template = locate_template( 'sector.php' );
    }

    return $template;
}

So you can try those and just replace the value of $page_id with the correct ID.

Try these steps, which worked well for me:

  1. Use a more specific rewrite rule RegEx pattern like so which ensures the current URL path is in the form of products/imports/<word>:

    function sector_rewrite_rule() {
        add_rewrite_rule(
            // matches products/imports/filters, but not products/imports/filters/foo
            '^products/imports/([^/]+)/?$',
    
            // ensure pagename is in the form of <parent>/<child>
            'index.php?pagename=products/imports&sector=$matches[1]',
    
            'top'
        );
    }
    
  2. Register sector as a public query var via the query_vars hook: (public query vars are those that will be read from GET/URL and POST parameters)

    add_filter( 'query_vars', 'sector_query_vars' );
    function sector_query_vars( $vars ) {
        $vars[] = 'sector';
    
        return $vars;
    }
    
  3. Now that the rewrite rule and query var has been both added, we just need to ensure the sector.php template is used on the products/imports/<word> endpoints. So since we're targeting a Page (post of type page), then I'm using the page_template hook, but you could instead use the template_include hook if you want to:

    add_filter( 'page_template', 'my_sector_page_template' );
    function my_sector_page_template( $template ) {
        if ( is_page( 'imports' ) && get_query_var( 'sector' ) ) {
            // this assumes the template is in the active theme directory
            // and just change the path if the template is somewhere else
            $template = locate_template( 'sector.php' ); // use a full absolute path
        }
    
        return $template;
    }
    

Remember to flush the rewrite rules after you're done with step 1 above! Just visit the Permalink Settings page without having to click on the Save Changes button.

Also, if you need to support pagination (e.g. products/imports/filters/page/2), then you'd need to adjust the above rewrite rule RegEx pattern and query for that.

本文标签: url rewritingHow to add custom rewrite rules and point to specific templates