Tempest views
This page describes the technical specification for Tempest views.
General
A couple of general naming conventions:
- All Tempest view files are suffixed with
.view.php
. - View components are always prefixed with
x-
:<x-base />
Views can be created directly by using the view()
function (this will create a GenericView
object behind the scenes). Data is available within the view by passing named arguments in the view()
function.
return view(__DIR__ . '/home.view.php', variable: 'Hello World');
Views can also be created by returning an object that implements the View
interface, all public properties and methods of this object are available to the view file:
use Tempest\View\View; use Tempest\View\IsView; final class HomeView implements View { use IsView; public function __construct( public string $name, public DateTime $date, ) { $this->path = __DIR__ . '/home.view.php'; } public function formatDate(DateTimeImmutable $date): string { return $date->format('Y-m-d'); } }
{{ $name }} {{ $this->formatDate($post->date) }}
View paths can be absolute paths, as well as relative to any discovery location available within Tempest.
// Absolute path return view(__DIR__ . '/home.view.php'); // Will search all registered discovery locations with this relative path return view('home.view.php');
Echo
The shorthand echo tag will print and automatically escape data:
<h1> {{ $this->post->title }} {{ $this->raw($this->post->title) }} </h1>
The shorthand raw echo tag will print data without escaping it:
<h1> {!! $this->post->title !!} {!! $this->raw($this->post->title) !!} </h1>
Data scoping
Variables defined in views can be accessed either via $this->variable
or $variable
directly:
view(__DIR__ . '/home.view.php', variable: 'hello');
{{ $this->variable }} {{ $variable }}
Variables passed to view components are only available directly via $variable
, they are only locally available within that view component.
<!-- home.view.php --> <x-base title="Hello world"></x-base>
<!-- x-base.view.php --> <x-component name="x-base"> {{ $title }} <!-- Won't work: --> {{ $this->title }} </x-component>
Attributes
All attributes prefixed with :
will be parsed as PHP code, these are called expression attributes. Expression attributes can be added on any element: both on regular HTML elements, and view component elements.
<a :href="$this->post->uri"></a>
Attributes passed to view components will be available as variables within that component. These are called attribute variables:
<!-- home.view.php --> <x-base title="Hello world"></x-base>
<!-- x-base.view.php --> <x-component name="x-base"> {{ $title }} </x-component>
Attribute variables can also be defined as expression attributes:
<!-- home.view.php --> <x-base :title="$this->post->title"></x-base>
<!-- x-base.view.php --> <x-component name="x-base"> {{ $title }} </x-component>
Attribute to variable mapping adheres to the following naming conventions:
- camelCase or PascalCase attribute names are automatically converted to all-lowercase variables, this is due to limitations in PHP's DOM extension: all attribute names are automatically converted to lowercase:
<x-base metaType="test" />
<x-component name="x-base"> {{ $metatype }} </x-component>
- kebab-cased attributes are converted to camelCase variables:
<x-parent meta-type="test" />
<x-component name="x-base"> {{ $metaType }} </x-component>
- snake_cased attributes are converted to camelCase variables:
<x-parent meta_type="test" />
<x-component name="x-base"> {{ $metaType }} </x-component>
Control structures
If
<div :if="$this->post->published">Published</div> <div :elseif="$this->post->archived">Archived</div> <div :else>Not published</div>
Foreach
<div :foreach="$this->posts as $post">{{ $post->title }}</div> <div :forelse>Nothing</div>
View components
Tempest components can be defined in two ways. First you can create PHP classes:
final readonly class MyViewComponent implements ViewComponent { public static function getName(): string { return 'x-my'; } public function compile(ViewComponentElement $element): string { $foo = $element->getAttribute('foo'); $bar = $element->getAttribute('bar'); if ($foo && $bar) { return "<div foo=\"{$foo}\" bar=\"{$bar}\"><x-slot /></div>"; } return '<div><x-slot /></div>'; } }
Or you can create anonymous components in .view.php
files.
<x-component name="x-my"> <div :if="$this->foo && $this->bar" :foo="$this->foo" :bar="$this->bar"> <x-slot /> </div> <div :else> <x-slot /> </div> </x-component>
View Component Slots
The default slot <x-slot />
will contain the contents of a component:
<x-my> <p>These contents will end up in the default slot</p> </x-my>
You can define multiple named slots:
<x-component name="x-my"> <header> <x-slot name="header" /> </header> <x-slot /> <footer> <x-slot name="footer" /> </footer> </x-component>
<x-my> This goes in the default slot <x-slot name="header"> The title </x-slot> <x-slot name="footer"> The footer </x-slot> This will be appended into the main slot </x-my>