plainTemplates - CSS driven templating engine
Introduction
plainTemplates is a PHP and JS library that aims to divide template into 2 separate layers:
- CSS selectors driven data attaching mechanism
- Plain HTML
It's simply creates your templates from plain HTML files on-the-fly, filling it with data and fetching parts you like.
It's basen on phpQuery - a jQuery port to PHP.
Pros
- True separation of data and visual layer
- HTML can be filled with example data
- High reusability (several templates from one HTML, one template from several HTMLs)
- Framework independed template organisation
- Transparent refreshes (including PHP code changes!)
- phpQuery integration
- No need to lear new, template-only language
- Saves a lot of time ;)
Cons
- You have to know CSS selectors (XPath will also help)
- In most cases you have to know jQuery/phpQuery
- You have to format data outside template (eg date).
Here is simple example, which inserts data from $data array into template in template.htm:
// get the class
require_once('../../plainTemplates/plainTemplates.php');
// always end dir with a slash
plainTemplates::$cacheDir = '../../plainTemplates/cache/';
$data = array(
array(
'title' => 'News 1 title',
'body' => 'News 1 body',
),
array(
'title' => 'News 2 title',
'body' => 'News 2 body',
),
array(
'title' => 'News 3',
'body' => 'News 3 body',
),
);
// include to fully preserve scope
include(
plainTemplates::createTemplate(
// first the template source
'template.htm',
// second the var info
array(
// var name in scope
'$data',
// and var value
$data,
// finally the selectors (of course there are defaults, that is UL and LI)
array(
'container' => '.someclass ul',
'row' => 'li'
)
)
)
);
Here is template.htm itself:
<div class='someclass'>
<p>UL below will be container, and every LI will be searched agains classes with field names.</p>
<ul>
<li>
<p>Only tags with field classes are replaced.</p>
<p class='title'>This will be replaces by the title</p>
<p class='body'>And this by the body...</p>
</li>
<li>
Only first row will be in the result and this wont be considered by any chance... so You can use as many row examples as You want.
</li>
</ul>
</div>
Now the result's source:
<ul><?php foreach( $data as $dataRow ): ?>
<li>
<p>Only tags with field classes are replaced.</p>
<p class="title"><?php print $dataRow['title']; ?></p>
<p class="body"><?php print $dataRow['body']; ?></p>
</li><?php endforeach; ?></ul>
Those are the basics, it can do a lot more, but i think its good that it can do simple things in a simple way...
Intermediate examples
EXAMPLE: Nested data
In this example you will learn about nested data, which is very common thing in eg CakePHP. So let's get started!
Code (examples/nested-data/code.php)
$data = array(
array(
'Post' => array(
'title' => 'Im the title',
'body' => 'Im the body',
),
'Comment' => array(
array(
'author' => 'Im the 1st comment of 1st post',
'body' => "This field has same name as post's one",
),
array(
'author' => 'Im the 2nd comment of 1st post',
'body' => 'So am i',
),
),
),
array(
'Post' => array(
'title' => 'Im the post without comments',
'body' => 'His right',
),
'Comment' => array(),
),
);
// include to fully preserve scope
include(
plainTemplates::createTemplate(
// first the template source
'template.htm',
// second the var info
array(
// var name in scope
'$data',
// and var value
$data,
// finally the selectors (of course there are defaults, that is UL and LI)
array(
// selectors for 'Post' row
'container' => '.someclass > ul',
'row' => '> li',
// selectors for 'Comment'. we have to group them inside 'Comment' index because it's another data (list), not row
'Comment' => array(
'container' => '.someclass > ul',
),
)
)
)
);
Template (examples/nested-data/template.htm)
<div class='someclass'>
<p>UL below will be container, and every LI will be searched agains classes with field names.</p>
<ul>
<li>
<p>In nested row, class names must have parent index prepended.</p>
<p class='Post-title'>This will be replaces by the title</p>
<p class='Post-body'>And this by the body...</p>
<div>
<ul>
<li>
<p>Here is different situation - we have nested data (list), with container etc, so we DONT prepend index to class name.</p>
<p class='author'>Comment's author field</p>
<p class='body'>And the body</p>
</li>
</ul>
</div>
</li>
<li>
Only first row will be in the result and this wont be considered by any chance... so You can use as many row examples as You want.
</li>
</ul>
</div>
Result::
<ul><?php foreach( $data as $dataRow ): ?>
<li>
<p>In nested data class names must have parent index prepended.</p>
<p class="Post-title"><?php print $dataRow['Post']['title']; ?></p>
<p class="Post-body"><?php print $dataRow['Post']['body']; ?></p>
<div>
<ul><?php foreach( $dataRow['Comment'] as $dataRowComment ): ?>
<li>
<p class="author"><?php print $dataRowComment['author']; ?></p>
<p class="body"><?php print $dataRowComment['body']; ?></p>
</li><?php endforeach; ?></ul></div>
</li><?php endforeach; ?></ul>
EXAMPLE: Row insertion
Previous examples was about inserting a list of data. Now you will learn about inserting only one row, eg to show this post.
Code (examples/row/code.php)
$data = array(
'Post' => array(
'title' => 'Im the title',
'body' => 'Im the body',
),
'Comment' => array(
array(
'author' => 'Im the 1st comment of 1st post',
'body' => "This field has same name as post's one",
),
array(
'author' => 'Im the 2nd comment of 1st post',
'body' => 'So am i',
),
)
);
// include to fully preserve scope
include(
plainTemplates::createTemplate(
// first the template source
'template.htm',
// second the var info
array(
// var name in scope
'$data',
// and var value
$data,
// finally the selectors (of course there are defaults, that is UL and LI)
array(
// empty container means that we dont want a loop
'container' => '',
// you can't use plain 'li', because all elements selected by this seletor except first one will be removed
// in this case LI for comment row
// there are other ways to write this selector, but this is the simplest
'row' => '.someclass > ul > li',
// this hasn't changed
'Comment' => array(
'container' => '.someclass > ul ul',
),
)
)
)
);
Template (examples/row/template.htm) is the same as previous one.
Result
<p>In nested row, class names must have parent index prepended.</p>
<p class="Post-title"><?php print $data['Post']['title']; ?></p>
<p class="Post-body"><?php print $data['Post']['body']; ?></p>
<div>
<ul><?php foreach( $data['Comment'] as $dataComment ): ?>
<li>
<p>Here is different situation - we have nested data (list), with container etc, so we DONT prepend index to class name.</p>
<p class="author"><?php print $dataComment['author']; ?></p>
<p class="body"><?php print $dataComment['body']; ?></p>
</li><?php endforeach; ?></ul></div>
EXAMPLE: Callback
In this example i will introduce callback function and it's often use to hidding uneeded content (comments listing wrapper in this case).
Callback is a function/method which gets phpQuery object at the end of template creation, but before saving file, so it can affect result.
Code (examples/callback/code.php), $data is same as in Nested data example
function callbackName($_) {
// find the only div in template
$_->find('div')
// insert condition before div
->beforePHP('if ( $dataRow[\'Comment\'] ):')
// close condition after div
->afterPHP('endif;');
}
// include to fully preserve scope
include(
plainTemplates::createTemplate(
// first the template source
'template.htm',
// second the var info
array(
// var name in scope
'$data',
// and var value
$data,
// finally the selectors (of course there are defaults, that is UL and LI)
array(
'container' => 'ul:first',
'row' => '> li',
'Comment' => array(
'container' => 'ul ul',
),
)
),
// now the new thing - callback
// callback function will have phpQuery object as only arg
'callbackName'
)
);
Template (examples/callback/template.htm)
<p>UL below will be container, and every LI will be searched agains classes with field names.</p>
<ul>
<li>
<p>In nested row, class names must have parent index prepended.</p>
<p class='Post-title'>This will be replaces by the title</p>
<p class='Post-body'>And this by the body...</p>
<div>
<h1>Comments</h1>
<ul>
<li>
<p>Here is different situation - we have nested data (list), with container etc, so we DONT prepend index to class name.</p>
<p class='author'>Comment's author field</p>
<p class='body'>And the body</p>
</li>
</ul>
</div>
</li>
<li>
Only first row will be in the result and this wont be considered by any chance... so You can use as many row examples as You want.
</li>
</ul>
Result
<ul><?php foreach( $data as $dataRow ): ?>
<li>
<p>In nested row, class names must have parent index prepended.</p>
<p class="Post-title"><?php print $dataRow['Post']['title']; ?></p>
<p class="Post-body"><?php print $dataRow['Post']['body']; ?></p>
<?php if ( $dataRow['Comment'] ): ?><div>
<h1>Comments</h1>
<ul><?php foreach( $dataRow['Comment'] as $dataRowComment ): ?>
<li>
<p>Here is different situation - we have nested data (list), with container etc, so we DONT prepend index to class name.</p>
<p class="author"><?php print $dataRowComment['author']; ?></p>
<p class="body"><?php print $dataRowComment['body']; ?></p>
</li><?php endforeach; ?></ul></div><?php endif; ?>
</li><?php endforeach; ?></ul>
Advanced examples
I will post some advanced examples here near future, so stay tuned and for now you can read generated phpdocs or those in the source code.
Download and links
Comments: 0
Add Comment