Using views
Introductionβ
Views contain the HTML provided 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 in the config/ folder.
Specify the name of the template to use with the configuration's engine 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 a 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 class View::parse:
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('nom-de-la-vue-sans-extension');
To pass variables to the view:
use Bow\View\View;
echo View::parse('nom-de-la-vue-sans-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('vue', $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 content of the variables passed to the view. You can also echo the results of any PHP function. In fact, you can insert any PHP code into 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 protected, 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 don't 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 let you build conditional branches, 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 specificity: %unless lets you write 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 over elements. For example, displaying all the users of your platform.
Using %loopβ
This clause does exactly what foreach does.
%loop($names as $name)
Bonjour {{ $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')
Bonjour {{ $name }}
%stop
%endif
%endloop
You may have noticed the %stop, which lets you stop the loop's execution. There is also its companion %jump, which stops execution at its level and starts the next iteration of the loop.
The %jump and %stop syntactic sugarβ
Often the developer needs to write loop-stopping conditions in a %loop like this:
%loop($names as $name)
%if($name == 'tintin')
%stop
// Or
%jump
%endif
%endloop
With syntactic sugar, you can shorten 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β
Often when developing your code, you will need to split up your application's views to be more flexible and write less code.
%include lets you include another template file into 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 needed, 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 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>
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:
# le fichier `layout.tintin.php`
<!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.
# le fichier se nomme `content.tintin.php`
%extends('layout')
%block('content')
<p>This is the page content</p>
%endblock
Explanationβ
The content.tintin.php file inherits 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 in content.tintin.php, which is content. This means that the content of the content %block will be substituted by %inject. This will ultimately give the following:
<!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 this, use the directive method.
Let's create some 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 name of the directive preceded by %. 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 about 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 at BowPHP startup, 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
{
//
}
}
Don't forget to register this provider in Kernel::configurations() so
that it is executed at boot.
Now the %super directive is available and you can use it.
return $tintin->render('%super');
// => Super !
The %macro directiveβ
Often, you will need to use or reuse a block of template to streamline the writing of your application. That is exactly what macros are for.
Macros must be defined in a separate file.
To use %macro, you must pass the name of the macro as the first parameter, followed by the macro's parameters.
Consider the file user-macro.tintin.php.
%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 call the file app.tintin.php, which contains 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.