0

Is it possible to have a template page in Wordpress, that could be used for different pages dynamically?

For example, I have a page template cars.php and I would like to use it for subpages of different types of cars e.g.: limousine, coupe, suv, van.

I would like to have this hierarchy of pages:

cars ─┬─limousine
      │
      ├─coupe
      │
      ├─suv
      │
      └─van
 

with this links:

example.com/cars/limousine/
example.com/cars/coupe/
example.com/cars/suv/
example.com/cars/van/

These types of cars come from a database where we could add cars of a new type, say spider.

I know how to do that manually, i.e. making pages limousine, coupe, suv, van and spider creating them using Wordpress editor where I would choose car.php as a template.

So in my cars.php page template I would get data of, say, limousine type if a user clicked on example.com/cars/limousine/ link. How should I use my cars.php page template so that I could get this page with the specified hierarchy? How would Wordpress recognise the link if I didn't explicitly created each of pages (limousine, coupe, suv, van and spider), how could that info about certain page be passed to cars.php?

Best regards,

Igor

Igor Beuermann
  • 127
  • 1
  • 10

1 Answers1

1

Edited post

I've removed my original response since it was getting in the way.

The way that WordPress (and many/most modern CMSs) work, is that they tell the server such as Apache, Nginx or IIS to do their normal stuff, but if the server results in an effective 404, pass that result into the CMS at a known point (index.php) for further processing.

Server level

The first place that you could hook into would be at the server level using something like an .htaccess file. You could have a rule that maps /cars/* onto just /cars/?car_type=*. Not redirect, just map. The map should result in an effective 404 at the server level which means WordPress kicks in with a known URL and a query string that it can just ignore. (I'm 95% certain this would work, but I don't have an Apache server available to test with right now.) You could register the query parameter, or you could just use $_GET and perform logic.

But I'm not a big fan of editing server config files, especially if this needs to be more dynamic, or be manageable by people that are comfortable with doing that.

WordPress level

So the next stop is to handle things somehow in WordPress. The bad news is that no matter what you do, WordPress is going to consider this a 404. The good news is, however, you can reroute the 404 as you need.

I'm going to keep going with the /cars/limo path, but you can adapt as necessary.

The first two steps are optional but are closer to the "official" way of doing things and I would encourage you to do them, but I'll explain later how you can skip them.

First, we need to register a pattern with WordPress that we're interested in. The first parameter is a regex, the second is a mapping, and the last says that we basically want to trump other rules. One you add this code, or make any changes to it, you need to flush your WordPress permalinks. Also, if you are developing locally and don't have rewrites enabled at all, you'll need to turn them on.

add_action(
    'init',
    static function () {
        add_rewrite_rule('cars/(.+)[/]?$', 'index.php?car_type=$matches[1]', 'top');
    }
);

The previous thing didn't do a whole lot, however, so we also need to tell WordPress that we're interested in certain query strings. This query string should be the same between the previous and following functions.

add_filter(
    'query_vars',
    static function ($query_vars) {
        $query_vars[] = 'car_type';
        return $query_vars;
    }
);

The last step is to use the magic template_include filter along with get_query_var. This filter is called on every non-admin page, so be careful with it and make sure you only change the return if you are guaranteed to be in your specific scenario. The get_query_var is able to pluck out our query string that we registered in the second step.

add_filter(
    'template_include',
    static function ($template) {
        $car_type = get_query_var('car_type');
        if ($car_type) {
            // The path must be absolute
            return __DIR__ . '/cars.php';
        }

        return $template;
    }
);

For the if logic, you might want to perform additional things such as checking an array to see if it is in the collection of known types. You really should avoid making a database call here if at all possible, because remember, this is called on every page.

Skipping the first two steps

If did say that you could skip the first two steps, and although I don't recommend it, I want to show you how you can do it. We're still going to use the template_include hook, but now we're just going to look at the URL manually and do things with it. This isn't guaranteed to survive across different server environments, and you could run into some funky scenarios, so I say skip it. I'm also not going to include logic for figuring out the page itself, that should be pretty easy, too.

add_filter(
    'template_include',
    static function ($template) {
        $url = wp_parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
        if( YOUR LOGIC FOR PARSING URL HERE ){
            return 'cars.php';
        }

        return $template;
    }
);
Chris Haas
  • 53,986
  • 12
  • 141
  • 274
  • Thank you @Chris Haas. You obviously rock here! But I don't know if I was clear. I have cars.php page template. I'd like to create child pages dynamically. If a user clicks example.com/cars/van/ I'd like it is created on the fly without making van page. I simplified this – we don't sell cars, we sell lights so there's much more ranges of products. I'd like to avoid making 30+ pages in the level below the cars template page. I'd like to generate this pages on the fly with a page template from the parent level. Maybe I should put the template in the child level – I don't know, I am asking. – Igor Beuermann Apr 29 '21 at 08:56
  • 1
    @IgorBeuermann, I think I understand now and I've updated my answer, hopefully that works. I'm assuming that once you are in `cars.php` you are going out to your own tables or doing some other logic to differentiate those URLs. The code above will load your template and I'll leave the rest to you, good luck! – Chris Haas Apr 29 '21 at 13:55
  • This is an elaborate answer!!! Let me chew through it and I'll come back, Thank you so much for your effort! – Igor Beuermann Apr 30 '21 at 09:50
  • I have cars one level deeper, namely: `example.com/products/cars/`. So in the `add_rewrite_rule()` I changed `'car/(.+)...'` to `'products/car/(.+)...'` `var_dump(query_vars)` in the `query_vars` filter shows me `'limo'` in the array. If I echo anything in the `template_include` I get some body (header and footer). Body tag shows it is blog. Otherwise the body is empty. I do everything having `example.com/products/cars/limo/` in the browser address bar. It seems this steps don't reach the `cars.php` page template. If I can ask for some more help... – Igor Beuermann May 04 '21 at 10:21
  • 1
    @IgorBeuermann, just to be clear, in the `template_include` filter, the only thing that you should be doing is returning which template on disk that you want to use. Unless you are debugging something, you shouldn't perform any `echo`, `include` or anything else that could change the flow of that filter. Is that what you are doing? Also, just to make sure, after you changed your rewrite rule, did you flush your permalinks? – Chris Haas May 04 '21 at 12:56
  • Yes, this was just for me to see whether there comes anything through. I just saw a blank page so I started discovering. I don't intend to insert anything into the filter. I just wanted to see what's going on. Otherwise – all the logic is in the `cars.php`. I just need to get to the `limo` subpage. Perhaps just one thing more: the `products` page and `cars.php` are in the same directory. In WP I defined `product` page (using `cars.php` page template) as `cars` page parent. So I can see `example.com/products/cars/` I'm sorry you encountered to such a novice in Wordpress... – Igor Beuermann May 04 '21 at 13:52
  • And, yes: after each change of rewrite rule I flushed permalinks by saving them in the admin page. – Igor Beuermann May 04 '21 at 14:13
  • 1
    @IgorBeuermann, I made one update above and changed the template to be an absolute using `return __DIR__ . '/cars.php';` (assuming this code lives in `functions.php`). If you are debugging inside of `template_include`, always issue a `die` because you will see "other things" that happen after which can clutter what is really going on. I made a quick demo site, created a page with a URL of `/products/` and then created another page as a child of it with a URL of `/products/car`, updated the rewrite, flushed and visited `/products/car/limo/`, and everything worked. – Chris Haas May 04 '21 at 14:31
  • Inside of `cars.php`, you can once again access `get_query_var('car_type')` to see what's in there and perform your own logic. It sounds like you are pulling this information from something that isn't WordPress, is that right? – Chris Haas May 04 '21 at 14:32
  • Finally! I got through! But I see I'll still have lots of work. Namely, css is wonky. I obviously land to this page through `Single Post Page -> Blog Post` route instead of `Static Page -> Page Template` route. Plus the breadcrumbs now show just `example.com`. They are from plugin though so they don't know what's going on. Plus I'll need to change the `` in the ``. But that's about it, no problems this is more front-end which I am more familiar with. Thank you a bunch, Chris! I'll check it as answered. – Igor Beuermann May 04 '21 at 15:49
  • Yes, the logic is in php and connected with a database. This is also easier for me. But these rules on servers give me nightmares :) – Igor Beuermann May 04 '21 at 15:52
  • 1
    Glad it is working! If you are using Yoast for breadcrumbs and get stuck, check out the filter `wpseo_breadcrumb_links` and post if you've got a question. You can tag me in this thread as a heads up if you want – Chris Haas May 04 '21 at 15:59
  • I have a Breadcrumb NavXT plugin. If I won't be able to change the breadcrumbs I'll consider Yoast - I know it's got breadcrumbs too (of course this is just tiny bit of it). And if I step in murky waters again I'll give you a call. – Igor Beuermann May 04 '21 at 16:24
  • Hello, @ChrisHaas, I have some troubles adding two more similar cases as you helped me before. The second still does it's job but somehow doesn't include all placeholders in matches[]. The third is even worse and maps the link to the first case link. I suppose the catch is in regex. I don't know should I open a new question or can I post you the details? How? – Igor Beuermann Jul 06 '21 at 15:03
  • @IgorBeuermann, open a new question that has everything that is working along with your updates. If it makes sense to reference this answer you can do that, too. – Chris Haas Jul 06 '21 at 22:41
  • Thank you very much @ChrisHaas ! It's in https://stackoverflow.com/questions/68275907/wordpress-mapping-with-add-rewrite-rule-and-regex I truly believe it's regex. – Igor Beuermann Jul 07 '21 at 08:23