I'm still struggling with the exact requirements but if I understand correctly I think you can do to accomplish your goals if you use the code I provide you below and then do the following:
Create a page for each of your services (which it sounds like you have already done): /service1/
, /service2/
, etc.
Create a "base" service page, with a URL of /base-service/
; don't worry, nobody will ever see this on the outside of the website.
Assign /base-service/
as the parent (or grandparent) page for all the "virtual shared" child pages you want replicated under each service.
Create any service specific pages that you also might want to have and assign them to the proper page (i.e. "Service 2", for example.)
Create a page template in your theme for your services called page-service.php
and make sure each of your service pages uses that page template (this is needed so we can identify which of your pages are meant to be services).
So here's the code for a class that I called Yoursite_SharedChildPages
which you can store in your theme's functions.php
file, or in a .php
file of a plugin that you might be writing for your site:
if (!class_exists('Yoursite_SharedChildPages')) {
class Yoursite_SharedChildPages {
// If wanted we could enhance to allow these values to be defined in an 'init' hook
static $PAGE_TEMPLATE = 'page-service.php';
static $ABSTRACT_ROOT = 'base-service';
static function on_load() {
// Hook 'request' to route the request correctly
add_filter('request',array(__CLASS__,'request'));
// Hook 'page_link' to compose URLs correctly
add_filter('page_link',array(__CLASS__,'page_link'));
}
static function request($query_vars) {
if (!empty($query_vars['pagename'])) {
// If the page URL rewrite matched meaning WordPress thinks it's a Page
// Split the URL path by path segments (i.e. by slashes)
$pagename = explode('/',$query_vars['pagename']);
if ($pagename[0] == self::$ABSTRACT_ROOT) {
// If the first path segment is the abstract root we care about
if (count($pagename)==1) {
// Don't allow anyone to visit this abstract root page
$pagename = array('#'); // An invalid page name, so it will 404
} else {
// If it is a child page in which case redirect to the first service
// This is important so the user can view the page after they edit it
$pages = self::get_page_ids_by_template(self::$PAGE_TEMPLATE);
if (isset($pages[0]) && $page = get_post($pages[0])) {
// Assuming we have at least one page with the page template
$pagename[0] = $page->post_name;
// then redirect to it the first service found.
wp_safe_redirect('/'.implode('/',$pagename).'/');
exit;
}
}
} else if (count($pagename)>1) {
// If there are child pages
if (get_page_by_path($query_vars['pagename'])) {
// If it is an actual child page then just let it pass thru
} else {
// If a virtual child page then see if parent has the template
$parent = get_page_by_path($pagename[0]);
$pages = self::get_page_ids_by_template(self::$PAGE_TEMPLATE);
if (in_array($parent->ID,$pages)) {
// If virtual child of a service page, change parent to the abstract root
$pagename[0] = self::$ABSTRACT_ROOT;
$query_vars['pagename'] = implode('/',$pagename);
// The URL doesn't match a virtual page, will appropriately get a 404
}
}
} else {
$pagename = false;
}
}
if (is_array($pagename))
$query_vars['pagename'] = implode('/',$pagename);
return $query_vars;
}
static function get_page_ids_by_template($template) {
// This can be performance optimized but I wouldn't worry about it
// until there is a performance issue
global $wpdb;
$sql = "SELECT post_id FROM {$wpdb->postmeta} " .
"WHERE meta_key='_wp_page_template' AND meta_value='%s'";
return $wpdb->get_col($wpdb->prepare($sql,$template));
}
static function page_link($link) {
$parts = explode('/',$link);
if ($parts[3]==self::$ABSTRACT_ROOT) {
// This is really only useful for links in the admin.
if (!is_admin()) {
global $wp_query;
if (!empty($wp_query->query_vars['pagename'])) {
$pagename = explode('/',$wp_query->query_vars['pagename']);
}
} else {
// If we get a URL that uses the abstract root, assign it the first service.
$pages = self::get_page_ids_by_template(self::$PAGE_TEMPLATE);
if (isset($pages[0]) && $page = get_post($pages[0])) {
// Assuming we have at least one page with the page template
$parts[3] = $page->post_name;
// then redirect to it the first service found.
$link = implode('/',$parts);
} else {
$link = preg_replace('#^(https?://[^/]+)(/.*)$#','$1',$link);
}
}
}
return $link;
}
}
Yoursite_SharedChildPages::on_load();
}
And here are some screenshots that can illustrate the steps mentioned above:
Let me know if this is not what you were looking for and/or if you run into something that needs to be addressed by this solution in addition to what I have coded. Good luck!