- Introduction
- Usage
- Configuration for Bow
- Displaying data
- The %verbatim directive
- The
%ifdirectives - The
%loop/%for/%whiledirectives - Conditional Classes and Styles
- Include subviews
- Raw PHP
- Flash session
- Service injection
- Authentication Directives
- Environment Guidelines
- Field CSRF
- Method Field
- Inheritance with %extends, %block and %inject
- The
%macrodirective
Tintin is a PHP template that is meant to be very simple and extensible. It can be used in any PHP project.
To install the package it will be better to use composer which is a php package manager.
composer require bowphp/tintinYou can use the package simply, like this. But except that this way of doing things does not allow you to exploit the inheritance system optimally. Use this way of doing things, only if you want to test the package or for small applications.
require 'vendor/autoload.php';
$tintin = new Tintin\Tintin;
echo $tintin->render('Hello, world {{ strtoupper($name) }}', ['name' => 'tintin']);
// -> Hello, world TINTINTo use the package properly, you should follow the following configuration:
require 'vendor/autoload.php';
$loader = new Tintin\Loader\Filesystem([
'path' => '/path/to/the/views/source',
'extension' => 'tintin.php',
'cache' => '/path/to/the/cache/directory'
]);
$tintin = new Tintin\Tintin($loader);| parameter | Description |
|---|---|
| php | The path to the views folder of your application |
| extension | the extension of the template files. By default, the value is tintin.php |
| cache | The cache folder. This is where tintin will create the cache. If it is not set, tintin will cache the compiled files in the temporary directory of php. |
// Configuration done beforehand
$tintin = new Tintin\Tintin($loader);
$tintin->render('filename', ['name' => 'data']);
// Or
$tintin->render('folder/filename', ['name' => 'data']);
// Or
$tintin->render('folder.filename', ['name' => 'data']);Note that the source of the files is always the path to
path.
To allow Bow to use Tintin as the default template engine, you will need to do some small configuration.
Add this configuration in the app/Kernel.php file:
public function configurations() {
return [
...
\Tintin\Bow\TintinConfiguration::class,
...
];
}And again in the views configuration file located in config/view.php.
Bow framework currently uses tintin as the default template
return [
// Set the engine to use
'engine' => 'tintin',
// File extension
'extension' => '.tintin.php'
];And that's it, now your default template engine is tintin 👍
You can display the content of the name variable in the following way:
Hello, {{ $name }}.Of course, you are not limited to displaying the contents of variables passed to the view. You can also echo the results of any PHP function. In fact, you can insert any PHP code into an echo Tintin statement:
Hello, {{ strtoupper($name) }}.Tintin
{{ }}statements are automatically sent through the PHPhtmlspecialcharsfunction to prevent XSS attacks.
By default, Tintin {{ }} statements are automatically sent through the PHP htmlspecialchars function to prevent XSS attacks. If you do not want your data to be protected, you can use the following syntax:
Hello, {{{ $name }}}.This clause {## ##} allows you to add a comment to your tintin code.
{## This comment will not be present in the rendered HTML ##}If you display JavaScript variables in a large part of your template, you can wrap the HTML code in the %verbatim directive so that you don't have to prefix each Tintin echo statement with a % symbol:
%verbatim
<div class="container">
Hello, {{ name }}.
</div>
%endverbatimThese are the clauses that allow you to establish conditional branching as in most programming languages.
You can build if statements using the %if, %elseif, %elif, %else, and %endif directives. These directives work the same way as their PHP counterparts:
%if ($name == 'tintin')
{{ $name }}
%elseif ($name == 'template')
{{ $name }}
%else
{{ $name }}
%endifYou can use
%elifinstead of%elseif.
A small specificity, the %unless allows to make a condition inverse to the %if.
To make it simple, here is an example:
%unless ($user->isAdmin())
// do something else
$endunlessIn addition to the conditional directives already discussed, the %isset, %empty, %notempty directives can be used as convenient shortcuts for their respective PHP functions:
%isset($records)
// $records is defined and is not null...
%endisset
%empty($records)
// $records is "empty"...
%endempty
%notempty($records)
// $records is not "empty"...
%endnotemptyYou can add
%elseto perform an opposite action
Often you may need to make lists or repetitions on elements. For example, display all users of your platform.
This clause does exactly the action of foreach.
%loop($names as $name)
Hello {{ $name }}
%endloopAnything PHP's foreach accepts works in the head, such as key/value pairs or list destructuring:
%loop($users as $id => $user)
{{ $id }}: {{ $user->name }}
%endloopThe whole loop can also sit on a single line:
<ul>%loop($names as $name) <li>{{ $name }}</li> %endloop</ul>This clause can also be coupled with any other clause such as %if.
A quick example.
%loop($names as $name)
%if($name == 'tintin')
Hello {{ $name }}
%stop
%endif
%endloopYou may have noticed the %stop it allows to stop the execution of the loop. There is also its spouse the %jump, it on the other hand allows to stop the execution at its level and to launch the execution of the next turn of the loop.
Often the developer is led to make conditions to stop the loop %loop like this:
%loop($names as $name)
%if($name == 'tintin')
%stop
// Or
%jump
%endif
%endloopWith syntactic sugars, we can reduce the code like this:
%loop($names as $name)
%stop($name == 'tintin')
// Or
%jump($name == 'tintin')
%endloop%stop compiles to break and %jump to continue. Both work in %loop, %for and %while, with or without a condition, and the condition may contain any PHP expression, including nested parentheses.
When the list you iterate over may be empty, you often end up wrapping the %loop in an %if. A bare %empty inside the loop lets you declare the content to render when the loop body never ran:
%loop($users as $user)
Hello {{ $user->name }}
%empty
No user yet.
%endloopThe %empty branch is rendered only if $users is empty. It works with nested loops, each %loop keeps its own %empty. This branch is only available with %loop.
Note:
%emptywithout parentheses is the loop branch.%empty($value)with parentheses stays the conditional helper described above and can still be used inside a loop.
%for does exactly the action of for.
%for($i = 0; $i < 10; $i++)
{{ $i }}
%endfor%while does exactly the action of while.
%while($name != 'tintin')
{{ $name = next($names) }}
%endwhile%jump and %stop are available in both, exactly as in %loop:
%for($i = 0; $i < 10; $i++)
%jump($i % 2 == 0)
%stop($i > 7)
{{ $i }}
%endforNote: the
%emptybranch is only available with%loop.%forand%whilehave no equivalent.
The %class directive conditionally compiles a CSS class string. The directive accepts an array of classes where the array key contains the class(es) you want to add, while the value is a boolean expression. If the array element has a numeric key, it will always be included in the rendered class list:
%php
$isActive = false;
$hasError = true;
%endphp
<span %class([
'p-4',
'font-bold' => $isActive,
'text-gray-500' => ! $isActive,
'bg-red' => $hasError,
])></span>
<span class="p-4 text-gray-500 bg-red"></span>Similarly, the %style directive can be used to conditionally add inline CSS styles to an HTML element:
%php
$isActive = true;
%endphp
<span %style([
'background-color: red',
'font-weight: bold' => $isActive,
])></span>
<span style="background-color: red; font-weight: bold;"></span>Often when you develop your code, you are led to subdivide the views of your application to be more flexible and write less code.
%include allows you to include another template file in another.
<div id="container">
%include('filename', %data)
</div>If you try to include a view that does not exist, Tintin will throw an error. If you want to include a view that may or may not be present, you should use the %includeIf directive:
%includeIf("filename", ["name" => "Tintin"])If you want to %include a view if a given boolean expression evaluates to true or false, you can use the %includeWhen and %includeUnless directives:
%includeWhen($user->isAdmin(), "include-file-name", ["name" => "Tintin"])In some situations, it is useful to embed PHP code in your views. You can use the Tintin %php or %raw directive to execute a simple block of PHP in your template:
%php
$counter = 1;
%endphp
%raw
$counter = 1;
%endrawIn case you want to display a flash message directly on view you can use %flash. And to check if a flash message exists %hasflash and %endhasflash :
%hasflash("error")
<div class="alert alert-danger">
%flash("error")
</div>
%endhasflashThe %service directive can be used to retrieve a service from the container. The first argument passed to %service is the name of the variable in which the service will be placed, while the second argument is the name of the class or interface of the service you want to resolve:
%service('user_service', 'App\Services\UserService')
<div>
%loop($user_service->all() as $user)
<p>{{ $user->name }}</p>
%endloop
</div>The %auth and %guest directives can be used to quickly determine whether the current user is authenticated or a guest:
%auth
// The user is authenticated...
%endauth
%guest
// The user is not authenticated...
%endguestIf needed, you can specify the authentication guard that should be checked when using the directives %auth and %guest:
%auth('admin')
// The user is authenticated...
%endauth
%guest('admin')
// The user is not authenticated...
%endguestYou can check if the application is running in the production environment using the %production directive:
%production
// Production specific content...
%endproductionOr, you can determine if the application is running in a specific environment using the %env directive:
%env('staging')
// The application is running in "staging"...
%endenv
%env(['staging', 'production'])
// The application is running in "staging" or "production"...
%endenvWhenever you define an HTML form in your application, you must include a hidden CSRF token field in the form so that the CSRF protection middleware can validate the request. You can use the Tintin %csrf directive to generate the token field:
<form method="POST" action="/profile">
%csrf
</form>Since HTML forms cannot perform PUT, PATCH or DELETE requests, you will need to add a hidden _method field to spoof these HTTP verbs. Tintin's %method directive can create this field for you:
<form action="/foo/bar" method="POST">
%method('PUT')
</form>Like any good template system tintin supports sharing code between files. This makes your code flexible and maintainable.
Consider the following tintin code:
# the `layout.tintin.php` file
<!DOCTYPE html>
<html>
<head>
<title>Hello, world</title>
</head>
<body>
<h1>Page header</h1>
<div>
%inject('content')
</div>
<p>Page footer</p>
</body>
</html>
And also, we have another file that inherits the code from the layout.tintin.php file
# the file is called `content.tintin.php`
%extends('layout')
%block('content')
<p>This is the page content</p>
%endblockThe content.tintin.php file will inherit the code from layout.tintin.php and if you notice, in the file layout.tintin.php we have the clause %inject which has as parameter the name of the %block of content.tintin.php which is content. Which means that the content of the %block content will be replaced by %inject. Which will give at the end this:
<!DOCTYPE html>
<html>
<head>
<title>Hello, world</title>
</head>
<body>
<h1>Page header</h1>
<div>
<p>This is the page content</p>
</div>
<p>Page footer</p>
</body>
</html>Tintin can be extended with its custom directive system, to do this use the directive method
Let's create directives to manage a form:
$tintin->directive('input', function (string $type, string $name, ?string $value) {
return '<input type="'.$type.'" name="'.$name.'" value="'.$value.'" />';
});
$tintin->directive('button', function (string $type, string $label) {
return '<button type="'.$type.'">'.$label.'"</button>';
});
$tintin->directive('form', function (string $action, string $method, string $enctype = "multipart/form-data") {
return '<form action="'.$action.'" method="'.$method.'" enctype="'.$enctype.'">';
});
$tintin->directive('endform', function () {
return '</form>';
});To use these directives, nothing could be simpler. Write the name of the directive preceded by %. Then if this directive takes parameters, launch the directive as you launch the functions in your program.
<div class="container">
%form("/posts", "post", "multipart/form-data")
%input("text", "name")
%button('submit', 'Add')
%endform
</div>Compilation is done as usual, for more information on compilation.
echo $tintin->render('form');Output after compilation:
<form action="/posts" method="post" enctype="multipart/form-data">
<input type="text" name="name" value="" />
<button type="submit">Add</button>
</form>In case you are using the Tintin configuration for Bow Framework.
Change your configuration in the ApplicationController::class in the app/Configurations folder.
namespace App\Configurations;
use Bow\Configuration\Loader as Config;
class ApplicationConfiguration extends Configuration
{
/**
* Launch configuration
*
* @param Config $config
* @return void
*/
public function create(Config $config): void
{
$tintin = app('view')->getEngine();
$tintin->directive('super', function () {
return "Super !";
});
}
}Now the %super directive is available and you can use it.
return $tintin->render('%super');
// => Super !Often, you will be led to use or reuse a template block to optimize the writing of your application. So macros are there for that.
Macros must be defined in a separate file.
To use the %macro you must pass first the macro name and then the macro parameters.
Consider the file user-macro.tintin.php.
%macro('users', array $users)
%loop($users as $user)
<div>{{ $user }}</div>
%endloop
%endmacroTo use the macro you must import it into another file with %import. We will call the file app.tintin.php containing the following template:
%import('user-macro')
%extends('layout')
%block('content')
<div class="container">
{{ users($users) }}
</div>
%endblockFor the compilation we will pass the following list of users:
$users = ["franck", "lucien", "brice"];
$tintin->render('app', compact('users'));After compiling the file
<div>franck</div>
<div>lucien</div>
<div>brice</div>