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 Apps Laravel Package

elfsundae/laravel-apps

elfsundae/laravel-apps lets a single Laravel installation host multiple self-contained applications, each with its own routes, controllers, views, and config. It simplifies modular monoliths and multi-app projects while sharing framework and vendor code.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require elfsundae/laravel-apps
php artisan vendor:publish --provider="Elfsundae\LaravelApps\AppsServiceProvider"

If the service provider isn't auto-discovered, register it in config/app.php (or bootstrap/providers.php on Laravel 11+). The package ships a config file (commonly config/apps.php) — this is the first file to read. It defines the list of applications, where each one's routes/config/providers live, and how the current app is resolved.

Where to look first

  1. The published config — this is the contract for everything else.
  2. The package's service provider — shows the boot order (config merge, route registration, provider loading).
  3. The package README's example app — copy it wholesale for your first app rather than inventing a structure.

First use case: split admin from public site

The canonical scenario is one codebase serving two surfaces with different routes, providers, and configs:

app/                 # shared code (models, services, traits)
apps/
  web/
    routes/web.php
    Providers/WebServiceProvider.php
    config/
  admin/
    routes/web.php
    Providers/AdminServiceProvider.php
    config/

Register both in config/apps.php, point each at its routes and providers, then resolve by domain or path prefix (admin.example.com vs www.example.com). Once you can php artisan route:list and see both app route sets, you're wired up correctly.

Implementation Patterns

Domain/path-based app resolution

The dominant pattern is resolving the app during bootstrap from the request host or path prefix. Keep the resolution logic declarative in config so it's cacheable and greppable:

'apps' => [
    'web'   => ['domain' => 'www.example.com',  'routes' => 'apps/web/routes'],
    'admin' => ['domain' => 'admin.example.com','routes' => 'apps/admin/routes'],
],

Avoid closures in the config if you plan to config:cache — that's a common footgun in this family of packages.

Per-app service providers

Treat each app's provider as its composition root: bind app-specific interfaces, register app-specific event listeners, and load app-specific config. Shared providers stay in config/app.php; app providers get loaded by the package and should not assume other apps' providers ran.

Config layering

Merge order typically is: package defaults → config/apps.php → app-specific config/*.php. If you need to override a single key (e.g., session.cookie per app), do it in the app's config directory rather than AppServiceProvider::boot() — it keeps the override visible next to the app it belongs to.

Shared vs. app-scoped code

  • Shared: models, domain services, DTOs, traits, base controllers.
  • App-scoped: routes, controllers (thin), middleware, view composers, Blade templates, translations.
  • Anything with a name collision (two HomeControllers) belongs app-scoped; use namespaces per app.

Routes and middleware

Group all app routes under the app's middleware stack, and prefer route-name prefixes (admin.) to avoid collisions when both apps register a dashboard route. Use separate auth guards per app — sharing a session cookie across admin and public surfaces is the most common source of "logged in as wrong user" bugs.

Migrations and seeders

Run app-scoped migrations with --path=apps/{slug}/database/migrations. Keep shared tables in the root database/migrations and namespace app tables with a prefix so a single database can host both apps without ambiguity.

Testing

Add a test bootstrap helper that spins up each app explicitly (set the host header or the resolution key) so feature tests don't accidentally exercise the wrong app. One Pest/PHPUnit dataset per app keeps the matrix small.

Gotchas and Tips

Config caching kills dynamic app resolution. If app definitions contain closures or read from env() at runtime, php artisan config:cache will break them. Resolve the app in a bootstrapper/middleware, keep the config file pure arrays.

Route caching and multiple apps. route:cache in Laravel expects a single route set. If the package registers all apps' routes up front, caching works but the cache is app-agnostic; if routes are registered conditionally by resolved app, route:cache will freeze whichever app you cached from. Verify behavior on your version before deploying to production, and prefer clearing/rebuilding route cache in your deploy script.

Service provider ordering. App providers run after the framework's; don't rely on config() being final inside register(). Defer anything config-dependent to boot().

Autoloading collisions. Two apps with App\Http\Controllers\... namespaces will collide. Give each app a distinct PSR-4 prefix in composer.json (e.g., Apps\Admin\) and composer dump-autoload after adding a new app — the most common "class not found" after scaffolding a new app.

Artisan commands are global unless scoped. make:controller won't know which app you mean. Either pass full paths, or override the generator stubs per app directory. Confirm which make:* commands the package patches before assuming app-awareness.

Debugging the resolved app. Log the resolved app key early (a one-liner in a middleware or a dd() in the bootstrapper) whenever routes or configs look wrong. Nine times out of ten the resolution rule matched a host you didn't expect — local dev on localhost:8000 often falls through to a default.

Sessions, cookies, and cache prefixes. Each app should have distinct session.cookie, cache.prefix, and queue names. Two apps sharing a cookie name will fight over sessions; two apps sharing a cache prefix will serve each other's cached fragments.

Extension points. The package's service provider and bootstrapper are the intended place to hook custom resolution logic (e.g., subdomain → tenant mapping). If you need behavior the config doesn't express, extend the package's resolver rather than forking — check the README's "extending" section for the exact class name on your installed version.

Version pinning. At 84 stars and a 0.95 score, the package is small and slow-moving, and Laravel's bootstrap (especially 11+) shifted service-provider registration. Pin a version in composer.json and re-test the bootstrap path on every Laravel minor upgrade.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console