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

Laravel Features Laravel Package

edalzell/laravel-features

A lightweight feature flag package for Laravel. Define named features in config, toggle them per environment or user, and check availability anywhere in your app with a simple API—ideal for gradual rollouts, beta access, and shipping code without new deploys.

View on GitHub
Deep Wiki
Context7

Getting Started

This package lets you carve a Laravel application into self-contained "features" — each feature is a class that owns its own routes, commands, translations, migrations, seeders, and dependencies, and the package wires them into the framework for you. As of v1.2.0 you can also hook extensions into a feature's boot, which is the first supported way to add behaviour to a feature without subclassing it.

  1. Install and publish config:
    composer require edalzell/laravel-features
    php artisan vendor:publish --tag=features-config
    
  2. Scaffold or hand-write your first feature class (typically app/Features/Billing/BillingFeature.php or similar, depending on the configured namespace).
  3. Register the feature in config/features.php (or let auto-discovery pick it up if enabled).
  4. Verify it boots: php artisan about and any feature-listing artisan command the package exposes, then php artisan route:list to confirm your feature's routes appear.

Where to look first: the feature base class/stub and the published config file. The config tells you which directories are scanned, how namespaces are derived, whether discovery is on, and which disk the package uses. The base class tells you the exact method names the package will call (routes(), commands(), translations(), migrations(), seeders(), dependencies()) and — new in v1.2.0 — where the boot-extension hook lives.

First use case: pick one existing slab of your app — a billing module with a handful of routes, a console command, and a few lang files — and move it into a feature. That exercises routes, commands, and translations in one pass, which is the fastest way to validate your config before you migrate anything else.

Upgrading from v1.1.x: v1.2.0 is additive, but the maintainer's review pass touched the registry, discovery, route groups, and disk handling. Re-run your route list and your seeders after upgrading and delete any workarounds you'd added for those areas before assuming they're still needed.

Implementation Patterns

Feature as the single entry point. Treat the feature class as a miniature service provider. Everything the feature needs the app to know about is declared in one place:

class BillingFeature extends Feature
{
    public function commands(): array
    {
        return [SyncInvoicesCommand::class, PruneDraftsCommand::class];
    }

    public function translations(): array
    {
        return ['billing' => __DIR__.'/resources/lang'];
    }

    public function routes(): array
    {
        return [
            'web' => [
                'prefix' => 'billing',
                'middleware' => ['web', 'auth'],
                'routes' => __DIR__.'/routes/web.php',
            ],
        ];
    }
}

Boot extensions (new in v1.2.0). The headline feature of this release: you can hook extensions into a feature's boot rather than only being able to add behaviour by editing or subclassing the feature class. The pattern is to keep the feature itself declarative — routes, commands, translations — and push cross-cutting boot-time behaviour (extra view composers, model observers, gates, middleware tweaks) into a small extension class registered from the feature. Check the base class for the exact hook signature; the important day-to-day win is that a package or a sibling module can contribute boot behaviour to a feature without you forking it.

// Sketch — confirm the exact hook name/signature against the base class.
class BillingFeature extends Feature
{
    public function extensions(): array
    {
        return [RegisterBillingGates::class, AttachInvoiceObserver::class];
    }
}

Treat extensions as the preferred seam for anything that would otherwise be a boot() body full of if (class_exists(...)) checks.

Routes as closures (new in v1.1.0). A route group can point at a file or, since v1.1.0, inline the definition. Handy for small features or a couple of endpoints that don't justify their own file:

public function routes(): array
{
    return [
        'web' => [
            'prefix' => 'billing',
            'middleware' => ['web'],
            'routes' => function (Router $router) {
                $router->get('/invoices', [InvoiceController::class, 'index']);
                $router->post('/invoices/{invoice}/pay', [InvoiceController::class, 'pay']);
            },
        ],
    ];
}

Use files for anything sizeable — closures are convenient but they're not route-cache friendly in the same way a file is, and they hide routes from a quick grep. Route-group handling was reworked in v1.2.0, so if you'd built a workaround around group prefixes or middleware, re-check it before keeping it.

Translations (new in v1.1.0). Declare one or more lang directories and reference them with the standard Laravel namespace syntax: __('billing::messages.paid'). Keep the key in the array aligned with the namespace you want to expose, and keep each feature's namespace distinct to avoid collisions.

Console commands (new in v1.1.0). Return command class names from commands(); Laravel resolves them from the container, so constructor injection works as normal. This is the cleanest place for feature-scoped scheduled jobs and maintenance commands.

Seeder discovery with ::class (fixed in v1.1.0). Seeders that reference another seeder via OtherSeeder::class inside $this->call([...]) are now discovered correctly. If you'd previously worked around this by using string class names, you can restore the ::class form — it's more refactor-safe.

Dependencies. Declare feature-to-feature dependencies in the feature itself (see the package docs on where these go — v1.1.0 clarified the location). Use this rather than relying on incidental load order; it makes it obvious when feature B needs feature A's migrations to have run first.

Day-to-day workflow. Keep the boundary honest: if a controller, model, or command only serves one feature, it belongs inside that feature's directory. Shared primitives go in app/Support or similar, not in a feature — otherwise you'll end up with features importing each other's internals, and the dependency declaration becomes a lie.

Gotchas and Tips

  • Route caching. After adding routes, closure-based groups especially, run php artisan route:clear && php artisan route:cache and confirm the app still routes. A feature that works in dev and 404s in production is almost always a stale route cache — and because route-group handling changed in v1.2.0, clear the cache immediately after upgrading before you debug anything else.
  • Re-test after the v1.2.0 review pass. Registry, discovery, route groups, and disk were all touched. If you had a workaround in any of those areas — a manual registration because discovery missed a feature, a wrapper around route groups, a hardcoded disk — remove it and confirm the stock behaviour covers you. Carrying old workarounds forward is how you end up with double-registered features.
  • Disk configuration. Confirm which disk the package publishes/reads from and that it exists in config/filesystems.php. A missing or misnamed disk surfaces as an odd "nothing loaded" failure rather than a clear error, so check it early when debugging.
  • Translation namespaces must be unique. If two features declare the same namespace, the later registration silently wins. Namespace by feature, not by generic words like messages or admin.
  • Command name collisions. Two features registering a command with the same $signature will produce confusing behaviour — one shadows the other. Namespace your signatures (billing:sync-invoices).
  • Seeder ordering. ::class discovery now works, but it does not guarantee ordering across features. Anything order-sensitive should be expressed through the dependency declaration, not through hoping discovery runs in a particular sequence.
  • Dependency declarations and discovery. If discovery is enabled, a feature can be loaded before you expect. Don't put side effects in register() that depend on another feature having booted; use boot() — or a boot extension — plus the dependency list.
  • Config caching. Changes to config/features.php won't apply under config:cache. Remember php artisan config:clear when adding a feature or changing discovery paths — this trips people up constantly.
  • Debugging a feature that "isn't loading." In order: (1) is it listed in the config or discoverable from the configured path? (2) does the class extend the correct base class? (3) is the file autoloadable — composer dump-autoload after moving classes into a new namespace. Nine times out of ten it's the autoloader.
  • Extension points. The feature base class is the intended seam: override the lifecycle methods there, and since v1.2.0 prefer the boot-extension hook for anything that isn't core to the feature's declaration. If you need behaviour the base class genuinely doesn't offer, a small abstract base of your own that all your features extend is still reasonable — but reach for the extension hook first, since it's supported and won't fight the package's internals.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager