aura/view
Aura.View implements the TemplateView and TwoStepView patterns using PHP itself as the template language, with file-based and closure templates, helpers, and sections. No runtime dependencies; requires PHP 8.4+.
^8.4 is required as of 7.0.0. Upgrade your runtime before adopting this release; older Laravel applications may be blocked by Composer.composer require aura/view:^7.0
use Aura\View\ViewFactory;
$viewFactory = new ViewFactory();
$view = $viewFactory->newInstance();
newInstance() and newInstance($helpers) still work. The longer positional form has changed; use ViewSpec objects when configuring registries.
Register a template, set data, set the view, then invoke it. Setters return void, so do not chain setter calls.
$view->getViewRegistry()->set('home', __DIR__ . '/views/home.php');
$view->setData(['title' => 'Welcome']);
$view->setView('home');
echo $view();
To use a layout:
$view->getLayoutRegistry()->set('default', __DIR__ . '/layouts/default.php');
$view->setLayout('default');
echo $view();
7.x README, especially the Escaping Output section.7.x unit tests for examples of the new interfaces, exceptions, ViewSpec, and parent().ViewFactory, View, ViewSpecTemplateRegistry, TemplateRegistryInterfaceSearchPathInterfaceHelperRegistry, HelperRegistryInterfaceAura\View\Exception\*File-based templates remain the normal path:
$view->getViewRegistry()->set('template', __DIR__ . '/views/template.php');
$view->setData(['user' => 'John']);
$view->setView('template');
echo $view();
Closure/callable templates are normalized to Closure instances and bound to the View, so use $this to reach data and helpers:
$view->getViewRegistry()->set('greeting', function () {
echo 'Hello, ' . $this->user;
});
$view->setData(['user' => 'Jane']);
$view->setView('greeting');
echo $view();
Do not rely on the original callable being stored as-is; TemplateRegistry::get() returns a Closure.
Register layouts in the layout registry:
$view->getLayoutRegistry()->set('default', __DIR__ . '/layouts/default.php');
In a template, capture a section:
<?php $this->beginSection('sidebar'); ?>
<div>Sidebar content</div>
<?php $this->endSection(); ?>
In a layout, render it if present:
<?php if ($this->hasSection('sidebar')): ?>
<?= $this->getSection('sidebar') ?>
<?php endif; ?>
endSection() without a matching beginSection() now throws Aura\View\Exception.
Register helpers explicitly and guard against collisions:
$helpers = $view->getHelpers();
if (! $helpers->has('escape')) {
$helpers->set('escape', function ($str) {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
});
}
$helpers->set('url', $urlHelper, override: true);
HelperRegistry::set() now throws Aura\View\Exception\HelperAlreadyRegistered if the name is already taken. Registering the identical callable twice also throws; use override: true deliberately or has() to branch.
Aura.Html-style helper locators still work without an adapter. The View accepts the helper manager as ?object because it reaches helpers only through __call().
ViewSpecThe old long positional form of ViewFactory::newInstance() has changed. The first $helpers argument is unchanged; registry configuration now uses ViewSpec.
use Aura\View\ViewSpec;
$view = $viewFactory->newInstance(
$helpers,
new ViewSpec(
map: ['home' => base_path('resources/views/home.php')],
paths: [base_path('resources/views')],
namespaces: ['app' => [base_path('resources/views/app')]],
extension: '.php',
),
new ViewSpec(
paths: [base_path('resources/views/layouts')],
),
);
The four registry settings are map, paths, namespaces, and extension. This is the preferred way to pre-register templates and avoid runtime lookups.
parent()Search paths are first-hit-wins. A template in an earlier directory shadows a later one, and previously the shadowed template was unreachable. parent() renders the shadowed template by resuming the search after the current template’s directory.
<?php // app/views/widget.php shadows package/views/widget.php ?>
<?php $this->beginSection('extra'); ?>
<p>Application-specific addition</p>
<?php $this->endSection(); ?>
<?= $this->parent() ?>
This lets an application override only the difference instead of copying the entire package template.
Use Aura.View alongside Laravel rather than trying to replace Blade globally. It is useful for isolated package templates, legacy modules, or media-type-agnostic rendering.
Bind the factory and view in a service provider:
public function register()
{
$this->app->singleton(\Aura\View\ViewFactory::class, function () {
return new \Aura\View\ViewFactory();
});
$this->app->bind(\Aura\View\View::class, function ($app) {
return $app->make(\Aura\View\ViewFactory::class)->newInstance();
});
}
Use it in a controller:
$view = app(\Aura\View\View::class);
$view->getViewRegistry()->set('invoice', resource_path('views/invoice.php'));
$view->setData(['invoice' => $invoice]);
$view->setView('invoice');
return response($view());
Register common paths and helpers in a service provider or module bootstrapper. config/Common.php, the extra.aura block, and Aura.Di config integration have been removed; wiring now belongs to the consuming framework’s module/provider.
Instantiate a fresh view per test and assert the rendered string:
public function testTemplateRendering()
{
$view = (new \Aura\View\ViewFactory())->newInstance();
$view->getViewRegistry()->set('test', function () {
echo $this->message;
});
$view->setData(['message' => 'Hello']);
$view->setView('test');
$this->assertSame('Hello', $view());
}
A view with no view template set now returns '' rather than throwing TemplateNotFound:
$view = (new \Aura\View\ViewFactory())->newInstance();
$this->assertSame('', $view());
^8.4 and declare(strict_types=1): subclasses must add matching native parameter and return types or they will fatal on load. Direct callers are unaffected.setData(), setView(), setLayout(), etc. return void. Replace chains such as $view->setView('home')->setData($data) with separate statements.ViewFactory::newInstance() long form changed: use ViewSpec for map, paths, namespaces, and extension. The old positional form fails with a TypeError naming the parameter.TemplateRegistry::get() returns \Closure: non-closure callables are normalized so $this can be bound to them. This also fixes the old array-callable failure in bindTo().TemplateRegistryInterface: if you contribute search paths, type against SearchPathInterface. Do not assume the concrete TemplateRegistry.HelperRegistry::set() throws HelperAlreadyRegistered unless override: true is passed. This includes registering the same callable twice.HelperNotFound: catch Aura\View\Exception\HelperNotFound rather than relying on a raw PHP Error.endSection() throws Aura\View\Exception: ensure every endSection() has a matching beginSection().InvalidTemplateName: a name containing more than one :: now throws Aura\View\Exception\InvalidTemplateName. Catch Aura\View\Exception rather than \InvalidArgumentException.'': the view no longer raises TemplateNotFound for a null template name. If you need an exception, assert has()/set the view explicitly before rendering.config/Common.php, extra.aura, the Aura\View\_Config\ PSR-4 entry, and the aura/di dev dependency are gone. Move wiring into your Laravel provider or module class.InvalidHelpersObject is deprecated: it is never thrown because the helpers parameter is typed ?object, so PHP’s TypeError rejects invalid values first. The class remains for legacy catch blocks until 8.0.Aura.View is media-type agnostic and ships no escaper by design. Always escape dynamic output for the target context.
$view->getHelpers()->set('h', function ($str) {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}, override: true);
Use Aura.Html or your own context-specific helpers where appropriate. The README’s Escaping Output section is the reference.
getPaths(), getNamespaces(), and getNamespacePaths().getNamespacePaths() returns [] for an unregistered namespace rather than throwing. Use hasNamespace() when you need to distinguish “unregistered” from “registered but empty.”has() before use. Missing helpers throw HelperNotFound.parent() depends on path-backed search behavior. It is most useful with search paths and shadowed templates; precompiled name-to-file maps have no search position to resume from.ViewSpec in a service provider/module bootstrap rather than doing it per request.Closure and bound to the View.addData() still merges data; setData() replaces it. Setters are not chainable.?object, not HelperRegistryInterface, because the View only calls helpers through __call(). This keeps Aura.Html’s helper locator compatible without an adapter.How can I help you explore Laravel packages today?