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

Think Multi App Laravel Package

topthink/think-multi-app

topthink/think-multi-app adds multi-application support to ThinkPHP, letting one codebase serve separate apps with independent routing, controllers, and configuration. It enables app-aware URL dispatch and domain binding for cleaner multi-site projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Install it once and treat it as infrastructure — there is nothing to configure for the default case:

composer require topthink/think-multi-app

The service provider is auto-discovered from composer.json (extra.think.services), so no manual registration is needed. It injects a global MultiApp middleware that runs before route matching and rewrites the current app based on the request.

Immediately after installing, the biggest trap is that the package assumes the default single-app files have already been moved. Do one of the following before the first request:

  • Move app/controller, app/model, app/view into app/index/ (and keep app/index/ as the default app), or
  • Delete the root-level app files and let the package scaffold them: php think build index (and php think build admin for a second app).

Check config/app.php and confirm:

'auto_multi_app' => true,
'default_app'    => 'index',
'app_map'        => [],

First use case: a two-app split — public site + admin panel:

app/
├── index/
│   ├── controller/Index.php   → app\index\controller\Index
│   ├── view/
│   └── route/app.php
└── admin/
    ├── controller/Dashboard.php → app\admin\controller\Dashboard
    └── route/app.php

A request to http://site.test/admin/dashboard/index resolves to app/admin/controller/Dashboard::index(). A request to http://site.test/ falls back to default_app (index).

Where to look first:

  • vendor/topthink/think-multi-app/src/MultiApp.php — the parseMultiApp() method is the whole package; read it once and the behavior is transparent.
  • config/app.php — auto_multi_app, default_app, app_map, app_express.
  • route/ and app/<app>/route/ — to understand where routing is loaded from in multi-app mode.

Implementation Patterns

App per bounded context, not per page. The idiomatic layout is one app per delivery surface: index (public), admin (back office), api (JSON), mobile (H5). Each gets its own controllers, views, route files, and middleware, which keeps cross-cutting concerns isolated.

Domain/subdomain binding is the cleanest production pattern. Instead of leaking the app name into every URL, map hosts to apps:

// config/app.php
'app_map' => [
    'www.example.com'   => 'index',
    'admin.example.com' => 'admin',
    'api.example.com'   => 'api',
],

Now https://admin.example.com/dashboard/index hits the admin app without an admin/ URL segment. This is the pattern most day-to-day work should use.

URL generation stays app-aware. Link helpers accept the app name as the first segment:

url('admin/user/edit', ['id' => $id]);   // /admin/user/edit?id=...
url('index/index/index');               // /index/index/index

In practice, either hard-code the app prefix or wrap this in a small helper so templates don't scatter app names.

Per-app routes. Route definitions live inside each app, not at the project root:

// app/admin/route/app.php
Route::group('admin', function () {
    Route::get('dashboard', 'Dashboard/index');
});

Because MultiApp runs before the route middleware, the route loader picks up the already-resolved app and only loads that app's route files. Keep app-specific route files self-contained.

Per-app middleware and hooks. Each app directory can carry its own middleware.php, common.php, and event.php, so auth, CORS, and rate limiting can be scoped to admin or api without polluting the public site. Register shared middleware globally and scope the rest per app.

Shared code goes outside the app folders. Don't reach from admin into index by relative path. Put reusable services, traits, and value objects in:

  • a dedicated app/common/ app, or
  • extend/, or
  • a proper Composer package.

Dependency direction should be: apps → shared, never app → app.

Namespacing is automatic. app/admin/controller/User.php must declare namespace app\admin\controller;. Composer's PSR-4 mapping for app\ → app/ handles the rest; no per-app autoload entries needed.

Short URLs with app_express. When enabled, a single-segment URL like /admin can be interpreted as an app + default controller/action, letting you expose /admin instead of /admin/index/index. Useful for pretty landing URLs per app; disable it if you want the full app/controller/action shape enforced.

Scaffolding a new app is a one-liner when the command is available (php think list will confirm it): php think build api creates app/api/ with the standard skeleton.

Gotchas and Tips

  • You must move app/controller after install. If you skip the restructure, requests 404 because the framework looks for app/<first-segment>/controller/... and finds nothing. This is the #1 install problem.
  • The first URL segment is reserved. Any route, controller, or static path whose first segment collides with an app name (e.g. a route /admin/settings) will be hijacked by the app resolver. When in doubt, use app_map with domains so the app name never appears in the path.
  • app_map matches the host exactly. admin.example.com and www.admin.example.com are different keys; ports and www. are not stripped automatically. Normalize the Host header or list every variant.
  • default_app only applies when no app segment is present. A mistyped app name is not silently routed to the default — it produces a 404. That's usually correct behavior, but it confuses people debugging a typo.
  • Case sensitivity bites on Linux. Class Index → Index.php, app admin → app/admin/. A Admin/ folder with namespace App\Admin will fail in production after passing on macOS.
  • Routes from the project root are effectively ignored in multi-app mode. Route definitions load from app/<app>/route/. If your app-specific app.php isn't being read, verify the path, not the route syntax.
  • Global route files won't preempt app resolution. MultiApp runs before the route middleware, so app selection happens first. If you need a URL handled before app dispatch (health checks, redirects), put it in web-server config or a very early middleware, not a route.
  • Debugging: enable app debug (app_debug => true) and log the resolved app name inside a temporary middleware placed after MultiApp. When the resolution looks wrong, it's almost always app_map, auto_multi_app, or a missing default_app.
  • Disable it when you need custom logic. Set 'auto_multi_app' => false and resolve the app yourself (e.g. from an X-App header or API version prefix). The middleware then does nothing, and you get full control — this is the documented extension point for header-based or version-based dispatch.
  • Keep the document root on public/. Multi-app makes app/<app>/ directories tempting to URL-map directly; doing so exposes source, config, and .env files.
  • Clear caches when renaming apps. After moving or renaming an app directory, run php think clear (and any route/schema cache commands you use). Stale compiled config is a common "I moved it and it still 404s" cause.
  • Small ecosystem, stable core. With ~20 stars this package has very little community Q&A, so expect to read the ~150-line MultiApp.php source for edge cases rather than search for answers. It is maintained by top-think alongside the framework itself, so it's reliable — just lean on framework docs, and treat the middleware file as the reference implementation.
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