Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Template Bundle Laravel Package

mindy/template-bundle

Mindy Template Bundle is a Symfony bundle for managing and rendering reusable templates, giving your application a flexible templating layer with simple configuration and integration into the Mindy ecosystem.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a MindyPHP/Symfony bundle, not a Laravel package. There is no service provider, no config/template.php, no Blade integration. Before writing any code, decide whether you are actually on Mindy/Symfony — if you are on Laravel, the honest "getting started" step is to close the tab and use Blade or a maintained Twig bridge. The steps below assume you have a legitimate reason to be here (Mindy app, porting work, or reading it as a reference).

Step 1 — Read the source before installing (10 minutes). With 2 stars and zero dependents, the repo is the documentation. In order:

  • composer.json — what the require block pulls in, the PSR-4 namespace, whether it pins a Mindy core version.
  • The bundle class (src/TemplateBundle.php or similar) — this tells you the config root key and whether a compiler pass is registered.
  • Resources/config/ — the service definitions. This is the real API: the service ID you'll inject, the engine class, the loader paths.
  • Tests/ if it exists — the only working usage examples you are likely to get.

Step 2 — Install with a pinned commit. Do not assume Packagist metadata is accurate for a package this small:

composer require mindy/template-bundle

If that fails or resolves to something unexpected, add a VCS repository and pin:

{
  "repositories": [{ "type": "vcs", "url": "https://github.com/MindyPHP/TemplateBundle" }],
  "require": { "mindy/template-bundle": "dev-master" }
}

Commit composer.lock with the resolved commit hash. You are effectively vendoring this.

Step 3 — Register the bundle in your Mindy kernel / bundles config:

// app/config/bundles.php or Kernel::registerBundles()
return [
    // ...
    new Mindy\Bundle\TemplateBundle\TemplateBundle(),
];

Step 4 — Configure the template path and render one view. Add the minimal config the bundle's Configuration::getConfigTreeBuilder() expects — almost certainly a template directory and a cache directory:

# app/config/config.yml — verify the root key against the bundle class
template:
    path: '%kernel.root_dir%/../templates'
    cache: '%kernel.cache_dir%/templates'

Then render your first template from a controller by injecting the renderer service. The service ID is in Resources/config/services.yml; it will look something like:

public function indexAction(TemplateRenderer $renderer)
{
    return new Response($renderer->render('pages/index.html', ['title' => 'Hello']));
}

First use case: a single "hello world" template that proves (a) the templates directory resolves, (b) the compiled cache writes to disk, and (c) escaping actually happens. Test (c) explicitly by rendering {{ user_input }} with <script>alert(1)</script> — an unescaped result here is a hard stop.

Implementation Patterns

Inject the renderer, don't reach for the container. The bundle's value is that it registers the engine as a service. Use that: type-hint the renderer (or a thin wrapper you own) in your controller and service constructors rather than calling the container statically. This keeps your code testable and means a future swap of the engine doesn't ripple through every call site.

class ReportController
{
    public function __construct(private TemplateRenderer $renderer) {}

    public function show(int $id): Response
    {
        return new Response($this->renderer->render('reports/show.html', [
            'report' => $this->reports->find($id),
        ]));
    }
}

Wrap it in your own adapter. Given the maintenance profile, put a ViewRenderer interface in your app and have the bundle-backed implementation live behind it. If the package goes stale — or you fork it — the seam is already in place.

interface ViewRenderer { public function render(string $tpl, array $data = []): string; }

class MindyViewRenderer implements ViewRenderer
{
    public function __construct(private $engine) {}
    public function render(string $tpl, array $data = []): string
    {
        return $this->engine->render($tpl, $data);
    }
}

Register custom filters/functions once, at boot. The bundle exposes hooks for extensions, but you do not want extension registration scattered through controllers. Do it in one bootstrap location (a service decorator, a compiler pass, or your own provider wrapper) so the set of helpers is greppable and documented:

$engine->addFilter('currency', fn ($n) => number_format($n, 2));
$engine->addFunction('asset', fn ($p) => $this->assetBase . '/' . ltrim($p, '/'));

Map helpers deliberately: anything Mindy expects (asset, csrf_field, route) should be a thin callback onto your framework's existing helper, not reimplemented.

Use the compiled-template cache, and key invalidation off template mtime. Leave cache enabled in dev only if recompilation is genuinely mtime-based; otherwise toggle cache off in dev and on in prod via config env substitution.

template:
    path: '%kernel.root_dir%/../templates'
    cache: '%kernel.cache_dir%/templates'
    auto_reload: '%kernel.debug%'

Prefer layout + partials over string concatenation. Render one full page template per action; compose with includes/extends rather than building HTML in PHP. This keeps the template directory the single source of truth and makes it possible to hand the templates to a designer — which is the whole reason to choose a Twig-style engine over Blade/heredoc.

Testing pattern. Assert on rendered output, not on the engine internals: build the renderer with a temp cache directory, render a fixture template, assert the string. Keep one test that renders a template containing untrusted input and asserts it is escaped — that test is the tripwire that catches a bad config change.

Gotchas and Tips

It will not register itself in Laravel. No laravel extra in composer.json means no auto-discovery, no config/ publish, no Illuminate\Contracts\View\Engine implementation. If you attempt this on Laravel you are building a service provider, a ViewEngine adapter, and a finder — a 3–5 day spike, not an install. Only do that if you are temporarily preserving Mindy syntax during a migration.

Escaping is the risk you cannot skip. Autoescape correctness is not verifiable from a README that doesn't exist. Write the <script>alert(1)</script> test on day one. If it isn't escaped by default, either configure it before writing a single production template or walk away — a hand-wired escaper is a direct XSS vector.

Cache directory must be writable by the web user, and shared across nodes. The most common operational surprise with compiled-template engines is either a permissions failure on first request (surfaces as a runtime error, not a boot error) or divergent output across nodes when the cache lives on local disk. Bake compile at build time or point the cache at shared storage. Confirm invalidation is mtime-keyed; stale compiled templates after deploy are the second most common surprise.

Template-name resolution must not touch request input. If any code path lets a user influence the template name (?theme=..., ?template=...), you have path traversal. The bundle almost certainly does not sanitize this for you. Whitelist theme/template names in your own code.

No test suite signal. There's no evidence of CI, tagged releases, or a changelog. Budget for the possibility that dev-master is the only installable version, and pin the commit hash explicitly so a force-push to master cannot silently change what you deploy.

You become the maintainer. Zero dependents means nobody else is filing bugs, nobody is patching security issues, and Stack Overflow has nothing. Assume you own this the moment it's in composer.json. If security or escaping support matters, fork it under your org, add a smoke-test suite to your CI, and pin internally — don't depend on the upstream repo.

PHP/Symfony version drift. The bundle targets an older Mindy core. Verify PHP 8.x compatibility and Symfony component pins against what is installable today before committing. Run composer why-not php 8.2 after install to see what is holding you back.

Compile errors surface at request time. Template syntax errors typically blow up on first render, not at boot. Add a smoke test that renders every template in the directory as part of CI so broken templates fail the build rather than a page.

Extension points are your escape hatch — use them narrowly. The custom-filter/function hooks are the right place to bridge framework helpers (asset, route, csrf_field) into templates. Do not use them to smuggle business logic into views; every function you add is a new thing a future developer has to learn and cannot reuse on the next Laravel project.

Onboarding cost is real. Mindy template syntax is a non-transferable skill. Two weeks of mid-level ramp-up is the realistic number, and that cost is what usually makes "free template engine" the more expensive option versus sticking with the framework's default.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console