Search by

preflow / blade

smyr

Preflow Blade adapter — Laravel Blade template engine implementation

Package info

github.com/getpreflow/blade

pkg:composer/preflow/blade

Statistics

Installs: 140

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0


README

Laravel Blade adapter for Preflow. Implements TemplateEngineInterface using illuminate/view, with custom directives for asset collection and template functions.

Installation

composer require preflow/blade

Requires PHP 8.4+ and illuminate/view 11+.

What's included

Component Description
BladeEngine TemplateEngineInterface implementation wrapping Blade's Factory and BladeCompiler
@css / @endcss Directive pair for co-located CSS via AssetCollector
Custom directives Template functions registered via addFunction() become @name(...) directives

BladeEngine

use Preflow\Blade\BladeEngine;
use Preflow\View\AssetCollector;
use Preflow\View\NonceGenerator;

$assets = new AssetCollector(new NonceGenerator(), isProd: true);

$engine = new BladeEngine(
    templateDirs: [__DIR__ . '/templates', __DIR__ . '/app/pages'],
    assetCollector: $assets,
    debug: false,
    cachePath: __DIR__ . '/storage/blade-cache',  // null = system temp dir
);

$html = $engine->render('blog.post', ['post' => $post]);
$engine->exists('partials.nav');         // bool
$engine->getTemplateExtension();         // 'blade.php'

Templates

Blade templates use .blade.php extension with standard Blade syntax:

{{-- resources/views/blog/post.blade.php --}}

<h1>{{ $post->title }}</h1>
<div>{!! $post->body !!}</div>

@if($post->published)
  <span>Published {{ $post->date }}</span>
@endif

@foreach($comments as $comment)
  <p>{{ $comment->text }}</p>
@endforeach

Co-located styles

Use @css / @endcss to register CSS with the AssetCollector:

@css
.post-title { font-size: 2rem; font-weight: 700; }
.post-body  { line-height: 1.7; }
@endcss

<h1 class="post-title">{{ $post->title }}</h1>
<div class="post-body">{!! $post->body !!}</div>

Layout with extends and sections

{{-- _layout.blade.php --}}
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>@yield('title', 'App')</title>
</head>
<body>
  @yield('content')
</body>
</html>
{{-- page.blade.php --}}
@extends('_layout')

@section('title', 'Blog')

@section('content')
  <h1>My Blog</h1>
@endsection

Custom function directives

Template functions registered via addFunction() become Blade directives. With isSafe: true, output is rendered raw; otherwise it is escaped via e().

$engine->addFunction(new TemplateFunctionDefinition(
    name: 'component',
    callable: fn (string $name, array $props = []) => $renderer->render($name, $props),
    isSafe: true,
));
@component('Counter', ['count' => 0])

Global variables

$engine->addGlobal('siteName', 'My App');
<footer>{{ $siteName }}</footer>

Live parts: @live

The Blade twin of Twig's {% live %} (preflow/core's page cache, live parts):

@live(['number' => $object->number])
    <button aria-pressed="{{ in_album($number) ? 'true' : 'false' }}">…</button>
@endlive
  • The body is taken out of the template before Blade compiles it, into a view file of its own ({cache}/live/{hash}.blade.php, written again from the template when the cache is cleared), and rendered with only the given values and the globals. The values are scalars and arrays; @live without them works too.
  • While a page renders for the page cache, @live prints a signed placeholder (file, index, values) instead; the body is rendered for each visitor when the page is served. Otherwise it renders in place.
  • Not nested, and only in template files (not in strings compiled on the fly).
  • The placeholder names the template by its view name in the template folders, or, in a component's own template, by the component class, with the body's hash: after a cleared cache the body is taken again from the template only while it still has that hash. A template outside both renders its live blocks in place and keeps the page from being stored.

Freshness and expiry: @freshness, @expires

The same as Twig's {% freshness %} and {% expires %} (preflow/core's page cache):

@freshness('next-visit') @foreach ($others as $job) … @endforeach @endfreshness
@freshness('timed', every: '12h') {{ $weather() }} @endfreshness
@expires(at: $event->startsAt)
@expires(in: '1h')
  • Names: 'fresh', 'next-visit', 'timed'; only timed takes every ('30m', '12h', '2d' or seconds). Nested, the innermost wins; a throw inside closes the scope. A timed page never outlives the page cache's max_age (4 h by default): raise it for longer times (up to 20 h).
  • As with every Blade directive, @endfreshness right after a letter or digit (x@endfreshness) is not seen: put a space or a tag before it.

Engine configuration

Set APP_ENGINE=blade in your .env to use Blade. Preflow's Application will automatically create a BladeEngine and register all extension providers.