-
Notifications
You must be signed in to change notification settings - Fork 0
Template Inheritance Helper
CONFIGURATION:
If you are using Code Igniter, you shouldn't have to do anything.
If not, you'll have to define a 'TI_VIEWS_DIR' constant, like
define ('TI_VIEWS_DIR', 'views/');
which should be a path where PHP can find your view files. You can make it relative to the current PHP working dir or absolute, it shouldn't matter.
If you don't do this, you'll have add the path to your files to your extend() calls.
Then just include this file somehow. Cod Igniter users can drop it into their 'helpers' - dir and make sure that it gets loaded (via autoload or manually).
QUICK TUTORIAL:
Imagine you have a website with the same basic structure in every page. Something like this:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title> Dan's Music Store </title> <link rel="stylesheet" href="css/base.css" type="text/css" media="all" /> </head> <body id="body">
Then you might have different subpages which actually use the same basic structure.
Now you can use PHP to include separate files for every area of that template, but I always felt like this was a suboptimal solution.
So the idea is to put some kind of markers into the base template which can be extended from child templates. We'll call it "main_template.php". An example could look like this:
main_template.php:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>
<? start_block_marker('title') ?>
Dan's Music Store
<? end_block_marker() ?>
</title>
<? start_block_marker('extra_head') ?>
<link rel="stylesheet" href="css/base.css" type="text/css" media="all" />
<? end_block_marker() ?>
</head> <body id="body">
<? start_block_marker('content') ?>
<h1>Welcome to the Dan's Music Store</h1>
<p>Instruments from all over the world</p>
<? end_block_marker() ?>
</div> <!-- container -->
</body> </html>
If you include just this, essentially you would get the same output as in the first example, because no special block content has been assigned yet.
To use this file as a base template for different content, you have to use the extend() function and start overriding the blocks of the base template. Let's call this one "guitars.php". It will be a section template which includes some extra content for the guitar section of our music store.
guitars.php:
<? extend('main_template.php') ?>
<? startblock('title') ?>
<?= get_extended_block() ?>
- Guitars
<? endblock() ?>
<? startblock('extra_head') ?>
<?= get_extended_block() ?>
<link rel="stylesheet" href="css/guitars.css" type="text/css" media="all" />
<? endblock() ?>
<? startblock('content') ?>
<h2>Look around!</h2>
<p>Such a fine selection of Guitars!</p>
<? endblock() ?>
<? end_extend() ?>
With extend('filename'), you tell this template which base template it should extend. You need to wrap this file up with a call to <? end_extend() ?> to make the magic work.
You can call get_extended_block() to inherit / receive the content of the parent block, which is useful for adding additional data to the base template.
In this example it's used to add a second part to the <title> - tag and to add an additional stylesheet.
Now we would include "guitars.php" instead of "main_template.php".
The output would look like this*:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>
Dan's Music Store
- Guitars
</title>
<link rel="stylesheet" href="css/base.css" type="text/css" media="all" />
<link rel="stylesheet" href="css/guitars.css" type="text/css" media="all" />
</head>
<body id="body">
<h2>Look around!</h2>
<p>Such a fine selection of Guitars!</p>
</div> <!-- container -->
</body> </html>
*I fixed the indentation a little
So now we have the extra content for the title and the head. The body has been overwritten by the content of "guitars.php".
Then we need a page where we display the specific guitars. We'll create a file 'destroyer_guitar.php'. It will extend the "guitar.php" - file. Of course the contents of this file will probably be dynamic in your case and not only a file for a specific guitar, but this is an example, mkaaaaay?
destroyer_guitar.php:
<? extend('guitars.php') ?>
<? startblock('title'); ?>
<? get_extended_block() ?>
- Destroyer ZX80
<? endblock() ?>
<? startblock('content') ?>
<h1>Destroyer ZX80</h1>
<p>A most excellent heavy metal Axe.</p>
<p>Available in the following sizes:</p>
<ul>
<li>Small</li>
<li>Large</li>
<li>Troll</li>
</ul>
<? endblock() ?>
<? end_extend() ?>
So here we once more replace the content with something different. The 'content' - data will overwrite the content from "guitars.php" and "main_content.php". If we wanted to preserve it, we could have left the content block out or could have inserted <? get_extended_block() ?> somewhere. Then the content from "guitars.php" would appear in that spot in the main template.
We did this for the 'title' - block, which inherits the data from the base templates.
If we would include "destroyer_guitar.php" now instead of "main_template.php" or "guitars.php", the output would look like this:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>
Dan's Music Store
- Guitars
- Destroyer ZX80
</title>
<link rel="stylesheet" href="css/base.css" type="text/css" media="all" />
<link rel="stylesheet" href="css/guitars.css" type="text/css" media="all" />
</head> <body id="body">
<div id="container">
<h1>Destroyer ZX80</h1>
<p>A most excellent heavy metal Axe.</p>
<p>Available in the following sizes:</p>
<ul>
<li>Small</li>
<li>Large</li>
<li>Troll</li>
</ul>
</div> <!-- container -->
</body> </html>
... now isn't that just beautiful? :)
CAUTION:
EVERY block within the inheritance hierarchy will be executed ONCE - wether its contents will be used or not.
That means that if some base template block contains anything, it will always be executed, whether its output will be used or not.
If this calculation would be too expensive, there is a function you can call to check if the current block's content is required or if it only will be overridden by the child templates anyways. Call block_rendering_neccessary() to find out if the block you're currently crafting will be rendered to avoid unneccessary computation.
like this:
<? startblock('weather') ?> <? if (block_rendering_neccessary()): ?> <?= simulate_global_warming_effects() ?> <? endif; ?> <? endblock() ?>
You could also check django's template inheritance documentation for another example. Django's template inheritance was my inspiration - actually I pretty much copied its functionality. I hope they don't mind.
http://www.djangoproject.com/documentation/templates/#template-inheritance
That's it for now. If you have questions, ideas or problems, please write me at daniel AT dornhardt.com.