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

Twig Constant Accessor Bundle Laravel Package

jdecool/twig-constant-accessor-bundle

Symfony bundle that integrates a Twig extension for reading PHP class constants and enum cases directly in templates. It exposes a dedicated accessor function so shared values can stay in PHP while remaining easy to use in Twig, without writing custom extensions.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check before you type composer require: this is a Symfony bundle, not a Laravel package. There is no ExtraServiceProvider, no config/twig-constant-accessor.php to publish, and no auto-discovery. The 1.10.0 release ("Add TwigConstantAccessorBundle attribute") does not change that — a PHP attribute is Symfony autoconfiguration metadata, and Laravel has no autoconfiguration pass to read it. If you are on a standard Blade codebase, the honest answer is still: don't install it — read src/ and copy the ~50-line Twig extension into app/Twig/. It is only worth a real install if you are running Twig inside Laravel via rcrowe/twigbridge.

Path A — TwigBridge (the only sane "install" route):

composer require rcrowe/twigbridge
composer require jdecool/twig-constant-accessor-bundle

Then register the extension manually in config/twigbridge.php, because TwigBridge will not pick up a Symfony bundle's DI config or its attributes:

'extensions' => [
    // ...
    \JDecool\TwigConstantAccessorBundle\Twig\ConstantAccessorExtension::class,
],

If the extension's constructor takes the accessor service, bind it in a service provider:

$this->app->singleton(ConstantAccessor::class, fn () => new ConstantAccessor(config('twig-constants.aliases', [])));

Path B — vendored extension: copy the class out of vendor/jdecool/.../src/Twig/, drop it in app/Twig/, and register it in config/twigbridge.php the same way. This is the version you can actually maintain, and it sidesteps the new src/Attribute/ directory entirely.

First use case: kill magic strings in email/mail templates. Status enums are the classic win — instead of {% if order.status == 'shipped' %}, you write {% if order.status == constant('App\\Enums\\OrderStatus::SHIPPED') %} (or the bundle's aliased form, depending on what getFunctions() in src/Twig/ shows). Open src/Twig/*Extension.php first — verify the exact function name and argument order before writing templates; the bundle's whole value-add is over Twig's built-in constant() (namespace aliasing so templates don't carry FQCNs), and the API differs. As of 1.10.0 also skim src/Attribute/ to see what the new attribute actually declares, since the release note says nothing beyond its existence.

Implementation Patterns

Alias your domain classes, don't inline FQCNs. The point of the package is that a template should say constant('OrderStatus::SHIPPED', 'enums'), not carry App\Enums\OrderStatus. Register aliases in a Laravel config file and feed them into the accessor:

// config/twig-constants.php
return [
    'aliases' => [
        'enums'   => 'App\\Enums\\OrderStatus',
        'payment' => 'App\\Enums\\PaymentMethod',
    ],
];

This gives you one place to rename a namespace instead of grepping resources/views.

Treat the new attribute as Symfony-only. The 1.10.0 TwigConstantAccessorBundle attribute is a declaration convenience for container-driven setups: you annotate a class and Symfony's autoconfiguration wires it. Under TwigBridge nothing scans attributes, so the attribute class is inert — do not move your alias map into it, and do not let a Symfony tutorial convince you the config file is obsolete. If the attribute turns out to carry an alias→class mapping, mirror that same shape in config/twig-constants.php so the two mental models match.

Pair it with backed enums (PHP 8.1+) sparingly. For a backed enum, constant('App\\Enums\\OrderStatus::SHIPPED') returns the case object, not the string. If your template compares to a DB column value you want:

{% if order.status == constant('App\\Enums\\OrderStatus::SHIPPED').value %}

If your Twig version supports the enum() function (Twig 3.x), prefer that and skip this package entirely for enum cases — the bundle is really about plain class constants.

Wrap repeated lookups in a domain facade. When a template needs five constants from one class, the pattern that ages well is a single class exposing the curated set:

final class Template
{
    public const SHIPPING_LABEL = 'Standard (3-5 days)';
    public const VAT_RATE       = 0.20;
}

Templates then only need one alias, and you keep the blast radius small.

Testing. Because constants are resolved at render time via the PHP constant() call, a typo blows up at request time, not compile time. Add a smoke test that renders each Twig template which uses the accessor:

$this->get('/orders/1')->assertOk();

Better: a data-driven test asserting every config alias resolves (defined($fqcn.'::'.$name)) — this catches enum renames in CI rather than in production emails.

Migration tip. Add the extension, then convert templates incrementally. Leave a {# TODO: constant #} marker so the hardcoded-literal cleanup is greppable, and replace 'pending'/'paid' literals in the same PR as the enum definition change.

Gotchas and Tips

  • Maintenance status is murky, not fixed. The repo was archived; a 1.10.0 tag then appeared. A single attribute commit is not evidence of a support commitment — re-check the archive banner and composer.json constraints before you plan upgrades, and expect to --ignore-platform-req or vendor the class anyway.
  • The 1.10.0 attribute is a no-op in Laravel. It requires Symfony's DI attribute reader. If you install 1.10.0 expecting config-free setup, you will get an extension that registers with an empty alias map and templates that throw "unknown class". Keep the explicit binding from Path A.
  • Overlap with Twig core. Twig ships a constant() function. If the bundle registers a function with the same name, registration order decides which one wins — and that ordering differs between Symfony's bundle system and TwigBridge's extension list. Explicitly test one template after wiring it up; if you get "unknown class" errors, your extension is being shadowed.
  • Escaping backslashes. In Twig single-quoted strings, App\Enums\Foo is fine (Twig does not process \E), but in double-quoted strings you must write App\\Enums\\Foo. Standardise on single quotes.
  • Constants must be public and the class autoloadable. private/protected constants, interface constants inherited from a parent, and traits work inconsistently — stick to public const on a concrete class.
  • Compile-time caching. If the bundle resolves the constant value during compilation (some accessors do), a changed constant will not appear until php artisan view:clear. Cheap habit: clear view cache after deploying any constant change — and again after bumping 1.9 → 1.10, since the attribute may alter how the extension is constructed in Symfony-style setups.
  • No Laravel package plumbing. No facade, no config merge, no artisan command. Anything you need (config publishing, binding) you write yourself in AppServiceProvider.
  • Extension point: if the accessor takes an alias map, that map is still the only real configuration surface. The new attribute is a second, Symfony-coupled entry point — don't build on it. Everything else — resolution strategy, error messages — is a fork or a subclass of the extension's function. Given the package size, subclassing is cheaper than an upstream issue.
  • Budget check. Tiny star count, no dependents, archived-then-one-commit history. The value is roughly one class plus one attribute you cannot use. If your team spends more than an hour wiring it, stop and inline the constant into the view model instead — {{ order.statusLabel }} from a presenter is usually the better Laravel answer anyway.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings