Create Custom URL Paths and Templates in WordPress

If you need to create a custom URL Path (rewrite) it’s easy enough to  just create the page in the admin and create a custom shortcode that will output the HTML that you want.  However, you would not be able to properly re-theme this output so a preferred method is to create a URL rewrite that will automatically pull a custom template file that can be customized in a theme as needed.

Create URL Path Rewrites

After you add or update a rewrite you need to flush the rewrites. This can be done using flush_rewrite_rules() or by going to the Settings > Permalinks page and saving a change there (which will issue the same flush).

/**
 * URL Rewrites
 */
public function myRewrite()
{
	/** @global WP_Rewrite $wp_rewrite */
	global $wp_rewrite;
	
	$newRules = array(
		'pets/?$' => 'index.php?my_page=pet',
		'pets/(\d+)/?$' => sprintf(
			'index.php?my_page=pet&pet_id=%s',
			$wp_rewrite->preg_index(1)
		),
	);

	$wp_rewrite->rules = $newRules + (array) $wp_rewrite->rules;
}

add_action('generate_rewrite_rules', 'myRewrite');

Template File Redirect

This handles getting the overridden file in a theme or falls back to your copy in your plugin.

/**
 * Set templates for custom pages
 *
 * @see http://stackoverflow.com/questions/4647604/wp-use-file-in-plugin-directory-as-custom-page-template
 */
public function myThemeRedirect()
{
	$plugindir = dirname(__FILE__);
	$prefix = 'myprefix'; // Set custom prefix for each plugin to prevent conflicts
	$themeFilesDir = 'theme-files'; // Sub directory in your plugin to put all your template files

	$page = get_query_var('my_page');
	$petId = (int)get_query_var('pet_id', 0);

	if ($page == 'pet' && empty($petId)) {

		// Pet Archive
		$data = array( // Data you can pass to the template
			'pets' => array(
				array('name' => 'Fluffy', 'species' => 'dog'),
				array('name' => 'Spot', 'species' => 'cat'),
			),
			'action' => 'my_action'
		);
		$filename = 'archive-pet.php'; // filename of template
		$fullTemplatePath = TEMPLATEPATH . DIRECTORY_SEPARATOR . $prefix . DIRECTORY_SEPARATOR . $filename;
		$returnTemplate = (file_exists($fullTemplatePath)) ? $fullTemplatePath : $plugindir . DIRECTORY_SEPARATOR . $themeFilesDir . DIRECTORY_SEPARATOR . $filename;
		doMyThemeRedirect($returnTemplate, true, $data);
		return;

	} elseif ($page == 'pet' && !empty($petId)) {

		// Single Pet
		$data = array( // Data you can pass to the template
			'pet' => array('name' => 'Fluffy', 'species' => 'dog'),
		);
		$filename = 'single-pet.php'; // filename of template
		$fullTemplatePath = TEMPLATEPATH . DIRECTORY_SEPARATOR . $prefix . DIRECTORY_SEPARATOR . $filename;
		$returnTemplate = (file_exists($fullTemplatePath)) ? $fullTemplatePath : $plugindir . DIRECTORY_SEPARATOR . $themeFilesDir . DIRECTORY_SEPARATOR . $filename;
		doMyThemeRedirect($returnTemplate, true, $data);
		return;

	}
}

/**
 * Process theme redirect
 *
 * @param mixed $path
 * @param bool $force force redirect regardless of have_posts()
 * @param array $data vars to set for theme
 */
function doMyThemeRedirect($path, $force=false, $data=array())
{
	global $post, $wp_query;

	if (have_posts() || $force) {
		if (!empty($data)) extract($data);
		include($path);
		die();
	} else {
		$wp_query->is_404 = true;
	}
}

add_action('template_redirect', 'myThemeRedirect');

Register Custom Query Variables

If you need to use any custom query variables such as “my_page” and “pet_id” in the example above, you need to register them.

/**
 * Register custom query vars
 *
 * @param array $vars The array of available query variables
 *
 * @return array
 *
 * @link https://codex.wordpress.org/Plugin_API/Filter_Reference/query_vars
 */
public function myRegisterQueryVars($vars)
{
	$vars[] = 'my_page';
	$vars[] = 'pet_id';
	return $vars;
}

add_filter('query_vars', 'myRegisterQueryVars');

Leave a Reply

Your email address will not be published. Required fields are marked *