Views and templates

Views hold your page markup. The Flo Template Engine adds a short syntax for printing values, conditions, loops and forms, and plain PHP works too.

For a complete controller, view, partial, layout and stylesheet working together, follow the blog example. Then add a contact form and shared theme markup.

Views and layouts

The view for BlogController::index() is views/blog/index.html. FloCMS renders it, then places the result inside the active layout:

  • templates/<template>/layouts/default.html for site pages
  • templates/<template>/layouts/admin.html for /admin pages

In the layout, <?= $data['content'] ?> prints the rendered view. APP_TEMPLATE in .env picks the template; files missing from it come from templates/default/.

The standard page dispatcher passes only the rendered $content to the layout, not every controller variable. Controller data is available in the page view; compute shared layout data independently and pass it explicitly to partials.

Printing values

<h1>{{ $title }}</h1>
<p>{{ $post['author'] }}</p>

{{ }} HTML-escapes printable values for text content and quoted HTML attributes. Select a property or array element instead of printing an entire record. HTML escaping does not encode JavaScript or validate URL schemes. To print trusted HTML as it is, use {!! $html !!}. In plain PHP, e($value) escapes a value.

Warning

Never use {!! !!} for anything a visitor typed. It prints raw HTML, which allows cross-site scripting.

Conditions and loops

@if($posts)
    @foreach($posts as $post)
        <article>{{ $post['title'] }}</article>
    @endforeach
@else
    <p>No posts yet.</p>
@endif
Syntax Meaning
@if(...), @elseif(...), @else, @endif Conditions
@foreach(...), @endforeach Loop over an array
@for(...), @endfor, @while(...), @endwhile Other loops
@forelse($items) ... @empty ... @endforelse Loop with a fallback when the array is empty (items are $key and $value)
@php ... @endphp A block of plain PHP
@lang('key') A translation from lang/<language>.php
@config('key'), @env('KEY') A config or .env value
@csrf The CSRF field for a form

Expressions can contain function calls, nested arrays and quoted strings:

@if(count($posts) > 0)
    @foreach(array_slice($posts, 0, 3) as $post)
        <p>@lang('blog.by', ['name' => strtoupper($post['author'])])</p>
    @endforeach
@endif

Note

This needs flocms-core 2.2.2 or newer. In 2.2.1 the expression ended at the first ), so @if(count($posts) > 0) compiled to broken PHP, and @forelse didn't work.

The engine replaces these words anywhere in a view or layout, also inside HTML comments and JavaScript. To show a literal @ before one of them, write it as &#64;.

Forms

Every form that posts data needs a CSRF token. @csrf prints the hidden field:

<form method="post" action="">
    @csrf
    <input type="email" name="email" required>
    <button type="submit">Subscribe</button>
</form>

For JavaScript requests, the admin layout includes a csrf-token meta tag and js/csrf.js, which sends the token as the X-CSRF-TOKEN header with fetch and jQuery requests. See sessions and CSRF.

Partials

Split repeated markup into partials in templates/<template>/partials/ and render them with data:

<?= render_partial('header.html', ['title' => $title]) ?>

Partials use the same template syntax as views.

They do not automatically inherit local variables from the calling template. Pass the values they need in the second argument of render_partial().

Assets

Link to CSS, JavaScript and images in the active theme with template_asset(). It falls back to the default theme when the file isn't in yours:

<link rel="stylesheet" href="<?= template_asset('css/style.css') ?>">

Translations

@lang('key') prints the text for the current language from lang/<language>.php, and __('key') returns it in PHP. See localization for placeholders, right-to-left languages and adding languages.

Compiled templates

Templates are compiled to PHP in views/cache/ the first time they're used, and again whenever they change. The web server must be able to write to that folder. php flo optimize compiles everything ahead of time, which is handy after a deployment, and php flo view:clear empties the cache.

Config::set('view.cache_path', ...) moves the cache to another folder. When the folder can't be written, templates still work: they are compiled on every request instead, which is slower.

Set Config::set('view.cache', false) to disable compiled-file caching explicitly. Use the boolean false; the string 'false' does not disable it. Rendering then compiles and evaluates the template in memory. Cache files incorporate the template's path, modification time, size and compiler version.

Rendering a file directly

Render a trusted template file without the page controller's view/layout selection:

use FloCMS\Core\TemplateEngine;

$html = TemplateEngine::renderFile(ROOT . '/templates/default/partials/header.html', ['title' => 'My site']);

The result is a string. Data is available as template variables and $data, and the usual caching rules apply. TemplateEngine::compiledPath($file) returns the compiled path, or null when caching is disabled/unavailable; cacheDirectory() returns the writable cache directory or null.

render_partial() handles active-template lookup for you. template_partial() returns its path without rendering it. See helpers.

Esc