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

Toggle Bundle Laravel Package

landingi/toggle-bundle

A Symfony bundle for managing feature toggles, letting you enable or disable application features through configuration without changing code. Useful for gradual rollouts, A/B testing, and safely shipping work in progress.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register

composer require landingi/toggle-bundle:^1.0

v1.0.0 is the first tagged release ("Initial release"), so there's no 0.x migration to worry about — but also no upgrade history to learn from. Pin ^1.0 and read composer.json constraints before you commit; that file, not the changelog, is your compatibility answer.

This bundle isn't on a Flex recipe, so you'll almost certainly register it yourself:

// config/bundles.php
return [
    // ...
    Landingi\ToggleBundle\ToggleBundle::class => ['all' => true],
];

Where to look first

The release notes contain exactly four words, so source and tests are the documentation. Read in this order:

  1. composer.json — the first thing to check on a brand-new 1.0.0: which Symfony and PHP versions it declares. A fresh tag usually means modern constraints, but "initial release" may also mean "first public tag of a long-internal package."
  2. src/ — find the main service and its interface (typically something like ToggleInterface / FeatureChecker with an isEnabled()/isActive() method). This is the contract you'll inject.
  3. DependencyInjection/Configuration.php — the real source of truth for config keys: default provider, cache settings, account resolution, storage.
  4. tests/ — the most reliable spec of behaviour, especially edge cases like "what happens when no account is passed."
  5. README.md — useful for the intended mental model, but with a 1.0.0 there's no guarantee the examples have been exercised against current Symfony releases.

First use case

The core job is "is feature X on for account Y?" — inject the checker and call it:

final class DashboardController extends AbstractController
{
    public function __construct(private ToggleInterface $toggle) {}

    public function __invoke(Account $account): Response
    {
        if ($this->toggle->isEnabled('new_dashboard', $account)) {
            return $this->render('dashboard/new.html.twig');
        }
        return $this->render('dashboard/legacy.html.twig');
    }
}

Then configure or seed whatever provider the bundle ships (array/config-driven or account-backed) so new_dashboard resolves for that account. Start with a single toggle in one controller; once that path works end-to-end, spread it outward.

Implementation Patterns

Inject the checker, never $container->get(). Treat the toggle service as a first-class collaborator so it's mockable and so you can swap implementations later.

Resolve account context once, near the edge. Whether the account comes from TokenStorage, a request attribute, or a subdomain, centralise that lookup (e.g. an AccountContext service) and pass the account explicitly into toggle checks. Avoid burying $security->getUser() inside every caller.

Hide the vendor behind your own interface.

interface FeatureFlags
{
    public function enabled(string $feature, ?Account $account = null): bool;
}

A wrapper pays off twice here: it isolates call sites from a 1.0.0 API that may still churn in 1.x minors, and it lets you migrate to a maintained flag system later (or a DB table) without touching call sites.

Feature names as constants or a backed enum, not magic strings.

enum Feature: string {
    case NewDashboard = 'new_dashboard';
    case BulkExport   = 'bulk_export';
}

This catches typos at static-analysis time and gives you a single place to grep for usage when a flag is retired.

Integration points to reach for

  • Twig: register a function like feature_enabled('bulk_export') (via a Twig extension delegating to your adapter) so templates can branch without controllers passing booleans.
  • Symfony Voters: wrap a feature check in a FeatureVoter so denyAccessUnlessGranted('FEATURE_BULK_EXPORT', $account) works uniformly across controllers, services and templates.
  • Event listeners / middleware: gate behaviour (e.g. analytics, throttling) with a check early in the request lifecycle.
  • Console commands & workers: pass the account explicitly — there's no HTTP context to auto-resolve from.

Provider/decorator for performance. Feature lookups happen on hot paths. If the bundle exposes a provider abstraction, decorate it with a request-scoped or PSR-6 cache so repeated isEnabled() calls in one request don't hit storage. Keep the decorator in your app, not in the vendor.

Testing pattern. In unit tests, replace the toggle service with a stub whose isEnabled() returns a fixed value per feature name; run each test scenario twice (on/off) to exercise both branches. In integration tests, seed the provider from config fixtures rather than toggling global state, and reset any in-memory cache between tests. Because the package is newly tagged, treat your own tests as the regression suite — there's no dependents' worth of battle-testing behind it.

Gotchas and Tips

The changelog is "Initial release" — and that's all you get. No feature list, no BC policy, no deprecation timeline. Assume the public API is what src/ says today, not what the README implies, and review 1.x minor diffs before upgrading in a live app.

Compatibility: verify, don't assume. The earlier "2021, Symfony 4/5-era" concern no longer applies — a fresh 1.0.0 normally ships current Symfony/PHP constraints. But "initial release" can equally be a first public tag of an internal bundle written years ago, so open composer.json and check before you plan the integration. If constraints exclude your Symfony version, either pin, vendor the bundle into your source tree, or fork it — with no dependents you won't get community patches for free.

Manual registration is expected. No Flex recipe means no auto-config, no default config file. Copy the bundle's documented sample config (often config/packages/toggle.yaml) and verify the root key matches the bundle's Configuration.php — a wrong key silently ignores your settings and falls back to defaults.

Account-null behaviour is undefined until you read the code. If no account is passed, does the check return false, throw, or use a global default? Establish this explicitly and encode it in your adapter, because "feature on for everyone when account is null" is a classic production surprise.

Cache invalidation will bite you. If the bundle or your decorator caches toggle results (especially per-request, per-account), a flag flipped in the DB may not take effect until the cache is cleared. Either add a cache warmer/clear step to your deployment, or use a TTL short enough that flips propagate within an acceptable window. Log flag misses (unknown feature names) so you can spot stale config after deploys.

Watch for silent defaults on unknown features. Many flag systems return false for an unknown name (safe) but some return true or throw. Wrap this in your adapter and decide consciously — default-off is almost always the right call for isEnabled.

Debugging tips. Add a console command that dumps the resolved value of every known feature for a given account (bypassing your cache). In development, log each isEnabled() call with the feature name and result; it's the fastest way to understand why a branch isn't firing. A Symfony profiler data collector (if you want to invest the time) gives per-request visibility.

Extension points are your escape hatch. If the bundle's provider abstraction is public, add a custom provider (DB, Redis, LaunchDarkly-style API) rather than editing vendor code. If the abstraction is too thin, fork — this package's size makes a fork cheap. Since 1.0.0 just landed, upstreaming a documented custom provider/README example is a realistic first contribution and cheaper than maintaining a fork.

Cleanup discipline. Flags accumulate. Track owners and expiry dates for each toggle (even in a comment alongside the constant), and treat flag removal as a task on the same ticket, not a follow-up. With a package this niche, future maintainers will thank you for not leaving dead isEnabled() branches scattered through the codebase.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package