Using views
Introductionβ
Views contain the HTML served by your application and separate your controller/application logic from your presentation logic.
Configurationβ
BowPHP implements 2 template engines and the default template is Tintin.
The view configuration is located in the view.php file inside the config/ folder.
Specify the name of the template to use with the engine configuration option. This option can take the following values: tintin, twig and php. By default Bow uses tintin.
Note that if you set php, views will be rendered without any template engine.
You can also change the template extension by modifying the value of the extension entry. You will also notice that views are stored in the templates directory by default.
Creating a viewβ
A simple view might look like this:
<!-- View stored in templates/greeting.tintin.php -->
<html>
<body>
<h1>Hello, {{ $name }}</h1>
</body>
</html>
After editing and saving your view in templates/greeting.tintin.php, you can now send it to users with the view helper like this:
$app->get('/', function() {
return view('greeting', ['name' => 'Tintin']);
});
Usageβ
Example usage with the static View::parse class:
View::parse(string $view, array $data = []): View
| Parameter | Type | Description |
|---|---|---|
$view | string | The name of the view, without extension, relative to the views folder |
$data | array | The data passed to the template |
use Bow\View\View;
echo View::parse('view-name-without-extension');
To pass variables to the view:
use Bow\View\View;
echo View::parse('view-name-without-extension', ['name' => 'bow']);
You can use the view helper, which is used in the same way.
To set an HTTP status code, use
response()->render('view', $data, $code) instead (see the
HTTP responses documentation). View::parse() does not take a code.
With the following view:
<!-- View stored in templates/greeting.tintin.php -->
<html>
<body>
<h1>Hello, {{ $name }}</h1>
</body>
</html>
Example in a controller:
namespace App\Controllers;
use App\Controllers\Controller;
class HomeController extends Controller
{
/**
* Show hello page
*
* @return mixed
*/
public function show()
{
return view('greeting', ['name' => 'Bowphp']);
// Or
return $this->render('greeting', ['name' => "Bowphp"]);
// Or
return response()->render('greeting', ['name' => "Bowphp"]);
}
}
Introduction to Tintinβ
Tintin is a PHP template engine designed to be very simple and extensible. It can be used in any PHP project. By default, Tintin is the template engine used by BowPHP.
Hello, {{ $name }}.
Of course, you are not limited to displaying the contents of the variables passed to the view. You can also echo the results of any PHP function. In fact, you can put any PHP code you want inside a Tintin echo statement:
Hello, {{ strtoupper($name) }}.
Tintin
{{ }}statements are automatically sent through the PHPhtmlspecialcharsfunction to prevent XSS attacks.
Displaying unescaped dataβ
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 escaped, you can use the following syntax:
Hello, {{{ $name }}}.
Adding a commentβ
The {## ##} clause lets you add a comment to your tintin code.
{## This comment will not be present in the rendered HTML ##}
The %verbatim directiveβ
If you are displaying JavaScript variables in a large portion of your template, you can wrap the HTML in the %verbatim directive so that you do not have to prefix each Tintin echo statement with a % symbol:
%verbatim
<div class="container">
Hello, {{ name }}.
</div>
%endverbatim
The %if directivesβ
These are the clauses that allow you to set up 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 }}
%endif
You can use
%elifinstead of%elseif.
One small feature: %unless lets you create a condition that is the inverse of %if.
To keep it simple, here is an example:
%unless ($user->isAdmin())
// do something else
%endunless
In addition to the conditional directives already discussed, the %isset, %empty and %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"...
%endnotempty
You can add
%elseto perform an opposite action.
The %loop / %for / %while directivesβ
You will often need to create lists or repeat actions over a set of elements. For example, displaying all the users of your platform.
Using %loopβ
This clause does exactly what foreach does.
%loop($names as $name)
Hello {{ $name }}
%endloop
This clause can also be combined with any other clause, such as %if.
A quick example.
%loop($names as $name)
%if($name == 'tintin')
Hello {{ $name }}
%stop
%endif
%endloop
You may have noticed the %stop, which stops the loop execution. There is also its counterpart %jump, which instead stops execution at that point and triggers the next iteration of the loop.
The %jump and %stop syntactic sugarβ
Developers often need to set up stopping conditions for the %loop like this:
%loop($names as $name)
%if($name == 'tintin')
%stop
// Or
%jump
%endif
%endloop
With the syntactic sugar, you can reduce the code like this:
%loop($names as $name)
%stop($name == 'tintin')
// Or
%jump($name == 'tintin')
%endloop
Using %for and %whileβ
This clause does exactly what for does.
%for($i = 0; $i < 10; $i++)
// ..
%endfor
This clause does exactly what while does.
%while($name != 'tintin')
// ..
%endwhile
Conditional classes and stylesβ
The %class directive conditionally compiles a CSS class string. The directive accepts an array of classes where the array key contains the class or classes 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>
Including sub-viewsβ
While developing your code, you will often need to split your application's views to be more flexible and write less code.
%include lets you include another template file within 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 when 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"])
Raw PHPβ
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 block of plain PHP in your template:
%php
$counter = 1;
%endphp
%raw
$counter = 1;
%endraw
Flash sessionβ
If you want to display a flash message directly in a view, you can use %flash. And to check whether a flash message exists, use %hasflash and %endhasflash:
%hasflash("error")
<div class="alert alert-danger">
%flash("error")
</div>
%endhasflash
Service injectionβ
The %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 class or interface name 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>
Authentication directivesβ
The %auth and %guest directives can be used to quickly determine whether the current user is authenticated or is a guest:
%auth
// The user is authenticated...
%endauth
%guest
// The user is not authenticated...
%endguest
If necessary, you can specify the authentication guard that should be checked when using the %auth and %guest directives:
%auth('admin')
// The user is authenticated...
%endauth
%guest('admin')
// The user is not authenticated...
%endguest
Environment Guidelinesβ
You can check whether the application is running in the production environment using the %production directive:
%production
// Production specific content...
%endproduction
Or, you can determine whether 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"...
%endenv
CSRF fieldβ
Whenever you define an HTML form in your application, you should 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>
Method fieldβ
Since HTML forms cannot make PUT, PATCH or DELETE requests, you will need to add a hidden _method field to spoof these HTTP verbs. The Tintin %method directive can create this field for you:
<form action="/foo/bar" method="POST">
%method('PUT')
</form>
Inheritance with %extends, %block and %injectβ
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 we also have another file that inherits the code from the layout.tintin.php file.
# the file is named `content.tintin.php`
%extends('layout')
%block('content')
<p>This is the page content</p>
%endblock
Explanationβ
The content.tintin.php file will inherit the code from layout.tintin.php, and if you look closely, the layout.tintin.php file has the %inject clause whose parameter is the name of the %block from content.tintin.php, which is content. This means that the content of the content %block will replace %inject. The final result will be 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>
Custom directiveβ
Tintin can be extended with its custom directive system. To do so, use the directive method.
Let's create directives to handle 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>';
});
Using these directives could not be simpler. Write the directive name prefixed with %. Then, if the directive takes parameters, call the directive the same way you call functions in your program.
<div class="container">
%form("/posts", "post", "multipart/form-data")
%input("text", "name")
%button('submit', 'Add')
%endform
</div>
Compilation works as usual; for more information, see 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>
Adding your directives via configurationβ
To register your directives when BowPHP starts, create (or
modify) a provider in app/Configurations/ that extends
Bow\Configuration\Configuration:
namespace App\Configurations;
use Bow\Configuration\Configuration;
use Bow\Configuration\Loader as Config;
class ApplicationConfiguration extends Configuration
{
/**
* Launch configuration
*/
public function create(Config $config): void
{
$tintin = app('view')->getEngine();
$tintin->directive('super', function () {
return "Super !";
});
}
public function run(): void
{
//
}
}
Do not forget to register this provider in Kernel::configurations() so
that it is executed at boot.
The %super directive is now available and you can use it.
return $tintin->render('%super');
// => Super !
The %macro directiveβ
You will often need to use or reuse a block of template to optimize how you write your application. That is exactly what macros are for.
Macros must be defined in a separate file.
To use %macro, you must pass the macro name as the first parameter, followed by the macro's parameters.
Consider the user-macro.tintin.php file.
%macro('users', array $users)
%loop($users as $user)
<div>{{ $user }}</div>
%endloop
%endmacro
To use the macro, you must import it into another file with %import. We will name the file app.tintin.php, containing the following template:
%import('user-macro')
%extends('layout')
%block('content')
<div class="container">
{{ users($users) }}
</div>
%endblock
For 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>
Is something missing?
If you run into problems with the documentation or have suggestions to improve the documentation or the project in general, please open an issue for us, or send a tweet mentioning the Twitter account @bowframework or directly on github.