Views are the pages your visitors see. FrostMVC uses Twig
as its template engine, and every view lives in app/views with the .php.twig extension.
Render a view from a controller with View::get(). The name is the path under app/views without the extension:
View::get('blog/view', ['post' => $post, 'related' => $related]); // app/views/blog/view.php.twig
Each key of the array becomes a Twig variable:
<h1>{{ post.title }}</h1>
<p>{{ post.body|raw }}</p>
{% for item in related %}
<a href="{{ BASEPATH }}/blog/view/{{ item.id }}">{{ item.title }}</a>
{% else %}
<p>No related posts.</p>
{% endfor %}
{{ value }} automatically. Only use |raw for HTML you trust, such as content you sanitised with Form::fip('body', 'html').
Put the shared page frame in a base template and let each view fill in its blocks:
{# app/views/templates/base.template.php.twig #}
<html>
<head><title>{% block pagename %}{% endblock %}{{ site.getName() }}</title></head>
<body>{% block content %}{% endblock %}</body>
</html>
{# app/views/blog/view.php.twig #}
{% extends "templates/base.template.php.twig" %}
{% block pagename %}{{ post.title }} — {% endblock %}
{% block content %}
<h1>{{ post.title }}</h1>
{% endblock %}
Use {% include "templates/menu.php.twig" %} for smaller reusable pieces.
BASEPATH | Your site URL, e.g. {{ BASEPATH }}/blog. |
RESOURCEPATH | URL of the assets folder, e.g. {{ RESOURCEPATH }}/css/style.css. |
site | The Site instance: site.getName(), site.getEmail(), site.getEnvironment(), site.getCurrentURL()… |
session | Session: session.get('user'), and session.flash('success') to show a message once. |
form | Form: form.start(), form.end() and input helpers. |
security, words, cookie, date_time | The Security, Words, Cookie and Date_Time helpers. |
Add your own with $instance->twig->addGlobal('name', $value) in app/settings.php.
The starter settings add version (for cache-busting asset URLs) and nonce (for the Content-Security-Policy of inline scripts).
Set a message in the controller, redirect, and show it once on the next page:
Session::set('success', 'Post saved!');
Functions::io_relocate(BASEPATH . '/blog');
{% if session.get('success') %}
<div class="alert alert-success">{{ session.flash('success') }}</div>
{% endif %}
Open POST forms with form.start() so they carry the CSRF token FrostMVC checks on every POST request:
{{ form.start(BASEPATH ~ '/blog/save', 'post') }}
<input name="title" value="{{ post.title }}"/>
<button type="submit">Save</button>
{{ form.end() }}
A POST without a valid token is rejected before your controller runs. The token settings are in Security::$CSRFTokenConfig in app/settings.php.
Register your own Twig functions and filters in libraries/TwigExtensions.php, which the starter loads at the end of settings.php.
For example, the starter's makeTitle filter turns my-blog-post into My Blog Post:
$fw->twig->addFilter(new TwigFilter('makeTitle', function ($title) {
return makeTitle($title);
}));
{{ 'my-blog-post'|makeTitle }}
In the Development environment Twig runs in debug mode, so {{ dump(post) }} prints a variable.
Compiled templates are cached in libraries/vendor/twig/tmp; delete that folder if a view does not update.