Vtpl is template engine for php that acts as a binding language between php and html.
Unlike most template engines that work by adding placeholders like {$myvar}
inside html code, Vtpl uses css selectors to specify where to insert php code or variables into the html code.
The goal is to keep the html unchanged for better maintainability for both backend and frontend developers.
With Vtpl when the frontend design or theme of your app is changed you don’t have to change anything, the logic for the html file will be automatically applied for these new html files.
Vtpl ensures proper separations of concerns, the frontend logic is separated from presentation.
Templates are just lists of css selectors and php code and variable names to insert.
This makes it possible to build a CMS like Vvveb where any html page of the CMS can be changed to the last element without affecting the rendering of dynamic content from the database.
Vtpl templates are just a list of key = value
pairs.
On the left you need to specify the CSS selector and on the right the code or variable that must be inserted for the selector.
#css_selector = $php_variable
The code to be inserted can be one of the following
div#id > span.class a = "lorem ipsum"
div#id > span.class a = $variable
div#id > span.class a = <?php if (isset($var)) echo htmlentities($var);?>
Note It's a good practice to adopt a standard for the css selectors from the beginning, for example use data attributes like
[data-product-name]
instead of generic id's or css classes to keep things clean.
/*
from, a special command that copies html from other templates,
useful to include up to date html code into all templates from the currently maintained template for the specified section
a common use is to apply the header from homepage to all other other html templates
*/
div#top > div.header = from(homepage.html|div#top > div.header)
/*
Or you can skip the selector part, in this case the same selector (eg div#top > div.header) is assumed.
*/
div#top > div.header = from(homepage.html)
/*
by default code is inserted into the specified elements without replacing the current elements (innerHTML)
to replace the entire elements with the specified code use outerHTML modifier
*/
div#id > span.class a|innerHTML = "lorem ipsum"
/*
Inserts the code before the element(s)
*/
div#id > span.class a|before = "lorem ipsum"
/*
Inserts the code at the beginning inside the element(s)
*/
div#id > span.class a|prepend = "lorem ipsum"
/*
Inserts the code after the element(s)
*/
div#id > span.class a|after = "lorem ipsum"
/*
Inserts the code at the end in the element(s)
*/
div#id > span.class a|append = "lorem ipsum"
/* Deletes all elements for the specified selector except for the first elements,
usually in mockups front end developers add multiple elements to better show the final page look,
the programmer just needs one element to iterate and fill data*/
div#id > span.class a|deleteAllButFirst
/*
<!-- this -->
<div id="id">
<span class="class">
<a>link 1</a>
<a>link 2</a>
<a>link 3</a>
</span>
</div
**wil result into this**
<div id="id">
<span class="class">
<a>link 1</a>
</span>
</div>
*/
The same as deleteAllButFirst
but will keep the first element (child element) for each set, all parent elements will keep only one child element in contrast with deleteAllButFirst
that works globally.
/*removes the specified elements if the variable ($articles) is false*/
div.articles|if_exists = $articles
/* removes the specified elements*/
div.articles|delete
/*
to inject code into a tag's attribute you must specify the attribute as modifier
*/
div#id > span.class a|href = "www.thewebsite.com"
div#id > span.class a|title = "The website"
You can also use variables directly in the attributes
<img alt="$image.alt" src="">
<a href="$this.product.url">link</a>
<span title="$date_modified"></span>
Includes additional files, useful to separate logic when things get bigger and harder to maintain in one file
import(profile/activity_tab.tpl)
Vtpl can have comments
//single line
/* Or multiple line
comments
*/
With placeholders you can use the value of attributes, attribute names or html node values/text inside the php code. For example if you need to display a specific image size based on an attribute set on the img tag you can use something like
<img src="puppy.jpg" data-size="thumb">
img|src = <?php
echo getScaledImage('@@__src__@@', '@@__data-size__@@');
?>
You can also use them to avoid code repetition for example to replace all variables with a specified prefix with one line.
<h3 data-v-product-title>My product</h3>
<span data-v-product-price>$100</span>
<p data-v-product-desciption>Lorem ipsum</p>
[data-v-product-*] = $product['@@__data-product-(*)__@@']
Then the resulted php code will be
<h3 data-v-product-title><?php echo $product['title'];?></h3>
<span data-v-product-price><?php echo $product['price'];?></span>
<p data-v-product-desciption><?php echo $product['description'];?></p>
Available placeholders:
@@__innerText__@@
- inner html of the node@@__innerText__@@
- inner text of the node@@__my-attribute__@@
- value of my-attribute of current node@@__data-v-plugin-(*)__@@
- (*) matches any charachter for example for data-v-plugin-name it will returnname
@@__data-v-plugin-(.+)__@@
- run any regex inside () and return first match \1 for example for data-v-plugin-name it will returnname
@@__my-*:my-(.*)__@@
- get attribute name that starts with 'my-' and run the regex after ':' used to extract attribute name from current node
With filters you can pass the content of the html tag through a php function. For example to make the first letter uppercase of all title on the page you can use the ucfirst filter like. This is useful if the html is edited by a designer that wants to apply filters like uppercase, friendly dates etc to the html without touching the templates.
<h3 data-post-title data-v-filter-ucfirst>my title</h3>
<span data-post-date_modified data-v-filter-friendly_date>2024-06-01</span>
Even if you have defined php variables for post-title and post-date the filters will be applied if theu are added to html by the user or designer
[data-post-title] = $post['title']
[data-post-date_modified] = $post['date_modified']
//or more simply
[data-post-*] = $post['@@__data-post-(*)__@@']
Sometimes you might need to give the user or designer the possibilty to add more complex options to html, in this case you can use json in html attributes like
<div data-my-product data-v-myjson='{var:{subvar1:val1, subvar2:val2}}'></div>
Then in the template you can use something like @@myjson.var.subvar1@@
will return val1 the configuration passed in the attribute to fill the content of the div with
[data-my-product] = <?php
$parameter1 = '@@myjson.var.subvar1@@;//will be 'val1'
$parameter2 = '@@myjson.var.subvar2@@;//will be 'val2'
$alloptions = @@myjson@@;//this will be a php array with the values from json
echo displayProduct($parameter1, $parameter2);
Process data-filter-*
attributes defined in filters array.
Process attributes with json and transforms them to php arrays ex: data-my-data='{var:"value"}'
Process macro definitions like @@macro Mymacro('var1', 'var2')
Mymacro must be a function defined with name vtplMymacro and must accept two variables like in definition
Process json path, for a node with data-v-myjson='{var:{subvar1:val1, subvar2:val2}}'
@@myjson.var.subvar1@@
will return val1
With macros you can call php functions while the template is being processed, the macro definition is replaced with the returned result from the function. They are used if you want to process some attribute values or content from the html.
You can define your own macros with @@macro myPhpfunction('myparameter')@@;
then you need to define a php function named vtplmyPhpfunction
that will be called when the macro is run.
You can also use the @@__my-attribute-name__@@
placeholder to pass attribute values to the macro function like @@macro myPhpfunction("@@__href__@@")@@
For example to have a if conditional attribute in the html to use it like <div data-v-if="price > 100">Free shipping</div>
you need to process the contents of data-v-if
attribute and show the tag only if the condition is met for this you can use a macro like this:
//if
[data-v-if]|before = <?php
$condition = @@macro IfCondition("@@__data-v-if__@@")@@;
if ($condition) {
?>
[data-v-if]|after = <?php } ?>
And in vtplIfCondition
php function you can just transform price > 100
to php code a boolean conditional like $this->price > 100
in this way the final php code that will be inserted in the compiled html/php will be
//if
<?php
$condition = $this->price > 100;
if ($condition) {
?>
Some macros like if and if class are available by default
data-v-if="condition"
data-v-if-not="condition"
To show/hide an element only if the condition is met
<span data-v-if="product.price > 20">Free shipping for this product</span>
<span data-v-if="product.price > 20">Free shipping for this product</span>
data-v-class-if-class_name="condition"
data-v-class-if-not-class_name="condition"
To add class premium
to a div if price is higher than 30
<div data-v-class-if-premium="product.price > 30"></div>
<div data-v-class-if-not-standard="this.product.price > 30"></div>
Will result in
<div class="premium"></div>
Sometimes you need to pass data to javascript for this cases you can use a simple {$variable}
syntax
<?php
$this->date_created = 'Today';
$this->my_object = ['date_created' => '10:00', 'date_modified' => '12:30'];
?>
<body>
<div></div>
<script>
let date_created = '{$this.date_created}';//the output will be text/string and needs quotes
let my_object = {$this.my_object};//the php array will be converted to json object
</script>
</body>
When you need to quickly insert some data in a node attribute at some position to avoid concatenating and embeding existing attribute text in the template.
<?php
$this->date_created = '12:30';
?>
<a title="Created at {$this.date_created}">Date<a>
If a global VTPL_DEBUG
constant is true then a console will show on the bottom of the page with all the insert operations made on the html.
When in debug mode, you will have a list of all template injections made on the html to visualize.