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

Samples Bundle Laravel Package

runroom/samples-bundle

Runroom Samples Bundle provides a simple Symfony integration for managing sample data, helping teams define reusable examples and load them consistently for development, testing, and demos.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package, so the mental model for a Laravel dev is: it is View::composer('*', ...) plus a composer-style hook that fires on every Twig render instead of only on view() calls. One event, dispatched right before the template is handed to Twig, carrying the template name and the variable bag so listeners can mutate it.

Minimal setup:

composer require runroom/samples-bundle

Then register it in config/bundles.php (Symfony Flex usually does this for you):

Runroom\SamplesBundle\RunroomSamplesBundle::class => ['all' => true],

Look at these first, in this order — the repo is small, so you can read the whole src/ in ten minutes:

  1. The event class (the object carrying getTemplate() / getParameters() or similar accessors).
  2. The Twig decorator/extension that dispatches it — this tells you exactly where in the render lifecycle the hook sits.
  3. The DependencyInjection/ extension and any services.xml/services.yaml, to confirm what is auto-registered and what you must wire yourself.

First use case, the one that actually justifies the dependency: stop repeating the same "chrome" variables in every controller. Instead of injecting SEO metadata, breadcrumbs, a cart count, or a feature-flag object into dozens of actions, register one subscriber and decorate the render:

final class GlobalViewDataSubscriber implements EventSubscriberInterface
{
    public function __construct(private SeoBuilder $seo, private Cart $cart) {}

    public static function getSubscribedEvents(): array
    {
        return [RenderEvent::class => 'onRender'];
    }

    public function onRender(RenderEvent $event): void
    {
        if (str_starts_with($event->getTemplate(), 'admin/')) {
            return;
        }

        $event->setParameter('seo', $this->seo->forCurrentRequest());
        $event->setParameter('cartCount', $this->cart->count());
    }
}

That's the whole value proposition: one central place where view data is composed, decoupled from controllers, testable in isolation.

Implementation Patterns

Guard by template prefix, not by controller. Because the event fires at the rendering layer, it has no idea which route or controller you came from. Filter on getTemplate() ('layout.html.twig', 'emails/', 'partials/') or on a request attribute pulled from the request stack via RequestStack. This is the single most important pattern; unguarded subscribers are how this bundle turns into a footgun.

Layered subscribers over one fat one. Register several small subscribers — SeoSubscriber, NavigationSubscriber, FeatureFlagSubscriber — each with its own template guard. Symfony runs them by priority (default 0, higher first), which lets you guarantee ordering, e.g. the navigation subscriber that populates breadcrumbs runs before the SEO subscriber that consumes them by declaring priority: 10.

Use it over Twig globals when the value is request-dependent. Twig globals: are resolved once at container/extension setup and re-resolved per request but not per template; a global is fine for a static config object, wrong for anything that varies by template ("no cart widget on checkout") or is expensive. The render event is per-render, so it lets you skip work for templates that will never use the variable.

Use it instead of Twig\Extension\AbstractExtension::getGlobals() when you need the template name. This is the genuinely differentiating capability: getGlobals() cannot see which template is rendering. Anything you'd naturally reach for {% include %} context hacks or a {{ render(controller(...)) }} sub-request to solve can often be replaced by a guarded listener here — cheaper, since no sub-request or container round-trip is involved.

Testing. The event object is a plain value object, so unit-test listeners by constructing it with a template name and parameters, calling onRender(), and asserting on the mutated bag. No kernel, no Twig environment, no fixtures. If the bundle's event class is final with no named constructor, use reflection sparingly or test through a functional WebTestCase rendering an actual template.

Integration tip for Laravel-brained code: do not try to reach for request()-style helpers or service location. Wire dependencies in the constructor and keep the listener stateless per request; the event carries only what Twig will see.

Gotchas and Tips

  • Fire frequency is the big unknown — verify it before shipping. If the dispatch point wraps the Twig environment's render(), the event fires for every {% include %}, {% embed %} and macro-bearing partial too, not just top-level controller renders. That means a per-render guard, and it means your subscriber can run dozens of times per page. Measure with a counter in dev before you assume once-per-request semantics.
  • Mutating vs. replacing parameters. Check whether the event exposes setParameter() (merge) or setParameters() (replace). Calling the replace-style setter from two listeners is a classic "why did my SEO object vanish" bug. Prefer the merge-style API, and if only the replace-style exists, always array_merge($event->getParameters(), [...]).
  • Escaping and autoescape are unaffected — you are injecting variables, not HTML, so anything you pass lands in the template's context and is escaped normally. Do not pass Markup/Twig\Markup unless you deliberately want to defeat escaping.
  • No caching layer of its own. Twig's compiled-template cache is irrelevant here because the dispatch happens at runtime, so listener cost is paid on every render. Keep listeners lazy: inject closures/ServiceLocator or defer heavy DB calls behind a flag that the template guard has already narrowed.
  • Low bus factor. Four stars, zero dependents, last release October 2024. Pin the version in composer.json and read the diff before upgrading major Symfony versions — the render lifecycle is an area Symfony has historically churned (kernel.view, Twig\Environment decoration). If the package is a thin decorator, keep your subscribers in your own namespace so you can swap in a hand-rolled 20-line Twig\Extension decorator later with minimal diff.
  • Sub-requests / ESI / fragments. If a render happens inside a sub-request, RequestStack::getMainRequest() and getCurrentRequest() differ; any subscriber that pulls routing or locale data will silently use the fragment's request. Decide explicitly which you want.
  • Extension point to prefer when available: decorating Twig\Environment::render() yourself. If the bundle already does that, don't decorate again — double dispatch is the fastest way to get duplicated variables and doubled listener cost.
  • Debugging: a one-line dump($event->getTemplate()) at the top of each subscriber in dev tells you immediately whether it fires once or thirty times and for which templates. Do this on day one; it answers the question that the README almost certainly doesn't.
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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log