Important announcements from CouchCMS team
6 posts Page 1 of 1
More often than not, the templates used in most sites share many common markup elements like header, sidebar, footer etc.
For reusing such elements across templates, the standard method in Couch, as I am sure you know, is isolating them as 'snippets' and then incorporating them back through the use of <cms:embed> tag (people often call this process 'chunking').

Couch, true to its philosophy, does not mandate or prescribe any hard-and-fast procedure for this chunking. One is free to follow any way that suits the use-case - often the best way is simply isolating the header and footer parts of the layout and embedding those in all templates.

However, for sites with more complex needs, following certain well established patterns of chunking the layout makes life a lot easier for developers.
One such pattern is the 'decorator' pattern. You can find a discussion on it at https://www.couchcms.com/docs/advanced- ... views.html where we use it in our 'Advanced tutorial'.

Our implementation of it mentioned above is rather simple but serves the needs of that particular use-case.
PHP heavy-weights like Symfony (and also others like Laravel) have used a different take in their implementation of it (if it interests you, you can find details about it at https://symfony.com/doc/current/templat ... nd-layouts).

It involves defining 'blocks' in the parent template and then the child templates 'extend' the parent (i.e. inherit it) and replace the parent's blocks with their own.

It may appear a bit complex at first sight but is, no doubt, a very powerful and flexible approach.
Particularly, there are two points in it that cannot be accomplished with our simple approach -

1. The child template can be extended by another child template which, then in turn, can be extended by another - it can go on without limits. This can be invaluable in implementing very complex layouts.

2. More importantly, any child template can access the original contents of its parent template.

So, sometime last year while preparing Couch v2.0, I added this method to our code base and then conveniently forgot about it :)
That is to say, it has always been possible with v2.0 to create layouts using blocks/extends but yours truly forgot to document the process so it was as good as not having it in the first place.

Anyway, time to make up for the lapse. Here is everything you'd need to know if this method holds any interest to you.

Meet the block/extends tags

There are primarily two new tags that'll help us in implementing this method - <cms:block> and <cms:extends>
An easier way of understanding how these tags work would be to see them action.

Take a look at the following snippet (please note that this is a 'snippet' i.e it resides in the snippets folder of your installation which, by default, is 'couch/snippets' but you can change the location for config). Let us call it 'parent.html' -
Code: Select all
<!DOCTYPE html>
<html>
    <head>
        <cms:block 'head'>
            <link rel="stylesheet" href="style.css" />
            <title><cms:block 'title'></cms:block> - My Webpage</title>
        </cms:block>
    </head>
    <body>
        <div id="content"><cms:block 'content'></cms:block></div>
        <div id="footer">
            <cms:block 'footer'>
                &copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
            </cms:block>
        </div>
    </body>
</html>

As you can see, it contains the base skeletal HTML layout.
Also notice the <cms:block> blocks it contains (there are four of them).

For understanding, you could consider these block elements as 'placeholders'.
Any snippet 'extending' (or 'inheriting') this snippet can then choose to replace these placeholders (blocks) with its own.

Let us create one such snippet. Take a look at the following snippet (again, remember this is a 'snippet' and mandatorily needs to placed within the snippets folder). Let us call it 'child.html' -
Code: Select all
<cms:extends 'parent.html' />

<cms:block 'title' >Index</cms:block>

<cms:block 'head' >
    <cms:block_parent />
    <style type="text/css">
        .important { color: #336699; }
    </style>
</cms:block>

<cms:block 'content' >
    <h1>Index</h1>
    <p class="important">
        Welcome on my awesome homepage.
    </p>
</cms:block>

Some important points about this child snippet -
1. Notice the <cms:extends> tag right at the top. This statement is what signifies that this snippet is actually a child snippet and is extending the specified snippet (parent.html in this case).

V. IMP: The <cms:extends> statement absolutely needs to be the very first line in the child statement else things will not work.


2. Also notice that this child snippet contains *only* <cms:block>s as first-level tags. This is important - all other tags at first-level will be totally ignored (in Symfony this actually throws an error). These blocks, in turn, can have any markup or Couch tags within them (even other <cms:block> tags nested within).

OK, now to understand what this peculiar structure of the child snippet does.
Notice the names of the <cms:block>s you see defined in there. They match those of the blocks appearing in the parent snippet.

Now when the <cms:extends> tag kicks in, it fetches the specified snippet (parent.html), replaces all blocks within the parent snippet with blocks contained within itself (i.e. the child snippet) having matching names and then finally executes the resulting code.

[Please notice that our child snippet has only three blocks defined so only these three will be substituted in the parent snippet. The remaining fourth block in the parent snippet will continue using its own code.]

Effectively, this means that the child snippet *dynamically modifies* the code within the parent snippet (by injecting its own blocks into it) and it is this modified code that finally executes and produces the output.

Powerful stuff. If you are familiar with Object Oriented Programming (OOP), this effectively is exactly what happens when a class inherits (or extends) a parent class. Symfony rightly names this method 'Template Inheritance'.

Alright, so as the final step let us now see the modified output.
For that we'll need a bonafide Couch template (i.e. a PHP file with <?php require_once("couch/cms.php"); ?> and <?php COUCH::invoke(); ?> statements and appearing in the admin-panel sidebar).

Let us create a simple Couch template to test our experiment so far. Create a file named 'test.php' in your site's root and place the following code within it -
Code: Select all
<?php require_once("couch/cms.php"); ?>
<cms:template>

</cms:template>

<cms:embed 'child.html' />

<?php COUCH::invoke(); ?>

I'm sure you'll recognize that it is a regular Couch template (albeit with no editable regions defined yet). Notice how it is embedding the 'child' snippet.
Perform the mandatory visit to it on the frontend as super-admin for it to be registered.

You'll see some output on the frontend while visiting this template. To see exactly what the generated HTML behind it is, do a view-source.
You should something like this -
Code: Select all
<!DOCTYPE html>
<html>
    <head>
        <link rel="stylesheet" href="style.css" />
        <title>Index - My Webpage</title>
        <style type="text/css">
            .important { color: #336699; }
        </style>
    </head>
    <body>
        <div id="content">   
            <h1>Index</h1>
            <p class="important">
                Welcome on my awesome homepage.
            </p>
        </div>
        <div id="footer">
            &copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
        </div>
    </body>
</html>

Notice how the generated HTML is basically the code in the parent snippet mixed with the code from the child snippet within the blocks we defined.

I'd like to draw your attention particularly to the <head> section. Notice how it contains code from *both* parent snippet as well as the child snippet (you might want to scroll up and examine the two snippets now).

As you have seen, the parent snippet had a block named 'head' with some code within it. The child snippet also had a block named 'head', so the code from this child block should totally replace that from the parent block. And so it does. However, the child block had the following statement within it -
Code: Select all
<cms:block_parent />

This tag (the third and last new tag we'll encounter) fetches the parent block's original code so the effective child code becomes a mix of the parent code plus the child code that follows the <cms:block_parent />.

This is one of this method's distinguishing feature that I mentioned at the start of this discussion -
2. More importantly, any child template can access the original contents of its parent template.


So basically that is it. This is how this new method of 'inheritance' works.

Allow me to put a finer point on some aspects that are easy to miss while learning them -
1. Symfony calls this method 'Template inheritance' but in Couch's context this is actually 'Snippet inheritance'.
Please keep in mind that both the 'parent' as well as the 'child' involved in this inheritance layout are 'snippets' in Couch's parlance (i.e. are files that are kept in snippets folder where they can be found by <cms:embed>).

2. Since we know that snippets cannot be directly invoked from the browser (i.e. do not have a URL leading upto them), the only way to make the snippets click into action is by embedding them (i.e. use <cms:embed>) in a bonafide Couch template (again 'template' in Couch parlance is a public facing PHP file i.e. one that can be accessed from browser using its URL, and registered with Couch).

Keeping Couch's terminology in mind, the point I am trying to make is that we cannot actually make a 'template' inherit another 'template' (i.e. don't try putting the <cms:extends> statement directly within a 'template' and expect it to inherit another 'template'). We have to use 'snippets' for that. A 'template' can only call a 'snippet' using <cms:embed>.

3. With the last point hopefully clear, take another look at the 'template' in our example (i.e. test.php). As discussed above, it is indeed using a <cms:embed> statement. The point I'd like to stress here is that the template is embedding the 'child' snippet (i.e. the one that has the <cms:extends> statement) and *not* the parent snippet.

This should be logical to see at this point now that we have already seen that it is actually the <cms:extends> tag that does all the magic of substituting code within the parent snippet with code within itself. We begin from the end of the chain - call the child snippet and it will automatically call the parent it is extending.

Wrapping up this post, following is a full real-life example implemented using this inheritance method.

A complete real-life example
Symfony suggests organizing complex layouts using 'three-level' inheritance.
Following is the Couchified version of the example code used in their docs (http://symfony.com/doc/current/templati ... tance.html) -

1. Create a 'base.html' snippet that contains the main layout for your site -
Code: Select all
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title><cms:block 'title'>Welcome!</cms:block></title>
        <cms:block 'stylesheets'></cms:block>
        <link rel="icon" type="image/x-icon" href="<cms:show k_site_link />assets/favicon.ico" />
    </head>
    <body>
        <cms:block 'body'></cms:block>
        <cms:block 'javascripts'></cms:block>
    </body>
</html>

2. Create a snippet for each "section" of your site. For example, the blog functionality would have a snippet called 'blog.html' that contains only blog section-specific elements -
Code: Select all
<cms:extends 'base.html' />

<cms:block 'body' >
    <h1>Blog Application</h1>

    <cms:block 'content'></cms:block>
</cms:block>

3. Create individual snippets for each 'view' (i.e. page-view, list-view, home-view etc.) used by the section and make each extend the appropriate section snippet. For example, the 'list-view' snippet would be called 'blog-list.html' and list the actual blog posts -
Code: Select all
<cms:extends 'blog.html' />

<cms:block 'content' >
    <cms:pages paginate='1' limit='10'>
        <h2><a href="<cms:show k_page_link />"><cms:show k_page_title /></a></h2>
        <p><cms:show content /></p>
       
        <cms:paginator />
    </cms:pages>
</cms:block>

Similarly, the 'page-view' snippet would be called 'blog-page.html' and would display contents from a single page -
Code: Select all
<cms:extends 'blog.html' />

<cms:block 'content' >
    <h2><cms:show k_page_title /></h2>
    <p><cms:show content /></p>
   
    <a href="<cms:show k_template_link />">Back to listing</a>
</cms:block>

Finally, make the template for the section call the appropriate snippets. For example, the 'blog.php' template below -
Code: Select all
<?php require_once('couch/cms.php'); ?>
<cms:template title='Blog' clonable='1'>
    <cms:editable name='content' label='Contents' type='richtext' />
</cms:template>
<cms:if k_is_page>
    <cms:embed 'blog-page.html '/>
<cms:else />
    <cms:embed 'blog-list.html '/>
</cms:if>
<?php require_once('couch/cms.php'); ?>

Notice how the view snippets (blog-list.html, blog-page.html) extend the section template (blog.html) which in turn extends the base layout snippet (base.html). This is the common three-level inheritance model.

When building your site, you may choose to follow this method or simply make each view snippet extend the base layout snippet directly (i.e. 'two-level' inheritance as seen in the first example above).

In the example above, all the snippets were placed right within the 'snippets' folder.
I personally find it useful to organize the snippets within sub-folders as follows (you are free to use whatever way suits you) -
Code: Select all
couch
    |_snippets
        |_base.html
        |_blog
        |    |_base.html
        |    |_list.html
        |    |_page.html
        |    |_archive.html
        |_products
        |    |_base.html   
        |    |_list.html
        |    |_page.html 
        |_contact.html

So now my main template (e.g. blog.php) would contain -
Code: Select all
<cms:embed 'blog/page.html' />

The view snippet (e.g. blog/page.html) would in turn contain -
Code: Select all
<cms:extends 'blog/base.html' />

And finally the section snippet (e.g. blog/base.html) would contain -
Code: Select all
<cms:extends 'base.html' />

In closing, not every site is likely to warrant the use of this method. But for those that do, a small amount of planning would go a long way in making the process of implementing the site so much easier.

Hope this helps. Do let me know if something is unclear.
Lovely, lovely, absolutely lovely feature!! :D

a small amount of planning would go a long way in making the process of implementing the site so much easier.

KK, could you maybe share some tips for a good planning?
This is a most welcome feature, I found myself needing exactly this solution recently when working on a V2 site, I was trying to optimize site structure to remove as much duplication as possible while also allowing some templates to require different things. A good example would be clonables:

We cannot currently define "list-view editables" in a clonable template, so we must use a second empty template to define editable regions for use in a list-view template. For this specific site I have been experimenting with making SEO management as easy as possible, which includes managing the
Code: Select all
<meta name="robots" content="noindex,nofollow" />
tag in the head of a template. For every normal template this was simple: Define some "SEO Settings" editables for templates, check if the user has added any content to the robots editable and output it. When it comes to the "list-view" of a clonable template, though, the editable regions belong to another template, so you must fetch the dummy template editables, which requires some separate tags.

In the end the solution was to end the <head> snippet without the closing </head> tag, then I could add custom tags into the head of each template. This allowed me to copy/paste the code for all of the normal templates across, then create the custom code for each list-view section.

Snippet inheritance, when implemented, will allow me to define two separate snippets that only have a small section of code changed, inheriting most of the output from the parent snippet.

This feature allows for much greater workflow in couch. Thank you @KK! Hope my response has given some insights in how such a feature can be used and will help us couch users.
Image
@bartonsweb, thanks for input.
I will also share something that I used to load template-specific resources/snippets:

Code: Select all
<!--Load Template-specific JS-->
<cms:if "<cms:exists "../assets/js/pages/<cms:show k_template_name />.js" />" >
   <cms:rel src="assets/js/pages/<cms:show k_template_name />.js" last_mod='1' />
<cms:else />
    <cms:rel src="assets/js/pages/general.js" last_mod='1' />
</cms:if>
<!--/template-specific JS-->

<!--Embed template-specific JS scripts w/support of CouchTags-->
<cms:if "<cms:exists "footer-files/js/<cms:show k_template_name />.js.html" />" >
    <cms:embed "footer-files/js/<cms:show k_template_name />.js.html" />
<cms:else />
    <cms:embed "footer-files/js/general.js.html" />
</cms:if>
<!--/embed template-specific JS scripts w/support of CouchTags-->


Means that if specific file doesn't exist, then we go with default code. And if it does exist (as in your example with clonable templates), then some specific snippet gets embedded. Kinda 'smart embed'.

With <cms:block /> it would be almost the same, however with unlimited depth. :) I hope to have grasped the idea after reading it two times. Maybe its application will reveal more of itself to me later. :)
Thanks @KK, this is really fantastically helpful.

Just a suggestion (from a noob Couch user):

1 - Perhaps this post (and any similar ones?) should make its way to the 'tags' (and maybe even the 'core concepts' docs pages to make the features more obvious to us inexperienced users?

2 - Any chance of an additional 'addons' page as part of the docs pages? I know that details on addons can be found in the forum in places but it would be great to make these more obvious as part of the learning process.

Again, thanks for producing such a great product (and putting up with us noobs).
It has come to my attention, that this feature is the only number one reason I want to work with CouchCMS 2.0+ and never look back. I have no idea how we lived without it before :D
6 posts Page 1 of 1

Who is online

In total there is 1 user online :: 0 registered, 0 hidden and 1 guest
(based on users active over the past 5 minutes)

Users browsing this forum: No registered users and 1 guest