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 Module Loader Laravel Package

zonneplan/laravel-module-loader

Laravel Module Loader helps structure large Laravel applications as self-contained modules. It discovers modules, registers their service providers, routes, configuration and migrations, and lets you enable or disable modules per environment for cleaner, more maintainable codebases.

View on GitHub
Deep Wiki
Context7

Getting Started

v7.1.1 is an additive patch: it introduces a sampling rate for module profiling. There are no breaking changes, so you can bump the constraint (composer update zonneplan/laravel-module-loader) and, if you already use the profiler, optionally tune the new sample rate. The only thing worth checking after upgrading is the published config stub — re-publish or diff config/module-loader.php (or your equivalent) to pick up the new profiler key rather than hand-guessing its name.

Minimal setup for a first-time user:

  1. composer require zonneplan/laravel-module-loader
  2. Publish the config: php artisan vendor:publish --tag="module-loader-config" (check the provider's tag list if this one 404s).
  3. Create your first module directory alongside the configured modules path (commonly app-modules/ or modules/) with a service provider that extends the package's base module provider.
  4. Confirm discovery with whatever listing command your version ships (e.g. php artisan module:list), then run php artisan about / boot the app and check the module's routes, migrations and views are picked up.

First use case: take one cohesive feature already living in app/ (Billing, Notifications, an internal admin area) and move it into a module — routes, migrations, views, config and its service provider — without changing its public namespace if the package allows namespace mapping. That gives you a real end-to-end validation of discovery, migrations and route loading before you commit to splitting more.

Where to look first:

  • The published config file — it defines the modules path, cache/manifest behaviour, and now the profiler sampling setting.
  • The package's base ModuleServiceProvider (or equivalent) — this is the contract that decides what is auto-discovered by convention.
  • The profiler service and whatever output sink it writes to (log channel, cache store, or debug surface) — that's where the new sampling rate takes effect.

Implementation Patterns

Convention-first module providers. The highest-leverage pattern is letting each module's provider extend the package base class and lean on convention for routes, migrations, views, translations, config and commands. You write the wiring once per module and get consistency for free; resist overriding every path manually, because that's the code that rots when the package bumps a minor.

Manifest/config caching in the deploy pipeline. Treat the module manifest the same way you treat config:cache and route:cache: build it during deploy, clear it on rollback. A repeatable deploy step of "clear, then warm" removes an entire class of "works locally, module missing in staging" incidents.

Feature-extraction workflow. Move one feature per PR. Keep the namespace stable across the move so use statements and container bindings don't churn, and keep the module's own composer.json/autoload entry honest if it has one. Extract, deploy, verify, then extract the next.

Profiling as a two-tier practice.

  • Locally / in CI: sample rate at 1.0 (or profiling simply on) so every request is measured while you're actively optimising module boot time.
  • In staging and production: a low sample rate (a few percent, or whatever fraction you can afford) so you get a statistically useful picture of slow modules over hours without paying the instrumentation cost on every request. This is exactly the knob v7.1.1 adds — wire it to an env var, and keep it in your config so it survives config:cache.

Do the profiling work after you've moved code into modules, not before — the point of the profiler is finding which module's register()/boot() is expensive, and that signal only exists once the module boundaries do.

Sampling is not a substitute for measurement discipline. Decide what you're comparing (before/after a refactor, or module A vs module B) and hold the sample rate constant during the comparison window. A 2% sample compared against a 100% sample tells you nothing.

Testing. Point module tests at the module's own namespace and keep factories/seeders inside the module. If the package caches discovery, make sure your test bootstrap clears it — a stale manifest in CI produces failures that only reproduce on a colleague's machine.

Integration with Octane / long-running workers. Anything that resolves modules at boot should be resolved once and cached, not per request; verify module registration happens in the worker bootstrap path, not on every job.

Gotchas and Tips

Config caching freezes your sample rate. Once config:cache runs, env() calls inside the package config return null. Declare the sampling setting as env('MODULE_PROFILER_SAMPLE_RATE', 0.0) in the config file only and never read env() from module providers. This is the single most common cause of "the profiler is on in staging but off in production".

Know what 0 and 1 mean. Sample rate 0.0 should mean off; 1.0 should mean every request. If you're seeing zero profiler records after enabling it, you've almost certainly got the value as the string "0", or the profiler enabled flag is still false. Check both keys before assuming the release is broken.

Sampling hides the outliers you're hunting for. A 1% sample will miss the once-an-hour request that takes 800ms to boot a module. If you're chasing a rare slow path, temporarily raise the rate (or run with the profiler fully on) in an environment where the overhead is acceptable — sampling is for trend data, not for reproducing a specific incident.

Instrumentation cost is real. Profiling writes somewhere: log lines, cache entries, a metrics buffer. At high sample rates with a chatty log channel you can double your log volume and introduce lock contention on the cache store. Give the profiler its own channel/store, and default the sample rate to off in the shipped config so a fresh install never surprises anyone.

Stale manifest after adding a module. Symptoms: a new module's routes 404 and its provider never runs, but nothing errors. The cache was built before the module existed. Rebuild/clear the manifest as part of your deploy and after every git pull that touches the modules directory.

New module directories and Composer's autoloader. If the package relies on PSR-4 entries for each module, a freshly created module directory won't autoload until composer dump-autoload. If the loader handles discovery itself, make sure it isn't fighting an authoritative classmap (--classmap-authoritative in production will not see new files until re-dumped).

Migrations need an explicit path. php artisan migrate only walks database/migrations by default; module migrations usually need --path=... per module or a package-level registration hook. Same story for migrate:fresh in tests.

Route caching vs. module routes. Cached routes are serialised closures-free data; module routes registered after the cache is built won't appear. Clear route cache whenever module route files change, and check for duplicate route name collisions when two modules both use index/show.

Debugging a slow boot. Use the profiler to split register() from boot() — a slow register() usually means container bindings doing I/O (reading config files, hitting the DB in a binding) and a slow boot() usually means route registration or eager service resolution. Fix accordingly: bindings should be lazy closures.

Extension points worth knowing. The module repository/manifest resolver and the profiler's output sink are the two seams most teams end up customising — swapping the sink for your metrics backend is far cleaner than post-processing log lines. Check the package's published interfaces before subclassing anything deep in the loader internals, which are more likely to churn across majors.

Pin sensibly. 7.1.1 is safe to adopt (^7.1), but read the changelog on every 7.x bump that touches the profiler — instrumentation changes have a habit of renaming env keys without a deprecation notice.

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.
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
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels