Skip to main content
Version: 5.x

Using views

Introduction​

Views contain the HTML served by your application and separate your controller/application logic from your presentation logic.

Configuration​

Template engines

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

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
ParameterTypeDescription
$viewstringThe name of the view, without extension, relative to the views folder
$dataarrayThe 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']);
Helper

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

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 PHP htmlspecialchars function 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 %elif instead 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 %else to 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:

app/Configurations/ApplicationConfiguration.php
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.