Laravel integration¶
View composers¶
composing: and creating: events fire for every template Smarty loads, including {extends} parents and {include} partials, so view composers work the same as they do for Blade:
Data added by a composer via $view->with(...) (or $view->withErrors(...) etc.) is transcribed onto the actual sub-template before render, so the variables are visible inside {extends} layouts and {include} partials — same contract Blade gives. Both composing: and creating: listeners can mutate the View; either way the values reach the render scope. View::share precedence is unchanged: the global shared bag still feeds every render, with composer-added data layered on top (composer wins on key collision, matching Blade's gatherData() order).
withErrors() works the same way — useful when a partial expects validation feedback even in routes where validation didn't run:
// AppServiceProvider::boot()
View::composer('partials.account-form', function ($view) {
$view->withErrors(session('errors', new \Illuminate\Support\ViewErrorBag));
});
Inside the partial, $errors (the auto-shared wrapper) reflects whatever the composer set, so {if $errors->has('email')}…{/if} renders consistently across routes that did and didn't validate.
A class-based composer is identical to Blade — point Laravel at any class with a compose(View $view) method:
The composer is resolved through the container on every render, so constructor type-hints (an Eloquent repository, a domain service, etc.) auto-wire.
Debug tooling¶
creating: and composing: view events fire for every template Smarty loads — entries, {extends} parents, and {include} partials — so anything in the Laravel ecosystem that listens to those events sees the full render tree. Debugbar's Views tab, Telescope's Views watcher, and any other tool that hooks Laravel's view events should work without extra wiring, the same way they do for Blade.
Template error source mapping¶
A runtime error inside a .tpl body — say {$user->getAuthIdentifier()} when $user is null — would naturally land on Smarty's compiled <hash>_<file>.tpl.php file under storage/framework/smarty/compile/, with no obvious link back to the template you actually wrote. This package rewrites that automatically.
- A custom compiler (
Debug\LineTrackingCompiler) emits/*__SLM:N*/and/*__SLF:/abs/path*/markers into the compiled output during compilation. Installed via reflection atTemplateinstantiation time, no vendor patching. Debug\SourceMapwalks back from the compiled-file frame to the closest preceding marker.- On Laravel 11+,
Debug\SmartyExceptionMapperextends the framework'sBladeMapperand is bound in its place, so the exception page rewrites every.tpl.phptrace frame to the.tplsource — same treatment Blade enjoys for.blade.php. SmartyEngine::remapException()walks the fullgetPrevious()chain so errors raised inside a{capture}body still surface the user's real exception, not Smarty'sNot matching {capture}{/capture}rethrow wrapper.
The mapping covers {block} bodies of {extends} children, {include}d partials (including inline), {function} bodies (both {call} and short-tag invocations), {capture} bodies, {if} condition expressions, and Smarty\CompilerExceptions raised at compile time. Laravel 10 has no BladeMapper to extend, so the trace-frame rewrite no-ops there — error messages still carry a (View: /path/to/source.tpl) suffix and CompilerException source paths/lines are preserved.