Jump to content

Sublayouts or a way to imitate template inheritance


Ivan Gretsky
 Share

Recommended Posts

Good day!

I am getting into Wireframe and liking it a lot. But I still have a lot to figure out. Before Wireframe I was using something I called a sublayout. My <head>, opening and closing <body> tags and all the stuff before the latter, like 3rd party marketing tags and js, were all in a skeleton.layout.php file. This file was common for all pages. Templates had their views, but they were not included directly in the skeleton.layout.php but rather in one of the few sublayout files, which also held some extra markup, common to groups of templates. I usually had at least 2 of them - one for the homepage, and another one for all the rest. But sometimes there were more of them.

I came up with this concept trying to imitate twig's template inheritance feature. I am not using twig and do not want a templating language in my projects (yet?), but want something similar. And sublayouts was my approach to it. I also did research things like plates, but never used nothing.

Now I am thinking, what would be the best approach to handle this with Wireframe. I can see these options:

  1. Using twig via renderers. But I am still not sold on bringing it into my projects.
  2. Making 2 or more layouts and including the upper and lower part as partials. But I do not like to have this base template code split around a few files. This way it seems harder to deal with the code.

But just maybe there are other options to configure Wireframe to do what I am after using its hidden powers?

Link to comment
Share on other sites

  • Ivan Gretsky changed the title to Sublayouts or a way to imitate template inheritance
On 12/7/2021 at 5:43 PM, Ivan Gretsky said:

Before Wireframe I was using something I called a sublayout. My <head>, opening and closing <body> tags and all the stuff before the latter, like 3rd party marketing tags and js, were all in a skeleton.layout.php file. This file was common for all pages. Templates had their views, but they were not included directly in the skeleton.layout.php but rather in one of the few sublayout files, which also held some extra markup, common to groups of templates. I usually had at least 2 of them - one for the homepage, and another one for all the rest. But sometimes there were more of them.

I came up with this concept trying to imitate twig's template inheritance feature. I am not using twig and do not want a templating language in my projects (yet?), but want something similar. And sublayouts was my approach to it. I also did research things like plates, but never used nothing.

If I got this right, you've got a three-layer approach (layout - sublayout - view) instead of Wireframe's regular two-layer design (layout - view).  If so, this is not really something I've had much need for before, but here are a couple of rough ideas:

  • If you have, say, "single column sublayout" and "two column sublayout", you could include the content that you'd usually put into these directly in the main layout file, accompanied by some sort of switch. For an example you could pass an argument to the layout via the bootstrap file (or from the Controller class), stating which sublayout you want to use for current page/template, and then just include related if statement in the layout file.
    • This is, of course, not a very clean solution if sublayouts differ a lot from each other. But if the differences are minor and/or sublayouts are relatively simple, then introducing a whole new layer might be overcomplicating things a bit.
  • You could use partials: instead of directly including <?= $placeholders->default ?> in your layout file, you could call <?= $partials->sublayouts->single_column() ?> etc. and thus mimic that extra layer. Of course this still requires some method of defining which sublayout to use for each template or page.

 Now, one thing I don't fully grasp is that how do you decide which template(s) use a specific sublayout? If I knew that, I might be able to suggest better solution. Would also be interesting to hear a bit more about what exactly is in the layout vs. sublayout.

This might be something that we could support in Wireframe right out of the box — though before that I'd like to fully understand how it should work, and what is / what are the problem(s) it solves 🙂

  • Like 1
Link to comment
Share on other sites

Thanks for the thorough answer,  @teppo!

On 12/8/2021 at 10:19 PM, teppo said:

Now, one thing I don't fully grasp is that how do you decide which template(s) use a specific sublayout? If I knew that, I might be able to suggest better solution.

For now it is done in the code. As I said, I've been using a variation of Wire Render Pattern approach. The template file in this approach is kind of like a controller. I've set layout and sublayout vars in _init.php to the most used values and overrode them in template files. In _main.php I've passed template-specific content string to sublayout and then the whole thing to the layout.

On 12/8/2021 at 10:19 PM, teppo said:

Would also be interesting to hear a bit more about what exactly is in the layout vs. sublayout.

Most of the times I only had one layout - a skeleton with html, head, body, script tags + counters and a place to include a sublayout. Everything else went  to sublayouts and views.

In my current project this makes a lot of sense as there is completely different markup in different areas of the site (including different header and footer), but the assets (css and most of the js) are common for all areas. So it is comfortable to have common things in the layout, all the variable chrome in sublayouts, and template-specific stuff in the views.

Link to comment
Share on other sites

Thanks for the clarifications, Ivan.

At the moment the <?= $partials->sublayouts->sublayout_name() ?> approach is probably the closest alternative one to what you've been using. Or, alternatively, you could split common parts of the layout to partials, and include those in two different layouts, leaving out the layout/sublayout separation altogether.

That being said, I just mocked up locally a setup where layouts can be embedded within layouts. This is probably even closer, though I feel like it needs a bit more polish and testing 🙂

  • Like 2
Link to comment
Share on other sites

  • 1 month later...
On 12/15/2021 at 12:18 AM, teppo said:

That being said, I just mocked up locally a setup where layouts can be embedded within layouts. This is probably even closer, though I feel like it needs a bit more polish and testing 🙂

That is just amazing news!

Created a github issue about this enhancement so we can track it.

  • Like 1
Link to comment
Share on other sites

  • 1 month later...

So... that "layouts within layouts" thing almost ended up in v 0.22, but I decided to hold it back. I don't actually think it solves the right issue, or that it makes enough sense as is. In other words I believe I may have been looking at this from the wrong direction.

That, or I just need some extra information before going forward 🙂

What I was going to add would've been a new $layouts API variable, which would've made it possible to embed second (named) layout within one layout. Something along these lines:

<!-- layouts/default.php -->

<head>
	<title>Hello World</title>
</head>

<body>
	<?= $layouts->render('single-column') ?>
</body>

<!-- layouts/single-column.php -->

<main>
	<h1><?= $page->title ?></h1>
	<?= $page->body ?>
</main>

The only thing this really adds to current feature set is the ability to reuse layouts — which, to be honest, doesn't seem particularly useful, especially considering that the downside is that it adds a new API variable, new layer of complexity, etc. To me it doesn't seem too different from this, which is already doable:

<!-- layouts/default.php -->

<head>
	<title>Hello World</title>
</head>

<body>
	<?= $partials->render('sublayouts/single-column') ?>
</body>

<!-- partials/sublayouts/single-column.php -->

<main>
	<h1><?= $page->title ?></h1>
	<?= $page->body ?>
</main>

With recent API updates, it's also quite straightforward to dynamically change the sublayout:

<!-- layouts/default.php -->

<body>
	<?= $partials->render('sublayouts/' . $view->sublayout ?: 'default') ?>
</body>
  
<!-- controllers/HomeController.php -->

<?php
// ...
public function render() {
	// override sublayout for the home template
	$this->view->sublayout = 'home';
}
// ...

Unless I'm missing something, this seems like a pretty clean way to achieve simple sublayouts. Only difference is that they live under the "partials" directory, but after thinking this through that actually seems like a logical place: having them under "layouts" would feel a bit weird (these are different from the "main layouts" after all), while adding a whole new "sublayouts" directory at the root level would, at least to me, seem a bit unnecessary.

(Although if partials get the ability to point to files in other / absolute directories, it will of course become possible to add such directory on a case by case basis.)

Am I on the right tracks here, @Ivan Gretsky? 🙂

Technically you can go as deep as you want with this type of structure, with relatively little code: if you need a third level of sub-sub-layouts, those would just need to be rendered in the sublayout partial(s). I guess it's, in a way, the opposite of what Twig/Blade/etc. do: instead of the sublayout or view defining that they extend some other file (layout or sublayout), you'd output the child in the parent file.

Anyway, let me know what you think. I'll be happy to work on this further, but I've realized that since this isn't really something I personally use, it's way too easy to forget what the actual problem I'm solving was... 😅

  • Like 1
Link to comment
Share on other sites

Thanks for continuing to think and work on it, @teppo!

I really like the new partials methods. If there would be a way to call a partial from a custom path like layouts/sublayouts that would be almost it. The question I still have is whether the view placeholders will still work in those placeholders, so I can still use them where needed?

---

The other way I can think of is defining a layout stack with an array, where 1st layout when rendered is passed to the second and so on. Each of the layouts from the stack receive all view placeholders, so they could be used anywhere. But this should be thought out thoroughly.

 


 

  • Like 1
Link to comment
Share on other sites

On 3/2/2022 at 5:30 PM, Ivan Gretsky said:

I really like the new partials methods. If there would be a way to call a partial from a custom path like layouts/sublayouts that would be almost it.

I'll look into this.

It feels like this should be accompanied by a config setting that defines allowed partials directories, to avoid any potential security issues down the line, and I'd also like to extend this to components (or at least build it in a way that makes sense for that context as well). I seem to recall a request for "components from/as modules" (I believe it was suggested by @bernhard), and this could be a good initial step towards that.

One thing that won't be supported is accessing partials in other root directories via the object-oriented $partials->path->to->partial syntax. I don't think it's essential here, mostly just thinking out loud. There are both logical issues (potentially conflicting names etc.) as well as technical ones (currently partials directory gets "pre-loaded" to memory during Wireframe bootstrap) that would make this problematic. (And, again, I don't think it's even necessary.)

On 3/2/2022 at 5:30 PM, Ivan Gretsky said:

The question I still have is whether the view placeholders will still work in those placeholders, so I can still use them where needed?

If by latter placeholders you mean partials, then yes, sublayouts implemented as partials have access to $placeholders. Actually had to test as I wasn't sure, but it looks like it works right out of the box 😅

On 3/2/2022 at 5:30 PM, Ivan Gretsky said:

The other way I can think of is defining a layout stack with an array, where 1st layout when rendered is passed to the second and so on. Each of the layouts from the stack receive all view placeholders, so they could be used anywhere. But this should be thought out thoroughly.

For the record I did consider this approach too, but felt that it wouldn't fully solve the issue.

Perhaps I'm too invested in the concept of inheritance as in templating languages, but the main issue is that this would likely be one-dimensional, while I'd like to see something that is more flexible. Sure, one-dimensional stack with enough layers could achieve almost anything, but that could get super complicated and would also affect performance negatively.

There may well be something to this idea, but for the time being I think other routes look more promising 🙂

  • Like 1
Link to comment
Share on other sites

On 3/4/2022 at 10:14 AM, teppo said:

I seem to recall a request for "components from/as modules" (I believe it was suggested by @bernhard),

Thx teppo, I'm not sure if it was me. I never got any further than testing WireFrame. I think I don't have it in any of my projects so it's out of my head and you can ignore my request if there is still something left 🙂 

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...