Skip to content
OpenSmartRoute
Skillv1.0.0

Laravel Toggle

Conventions and APIs for the offload-project/laravel-toggle package — config and database feature toggles, per-flag driver routing, Blade directives, Inertia sharing, and cache management.

by offload-project(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from offload-project/laravel-toggle (skills/SKILL.md). Install upstream with npx skills add offload-project/laravel-toggle --skill skills. Copyright stays with the author.

Context

offload-project/laravel-toggle is a Laravel 11/12/13 package (PHP 8.3+) for global on/off feature flags. It ships:

  • A ToggleManager resolved via the Toggle facade (OffloadProject\Toggle\Facades\Toggle).
  • A Driver contract with three implementations: ConfigDriver (read-only, env-driven), DatabaseDriver (mutable at runtime with config fallback), and PerFlagDriver (routes each flag to the config or database driver based on flags / database_flags lists).
  • A Toggle Eloquent model (OffloadProject\Toggle\Models\Toggle) backing the database driver.
  • Blade directives @toggle, @elsetoggle, @endtoggle.
  • ShareTogglesWithInertia middleware that exposes all flags to the frontend as a flags Inertia prop (keys camelCased).
  • Artisan commands toggle:list, toggle:create, toggle:cache-clear.
  • Exceptions ToggleNotFoundException and ReadOnlyDriverException.

Apply this skill when working in a Laravel app that has offload-project/laravel-toggle in composer.json, or when the user asks for help with Toggle::, @toggle, the Toggle model, feature flags, or driver/cache wiring in this package.

Rules

Calling toggles

  1. Always check toggle state through the Toggle facade (Toggle::active($name) / Toggle::inactive($name)). Do not read config('toggle.flags.foo') or query the toggles table directly — those paths bypass per-flag routing and the cache.
  2. Pass either a string key (kebab-case) or a backed-string enum case. Prefer enums in application code for type safety; reserve raw strings for Blade or quick scripts.
  3. Use Toggle::inactive($name) instead of ! Toggle::active($name) so intent is explicit in diffs.

Enabling / disabling at runtime

  1. Toggle::enable($name), Toggle::disable($name), and Toggle::delete($name) only work when the flag resolves through the database driver. If you call them on a config-only flag, the underlying ConfigDriver throws ReadOnlyDriverException. To make a flag mutable, list it in database_flags (or set the global driver to database).
  2. enable() / disable() / delete() automatically forget the cache entry for that flag. Do not add manual Toggle::forgetCache() calls after them.
  3. Modifying the Toggle Eloquent model directly (e.g. Toggle::updateOrCreate(...)) also clears the cache because the model's saved and deleted hooks call Toggle::forgetCache(). Prefer the facade methods, but the model is safe when you need to seed or bulk-write.

Per-flag driver routing

  1. Put read-only / env-driven flags in config('toggle.flags') with an env() default. These are baked into the deploy.
  2. Put runtime-mutable flag names in config('toggle.database_flags'). Listed flags always resolve through the database driver (with config fallback), regardless of TOGGLE_DRIVER.
  3. Don't list the same flag in both flags and database_flags unless you intend the config value to be the fallback when the database row is missing — database_flags wins for resolution, and flags only contributes the fallback value.
  4. For unlisted flags, the global TOGGLE_DRIVER (config or database) decides which driver handles them. Avoid relying on unlisted flags in production code; declare every flag explicitly.

Defaults for unknown flags

  1. Set TOGGLE_DEFAULT=false (default), true, or exception. Use exception in development to catch typos early; keep production at false (or true for safe-by-default flags).
  2. When TOGGLE_DEFAULT=exception, callers must be prepared for ToggleNotFoundException. Don't wrap every Toggle::active() in try/catch — instead, declare the flag.

Caching

  1. Caching is on by default (TOGGLE_CACHE_ENABLED=true) with a 1-hour TTL. The cache key prefix is toggle:. Leave caching on in production.
  2. If the cache store is unavailable, Toggle::active() still resolves correctly by falling through to the driver — there's no need to wrap calls in defensive try/catch.
  3. After bulk-changing many flags out-of-band (e.g. a seed or migration), call Toggle::flushCache() once rather than forgetCache() per flag.
  4. Tests that exercise toggle changes should either use the array cache driver or call Toggle::flushCache() in beforeEach to avoid cross-test bleed.

Blade directives

  1. Use @toggle('flag-name') ... @elsetoggle ... @endtoggle for template-side conditionals. The directive accepts a string or an enum case (passed via {{ Feature::NewCheckout->value }} or directly if your Blade compiler supports it).
  2. Do not call Toggle::active() inline in Blade unless you need to combine it with another expression — @toggle is the canonical form.

Inertia integration

  1. To expose flags to the frontend, replace HandleInertiaRequests in bootstrap/app.php with ShareTogglesWithInertia (or extend it). The middleware shares all flags as a flags prop with camelCased keys (new-checkoutnewCheckout).
  2. Anything exposed via the Inertia middleware is public to the browser. Do not put sensitive kill-switches (rate-limit bypasses, internal admin features) in the same toggle namespace as user-facing flags; route those through a separate authorization check.

Artisan

  1. Use php artisan toggle:create <name> to scaffold a new config-driven flag. Add --active to default it to true, --db to also create a database row. The command edits config/toggle.php and .env; review the diff before committing.
  2. php artisan toggle:list shows all defined flags and current state — useful for verifying a deploy or debugging which driver a flag is resolving through.
  3. php artisan toggle:cache-clear [name] clears the cache for a specific flag or all flags.

Naming

  1. Use kebab-case for flag names (new-checkout, dark-mode). The Inertia middleware converts these to camelCase for the frontend; mixing snake_case or PascalCase breaks that contract.
  2. Prefix TOGGLE_ env vars and shout-case the rest (TOGGLE_NEW_CHECKOUT). The toggle:create command follows this convention; match it for hand-added flags.

Examples

Defining flags in config

// config/toggle.php

'flags' => [
    // Config-driven, read-only — controlled by .env
    'new-checkout' => env('TOGGLE_NEW_CHECKOUT', false),
    'dark-mode'    => env('TOGGLE_DARK_MODE', true),
],

'database_flags' => [
    // Database-driven, mutable at runtime via Toggle::enable() / Toggle::disable()
    'maintenance-banner',
    'beta-access',
],

Checking a flag

use OffloadProject\Toggle\Facades\Toggle;

if (Toggle::active('new-checkout')) {
    return new NewCheckoutController()->show($request);
}

if (Toggle::inactive('dark-mode')) {
    // Light mode only
}

Using a backed enum

enum Feature: string
{
    case NewCheckout = 'new-checkout';
    case DarkMode    = 'dark-mode';
    case BetaAccess  = 'beta-access';
}

use App\Enums\Feature;
use OffloadProject\Toggle\Facades\Toggle;

if (Toggle::active(Feature::NewCheckout)) {
    // ...
}

Toggle::enable(Feature::BetaAccess);

Blade

@toggle('new-checkout')
    <x-new-checkout-form />
@elsetoggle
    <x-legacy-checkout-form />
@endtoggle

Mutating at runtime (database-routed flag)

use OffloadProject\Toggle\Facades\Toggle;

// 'maintenance-banner' is listed in config('toggle.database_flags')
Toggle::enable('maintenance-banner');
Toggle::disable('maintenance-banner');
Toggle::delete('maintenance-banner'); // removes the row, falls back to config

Sharing flags with Inertia

// bootstrap/app.php
use OffloadProject\Toggle\Middleware\ShareTogglesWithInertia;

->withMiddleware(function (Middleware $middleware) {
    $middleware->web(append: [
        ShareTogglesWithInertia::class,
    ]);
})
// resources/js/Pages/Checkout.vue (or React equivalent)
const {flags} = usePage().props

if (flags.newCheckout) {
    // ...
}

Testing pattern

use OffloadProject\Toggle\Facades\Toggle;

beforeEach(function () {
    config()->set('toggle.driver', 'database');
    Toggle::flushCache();
});

it('shows the new checkout when enabled', function () {
    Toggle::enable('new-checkout');

    $this->get('/checkout')->assertSee('New Checkout');
});

Anti-patterns

  • ❌ Reading config('toggle.flags.foo') directly — bypasses per-flag routing and the cache; database overrides will be ignored.
  • ❌ Querying the toggles table with raw Eloquent in feature code — use the facade so the cache and config fallback stay consistent.
  • ❌ Calling Toggle::enable() / disable() / delete() on a flag that's only in config('toggle.flags')ReadOnlyDriverException will be thrown. Add the flag to database_flags (or switch TOGGLE_DRIVER to database).
  • ❌ Wrapping every Toggle::active() in try/catch. With TOGGLE_DEFAULT set to false or true, undefined flags don't throw; with exception only undefined flags throw and the right fix is to declare the flag.
  • ❌ Manually clearing the cache after Toggle::enable() / disable() — those facade methods already do it.
  • ❌ Putting sensitive admin kill-switches in the same flag namespace as user-facing flags when ShareTogglesWithInertia is enabled — they will leak to the browser.
  • ❌ Subclassing OffloadProject\Toggle\Models\Toggle to add behavior. Use events on the model or a separate service; the model is part of the driver wiring.
  • ❌ Mixing snake_case / camelCase flag names. The Inertia middleware camelCases kebab-case names — anything else produces inconsistent prop keys.
  • ❌ Editing files inside vendor/offload-project/laravel-toggle. All extension points (driver, cache store, default behavior, flags) are exposed via config/toggle.php and the Driver contract.

References

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/offload-project-laravel-toggle-skills/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

offload-project-laravel-toggle-skills.ocm.jsonjson
{
  "ocm": "1",
  "id": "offload-project-laravel-toggle-skills",
  "kind": "skill",
  "name": "Laravel Toggle",
  "description": "Conventions and APIs for the offload-project/laravel-toggle package — config and database feature toggles, per-flag driver routing, Blade directives, Inertia sharing, and cache management.",
  "publisher": "offload-project",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "general"
    ],
    "tags": [
      "skill-md",
      "laravel",
      "php",
      "feature-flags",
      "feature-toggles",
      "inertia",
      "blade",
      "eloquent",
      "github"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Conventions and APIs for the offload-project/laravel-toggle package — config and database feature toggles, per-flag driver routing, Blade directives, Inertia sharing, and cache management."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "github",
      "repository": "https://github.com/offload-project/laravel-toggle",
      "path": "skills/SKILL.md",
      "ref": "093caf222019875bae58d62c68e942e86fa185ba",
      "url": "https://github.com/offload-project/laravel-toggle/blob/093caf222019875bae58d62c68e942e86fa185ba/skills/SKILL.md",
      "key": "offload-project/laravel-toggle/skills/SKILL.md"
    }
  },
  "instructions": "## Context\n\n`offload-project/laravel-toggle` is a Laravel 11/12/13 package (PHP 8.3+) for global on/off feature flags. It ships:\n\n- A `ToggleManager` resolved via the `Toggle` facade (`OffloadProject\\Toggle\\Facades\\Toggle`).\n- A `Driver` contract with three implementations: `ConfigDriver` (read-only, env-driven), `DatabaseDriver` (mutable at runtime with config fallback), and `PerFlagDriver` (routes each flag to the config or database driver based on `flags` / `database_flags` lists).\n- A `Toggle` Eloquent model (`OffloadProject\\Toggle\\Models\\Toggle`) backing the database driver.\n- Blade direc",
  "cost": {
    "context_tokens": 2513
  }
}

Fetch it by URL: GET /api/v1/registry/offload-project-laravel-toggle-skills/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.