-
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
-
phpQuery - a jQuery port to PHP
Introduction
phpQuery is PHP-port of jQuery - well known and great web2.0 JS library
It's something defferent than jQPie, which is form of JS code generator and server-client layer.
For example You can do something like this:
print _('file.htm') ->find('body div.cls1.cls2 ul > li:first') ->parent() ->prepend('<li>my new first LI</li>') ->parents('.myClass') ->remove() ->end() ->appendTo('body') ->parents('html') ->html();Code above will find first LI inside specific UL, then move pointer into it's parent (UL), then prepend (add at the begining) new LI, then pointer will move to parent element with class .myClass, which will be removed, and pointer will go back to UL (with end() method), and then UL will be appended to BODY (moved, not copied). Atfer all this operations parent with tag HTML will be searched and it's content will be returned to print statement.
phpQuery acts almost like jQuery - it returns new instance on certain methods and allows to revert stack.
It works on DOM Extension and is designed for PHP5 only.
There is almost no docs yet, so please refer to jQuery's one (DOM section).
Difference against jQuery
phpQuery differs in some cases from jQuery:
- Interation
- Callbacks
- No DOM nodes
- In some method names (PHP reserved words)
- PHP specific addons
- Other addons
Interation
phpQuery makes use of PHP's SPL Interator interafce, so You can do:
foreach(_('ul>*') as $_li) { $_li->prepend('new beginning of every LI'); }Callbacks
PHP doesn't have closures, but You can still use callbacks - direct or created with create_function() like so:
function imTheCallback($_node){ $_node->html("i'm changed content"); } class imTheClass { static function imTheStaticCallback($_node){ $_node->html("i'm changed content v2"); } function imTheCallbackToo($_node){ $_node->html("i'm changed content v3"); } } $class = new imTheClass; _('ul>*') .each('imTheCallback') .each(array('imTheClass', 'imTheStaticCallback')) .each(array($class, 'imTheCallbackToo')) .each(create_function('$_node', ' $_node->html("i\'m changed content v4");' )); }No DOM nodes
Every node passed to callback or inside iteration is phpQuery object, not a DOM node. Also there isn't a get() method.
Method names
There are several methods in jQuery's interface with names which couldn't be used as PHP class method or was changed to preserve consistent naming convention.
All those methods have been prefixed with _underscore and here's the list:
- _clone
- _next
- _prev
- _empty
PHP specific addons
There are couple of PHP specific addons in phpQuery for easier developement:
- appendPHP($code) - equals to append(<?php $code ?>)
- prependPHP($code) - equals to prepend(<?php $code ?>)
- beforePHP($code) - equals to before(<?php $code ?>)
- afterPHP($code) - equals to after(<?php $code ?>)
- attrPHP($attr, $code) - equals to attr($attr, <?php $code ?>)
- php($code) - equals to html(<?php $code ?>)
- phpPrint($code) - equals to html(<?php print $code ?>)
- phpMeta($selector, $code) - equals to find($selector)->php($code)->end()
- __toString() - equals to htmlWithTag()
Other addons
There is/will be several methods not present in standard jQuery, which i use (with jQuery) in my projects. More about this later.
Developement status
Actually phpQuery seems to be quite stable and is main part of plainTemplates lib, which powers this blog.
Although there are couple of things to be done:
- Dedicated docs (copy jQuery's one, add PHP specific, generate phpdoc)
- Missing methods (css, val)
Download and links
Here are the link which could be helpfull when dealing with phpQuery:
Comments: 0