├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── asset.php ├── composer.json ├── config └── apps.php └── src ├── AppManager.php ├── AppsServiceProvider.php ├── Facades └── Apps.php ├── MacroRegistrar.php └── helpers.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | ## 0.9.0 (2022-02-20) 4 | 5 | - Add support for Laravel 9 6 | 7 | ## 0.8.0 (2020-12-01) 8 | 9 | - Support PHP 8 10 | 11 | ## 0.7.0 (2020-09-13) 12 | 13 | - Add support for Laravel 8 14 | 15 | ## 0.6.0 (2020-03-04) 16 | 17 | - Add support for Laravel 7 18 | 19 | ## 0.5.0 (2019-09-15) 20 | 21 | - Support Laravel 6 22 | 23 | ## 0.4.0 (2019-04-03) 24 | 25 | - Added support for Laravel 5.7~5.8 26 | 27 | ## 0.3.0 (2018-02-11) 28 | 29 | - Support Laravel 5.6 30 | 31 | ## 0.2.0 (2018-01-27) 32 | 33 | - Added `ids()` 34 | - `routes($attributes)` support Closure 35 | 36 | ## 0.1.0 (2017-12-25) 37 | 38 | - Initial release 39 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2022 Elf Sundae 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Apps 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/elfsundae/laravel-apps.svg?style=flat-square)](https://packagist.org/packages/elfsundae/laravel-apps) 4 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) 5 | [![tests](https://github.com/ElfSundae/laravel-apps/actions/workflows/tests.yml/badge.svg)](https://github.com/ElfSundae/laravel-apps/actions/workflows/tests.yml) 6 | [![StyleCI](https://styleci.io/repos/112607947/shield)](https://styleci.io/repos/112607947) 7 | [![SymfonyInsight Grade](https://img.shields.io/symfony/i/grade/0dca9ec7-57c6-4162-97a5-8d0a3159844d?style=flat-square)](https://insight.symfony.com/projects/0dca9ec7-57c6-4162-97a5-8d0a3159844d) 8 | [![Quality Score](https://img.shields.io/scrutinizer/g/ElfSundae/laravel-apps.svg?style=flat-square)](https://scrutinizer-ci.com/g/ElfSundae/laravel-apps) 9 | [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/ElfSundae/laravel-apps/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/ElfSundae/laravel-apps/?branch=master) 10 | [![Total Downloads](https://img.shields.io/packagist/dt/elfsundae/laravel-apps.svg?style=flat-square)](https://packagist.org/packages/elfsundae/laravel-apps) 11 | 12 | This package provides basic support for Laravel multi-application. 13 | 14 | 15 | 16 | - [Installation](#installation) 17 | - [Configuration](#configuration) 18 | - [Usage](#usage) 19 | - [Obtain Application Manager Instance](#obtain-application-manager-instance) 20 | - [Retrieve Application URL Configuration](#retrieve-application-url-configuration) 21 | - [Determine The Current Application Identifier](#determine-the-current-application-identifier) 22 | - [Selectively Register Service Providers](#selectively-register-service-providers) 23 | - [Define Application Routes](#define-application-routes) 24 | - [Generate URL](#generate-url) 25 | - [Custom Root URL For asset\(\) Helper](#custom-root-url-for-asset-helper) 26 | - [Extend Application Manager](#extend-application-manager) 27 | - [Testing](#testing) 28 | - [License](#license) 29 | 30 | 31 | 32 | ## Installation 33 | 34 | You can install this package using the [Composer](https://getcomposer.org) manager: 35 | 36 | ```sh 37 | $ composer require elfsundae/laravel-apps 38 | ``` 39 | 40 | For earlier Laravel than v5.5, you need to register the service provider manually: 41 | 42 | ```php 43 | ElfSundae\Apps\AppsServiceProvider::class, 44 | ``` 45 | 46 | Then publish the [configuration file](config/apps.php) to your `config` directory: 47 | 48 | ```sh 49 | $ php artisan vendor:publish --tag=laravel-apps 50 | ``` 51 | 52 | ## Configuration 53 | 54 | The `url` configuration option defines the root URL of each application: 55 | 56 | ```php 57 | 'url' => [ 58 | 'web' => 'https://example.com', 59 | 'admin' => 'https://example.com/admin', 60 | 'mobile' => 'https://m.example.com', 61 | 'api' => 'https://api.example.com', 62 | 'assets' => 'https://assets.foo.net', 63 | ], 64 | ``` 65 | 66 | The `providers` array lists the class names of service providers for each application, you may configure this to [selectively register service providers](#selectively-register-service-providers). 67 | 68 | The `config` option may be used to override the default configurations for each application. Additionally, you may wish to put all of your application defaults in one place instead of editing separate configuration files, just put them in the `default` key: 69 | 70 | ```php 71 | 'config' => [ 72 | 73 | 'default' => [ 74 | 'app.timezone' => 'Asia/Shanghai', 75 | 'app.log' => env('APP_LOG', 'daily'), 76 | 'app.log_max_files' => 50, 77 | 'filesystems.disks.public.url' => env('APP_URL_ASSETS', env('APP_URL')).'/storage', 78 | 79 | 'debugbar.options.auth.show_name' => false, 80 | 'debugbar.options.route.label' => false, 81 | ], 82 | 83 | 'admin' => [ 84 | 'auth.defaults' => [ 85 | 'guard' => 'admin', 86 | 'passwords' => 'admin_users', 87 | ], 88 | 'filesystems.default' => 'public', 89 | 'session.domain' => env('SESSION_DOMAIN_ADMIN', null), 90 | ], 91 | 92 | 'api' => [ 93 | 'auth.defaults.guard' => 'api', 94 | 'filesystems.default' => 's3', 95 | ], 96 | 97 | ], 98 | ``` 99 | 100 | ## Usage 101 | 102 | ### Obtain Application Manager Instance 103 | 104 | You may obtain the application manager instance using the `Apps` facade, the `apps()` helper function or injecting the `ElfSundae\Apps\AppManager` dependency. 105 | 106 | ### Retrieve Application URL Configuration 107 | 108 | ```php 109 | use ElfSundae\Apps\Facades\Apps; 110 | 111 | // Get all application URLs 112 | Apps::urls(); 113 | 114 | // Get all application identifiers 115 | apps()->ids(); 116 | 117 | // Get URL root for the assets app 118 | apps()->root('assets'); 119 | 120 | // Get URL domain for the api app 121 | apps()->domain('api'); 122 | 123 | // Get URL prefix for the admin app 124 | apps()->prefix('admin'); 125 | ``` 126 | 127 | ### Determine The Current Application Identifier 128 | 129 | The application identifier to the current request can be determined via the `id` method on the app manager, or using the corresponding `app_id` helper function: 130 | 131 | ```php 132 | $appId = Apps::id(); 133 | 134 | $appId = app_id(); 135 | ``` 136 | 137 | You may also pass arguments to the `id` method to check if the current application identifier matches a given value. The method will return `true` if the identifier matches any of the given values: 138 | 139 | ```php 140 | if (Apps::id('admin')) { 141 | // Currently requesting admin app 142 | } 143 | 144 | if (app_id('web', 'admin')) { 145 | // Currently requesting either web app OR admin app 146 | } 147 | ``` 148 | 149 | ### Selectively Register Service Providers 150 | 151 | Instead of adding all service providers to the `config/app.php` file, you may want to selectively register service providers for certain sub applications to optimize performance. To do so, simply list the providers to the `providers` array in the `config/apps.php` configuration file: 152 | 153 | ```php 154 | 'providers' => [ 155 | 156 | 'admin' => [ 157 | Rap2hpoutre\LaravelLogViewer\LaravelLogViewerServiceProvider::class, 158 | Yajra\DataTables\DataTablesServiceProvider::class, 159 | App\Providers\AdminServiceProvider::class, 160 | ], 161 | 162 | 'api' => [ 163 | App\Providers\ApiServiceProvider::class, 164 | ], 165 | 166 | ], 167 | ``` 168 | 169 | :warning: If your application runs on Laravel 5.5+ which support [package discovery](https://laravel.com/docs/5.5/packages#package-discovery), you also need to disable discovery for the optional packages in the `composer.json` file: 170 | 171 | ```js 172 | "extra": { 173 | "laravel": { 174 | "dont-discover": [ 175 | "rap2hpoutre/laravel-log-viewer", 176 | "yajra/laravel-datatables-oracle" 177 | ] 178 | } 179 | } 180 | ``` 181 | 182 | > Don't worry about the [deferred service providers](https://laravel.com/docs/providers#deferred-providers), as the deferred providers are only loaded when needed. 183 | 184 | ### Define Application Routes 185 | 186 | The `routes` method on the app manager helps you define route group for each application. In general, you will call it in the `map` method of your `RouteServiceProvider`: 187 | 188 | ``` 189 | class RouteServiceProvider extends ServiceProvider 190 | { 191 | protected $namespace = 'App\Http\Controllers'; 192 | 193 | /** 194 | * Define the routes for the application. 195 | * 196 | * @return void 197 | */ 198 | public function map() 199 | { 200 | apps()->routes(); 201 | } 202 | } 203 | ``` 204 | 205 | The route files named with the application identifiers in the `routes` directory will be automatically loaded, such as `routes/web.php`, `routes/admin.php`. 206 | 207 | By default, the `routes` method will assign the existing middleware group named with the application identifier or `web` to the route group, and the namespace applied to your controller routes will be `StudlyCase` of the application identifier. 208 | 209 | For example, `apps()->routes()` is equivalent to: 210 | 211 | ```php 212 | // web: https://example.com 213 | Route::group([ 214 | 'domain' => 'example.com', 215 | 'middleware' => 'web', 216 | 'namespace' => $this->namespace.'\Web', 217 | ], function ($router) { 218 | require base_path('routes/web.php'); 219 | }); 220 | 221 | // api: https://api.example.com 222 | Route::group([ 223 | 'domain' => 'api.example.com', 224 | 'middleware' => 'api', 225 | 'namespace' => $this->namespace.'\Api', 226 | ], function ($router) { 227 | require base_path('routes/api.php'); 228 | }); 229 | 230 | // admin: https://example.com/admin 231 | Route::group([ 232 | 'domain' => 'example.com', 233 | 'prefix' => 'admin', 234 | 'middleware' => 'web', // suppose if the "admin" middleware group does not exist 235 | 'namespace' => $this->namespace.'\Admin', 236 | ], function ($router) { 237 | require base_path('routes/admin.php'); 238 | }); 239 | 240 | // ... 241 | ``` 242 | 243 | Of course, you are free to specify any route attributes: 244 | 245 | ```php 246 | apps()->routes([ 247 | 'web' => [ 248 | 'namespace' => $this->namespace, 249 | ], 250 | 'admin' => [ 251 | 'middleware' => ['web', 'admin.ip'], 252 | 'as' => 'admin.', 253 | 'where' => [ 254 | 'id' => '[0-9]+', 255 | ], 256 | ], 257 | ]); 258 | ``` 259 | 260 | In addition to an array, you can pass a Closure to the `routes` method: 261 | 262 | ```php 263 | apps()->routes(function ($id, $apps) { 264 | return ['as' => $id.'.']; 265 | }); 266 | ``` 267 | 268 | ### Generate URL 269 | 270 | You can use the `url` method or the corresponding `app_url` helper function to generate an absolute URL to a path for a specified application: 271 | 272 | ```php 273 | apps()->url('admin', 'user', [$user]); // https://example.com/admin/user/123 274 | 275 | app_url('api', 'posts'); // https://api.example.com/posts 276 | ``` 277 | 278 | The `asset` method generates a URL with the root URL of the `assets` application: 279 | 280 | ```php 281 | apps()->asset('js/app.js'); // https://assets.foo.net/js/app.js 282 | ``` 283 | 284 | ### Custom Root URL For asset() Helper 285 | 286 | The Laravel built-in `URL::asset` method or the corresponding `asset`, `secure_asset` helper functions are designed to generate URL for the application assets. In most applications, we will probably specify a cookie-free domain or use CDN for the assets, however we can not set custom root URL for these built-in assets methods, and for now there is no elegant way to extend the core `UrlGenerator`. 287 | 288 | You may use `URL::assetFrom`, `Apps::asset`, or a custom helper function to generate assets URLs, but it is awfully boring to replace all `asset()` calls to your own assets method for the third-party packages. Maybe a better workaround is overwriting the built-in `asset` helper: define your `asset` function before including the Composer autoloader file, in your `public/index.php` file: 289 | 290 | ```php 291 | function asset($path, $secure = null) 292 | { 293 | return apps()->asset($path, $secure); 294 | } 295 | 296 | require __DIR__.'/../vendor/autoload.php'; 297 | ``` 298 | 299 | This package ships with an [`asset.php`](asset.php) file you may include to use the root URL of the `assets` application for the `asset()` helper: 300 | 301 | ```php 302 | require __DIR__.'/../vendor/elfsundae/laravel-apps/asset.php'; 303 | 304 | require __DIR__.'/../vendor/autoload.php'; 305 | ``` 306 | 307 | > FYI, related PR [laravel/framework#22372](https://github.com/laravel/framework/pull/22372). 308 | 309 | ### Extend Application Manager 310 | 311 | The [`AppManager`](src/AppManager.php) class is macroable, that means you can use the `macro` method to extend it: 312 | 313 | ```php 314 | Apps::macro('route', function ($name, $parameters = []) { 315 | return URL::route($this->id().'.'.$name, $parameters); 316 | }); 317 | ``` 318 | 319 | ## Testing 320 | 321 | ```sh 322 | $ composer test 323 | ``` 324 | 325 | ## License 326 | 327 | This package is open-sourced software licensed under the [MIT License](LICENSE.md). 328 | -------------------------------------------------------------------------------- /asset.php: -------------------------------------------------------------------------------- 1 | asset($path, $secure); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elfsundae/laravel-apps", 3 | "type": "library", 4 | "description": "Laravel multi-application support.", 5 | "keywords": ["laravel", "multi", "multiple", "application", "assets", "domain"], 6 | "homepage": "https://github.com/ElfSundae/laravel-apps", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Elf Sundae", 11 | "email": "elf.sundae@gmail.com", 12 | "homepage": "https://0x123.com" 13 | } 14 | ], 15 | "require": { 16 | "php": "~7.1|~8.0", 17 | "illuminate/support": "~5.0|~6.0|~7.0|~8.0|~9.0" 18 | }, 19 | "require-dev": { 20 | "mockery/mockery": "~1.0", 21 | "phpunit/phpunit": "~5.7|~6.0|~7.0|~8.0|~9.0", 22 | "orchestra/testbench": "~3.0|~4.0|~5.0|~6.0|~7.0" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "ElfSundae\\Apps\\": "src/" 27 | }, 28 | "files": [ 29 | "src/helpers.php" 30 | ] 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "ElfSundae\\Apps\\Test\\": "tests/" 35 | } 36 | }, 37 | "scripts": { 38 | "test": "vendor/bin/phpunit" 39 | }, 40 | "extra": { 41 | "branch-alias": { 42 | "dev-master": "0.8-dev" 43 | }, 44 | "laravel": { 45 | "providers": [ 46 | "ElfSundae\\Apps\\AppsServiceProvider" 47 | ], 48 | "aliases": { 49 | "Apps": "ElfSundae\\Apps\\Facades\\Apps" 50 | } 51 | } 52 | }, 53 | "minimum-stability": "dev", 54 | "prefer-stable": true 55 | } 56 | -------------------------------------------------------------------------------- /config/apps.php: -------------------------------------------------------------------------------- 1 | [ 15 | 'web' => env('APP_URL'), 16 | 'admin' => env('APP_URL_ADMIN', env('APP_URL')), 17 | 'api' => env('APP_URL_API', env('APP_URL')), 18 | 'assets' => env('APP_URL_ASSETS', env('APP_URL')), 19 | ], 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Service Providers 24 | |-------------------------------------------------------------------------- 25 | | 26 | | The service providers listed here will be automatically registered for 27 | | each application. 28 | | 29 | */ 30 | 31 | 'providers' => [ 32 | 33 | 'admin' => [ 34 | // 35 | ], 36 | 37 | 'api' => [ 38 | // 39 | ], 40 | 41 | ], 42 | 43 | /* 44 | |-------------------------------------------------------------------------- 45 | | Application Configuration 46 | |-------------------------------------------------------------------------- 47 | | 48 | | Here you may override the default configurations for each application. 49 | | 50 | */ 51 | 52 | 'config' => [ 53 | 54 | 'default' => [ 55 | 'app.editor' => env('APP_EDITOR'), 56 | ], 57 | 58 | 'admin' => [ 59 | // 60 | ], 61 | 62 | 'api' => [ 63 | 'auth.defaults.guard' => 'api', 64 | ], 65 | 66 | ], 67 | 68 | ]; 69 | -------------------------------------------------------------------------------- /src/AppManager.php: -------------------------------------------------------------------------------- 1 | container = $container; 37 | 38 | $this->container->rebinding('request', function () { 39 | $this->refreshId(); 40 | }); 41 | } 42 | 43 | /** 44 | * Get all application URLs. 45 | * 46 | * @return array 47 | */ 48 | public function urls() 49 | { 50 | return $this->container['config']->get('apps.url', []); 51 | } 52 | 53 | /** 54 | * Get all application identifiers. 55 | * 56 | * @return array 57 | */ 58 | public function ids() 59 | { 60 | return array_keys($this->urls()); 61 | } 62 | 63 | /** 64 | * Get the root URL for the application identifier. 65 | * 66 | * @param string $app 67 | * @return string 68 | */ 69 | public function root($app = '') 70 | { 71 | return Arr::get($this->urls(), (string) $app) 72 | ?: $this->container['config']['app.url']; 73 | } 74 | 75 | /** 76 | * Get the URL domain for the application identifier. 77 | * 78 | * @param string $app 79 | * @return string 80 | */ 81 | public function domain($app = '') 82 | { 83 | return parse_url($this->root($app), PHP_URL_HOST); 84 | } 85 | 86 | /** 87 | * Get the URL prefix for the application identifier. 88 | * 89 | * @param string $app 90 | * @return string 91 | */ 92 | public function prefix($app = '') 93 | { 94 | return trim(parse_url($this->root($app), PHP_URL_PATH), '/'); 95 | } 96 | 97 | /** 98 | * Get or check the current application identifier. 99 | * 100 | * @return string|bool 101 | */ 102 | public function id() 103 | { 104 | if (is_null($this->appId)) { 105 | $this->appId = (string) $this->idForUrl($this->container['request']->getUri()); 106 | } 107 | 108 | if (func_num_args() > 0) { 109 | return in_array($this->appId, is_array(func_get_arg(0)) ? func_get_arg(0) : func_get_args()); 110 | } 111 | 112 | return $this->appId; 113 | } 114 | 115 | /** 116 | * Get the application identifier for the given URL. 117 | * 118 | * @param string $url 119 | * @return string|null 120 | */ 121 | public function idForUrl($url) 122 | { 123 | return collect($this->urls()) 124 | ->filter(function ($root) use ($url) { 125 | return $this->urlHasRoot($url, $root); 126 | }) 127 | ->sortByDesc(function ($root) { 128 | return strlen($root); 129 | }) 130 | ->keys() 131 | ->first(); 132 | } 133 | 134 | /** 135 | * Refresh the current application identifier. 136 | * 137 | * @return $this 138 | */ 139 | public function refreshId() 140 | { 141 | $this->appId = null; 142 | 143 | return $this; 144 | } 145 | 146 | /** 147 | * Determine if a URL has the given root URL. 148 | * 149 | * @param string $url 150 | * @param string $root 151 | * @param bool $strict 152 | * @return bool 153 | */ 154 | protected function urlHasRoot($url, $root, $strict = false) 155 | { 156 | if (! $strict) { 157 | $url = $this->removeScheme($url); 158 | $root = $this->removeScheme($root); 159 | } 160 | 161 | return (bool) preg_match('~^'.preg_quote($root, '~').'([/\?#].*)?$~i', $url); 162 | } 163 | 164 | /** 165 | * Remove scheme for a URL. 166 | * 167 | * @param string $url 168 | * @return string 169 | */ 170 | protected function removeScheme($url) 171 | { 172 | return preg_replace('#^https?://#i', '', $url); 173 | } 174 | 175 | /** 176 | * Generate an absolute URL to a path for the given application identifier. 177 | * 178 | * @param string $app 179 | * @param string $path 180 | * @param mixed $parameters 181 | * @return string 182 | */ 183 | public function url($app = '', $path = '', $parameters = []) 184 | { 185 | return $this->root($app).$this->stringAfter( 186 | $this->container['url']->to($path, $parameters), 187 | $this->container['url']->to('') 188 | ); 189 | } 190 | 191 | /** 192 | * Generate the URL to an application asset. 193 | * 194 | * @param string $path 195 | * @param bool|null $secure 196 | * @return string 197 | */ 198 | public function asset($path, $secure = null) 199 | { 200 | return $this->container['url']->assetFrom($this->root('assets'), $path, $secure); 201 | } 202 | 203 | /** 204 | * Return the remainder of a string after a given value. 205 | * 206 | * @param string $subject 207 | * @param string $search 208 | * @return string 209 | */ 210 | protected function stringAfter($subject, $search) 211 | { 212 | return $search === '' ? $subject : array_reverse(explode($search, $subject, 2))[0]; 213 | } 214 | 215 | /** 216 | * Register routes for each application. 217 | * 218 | * You may call this method in the `map` method of your `RouteServiceProvider`. 219 | * 220 | * @param array|\Closure $attributes 221 | * @return void 222 | */ 223 | public function routes($attributes = []) 224 | { 225 | if (! $attributes instanceof Closure) { 226 | $attr = $attributes; 227 | $attributes = function ($id) use ($attr) { 228 | return Arr::get($attr, $id, []); 229 | }; 230 | } 231 | 232 | foreach ($this->ids() as $id) { 233 | if (file_exists($file = $this->getRouteFile($id))) { 234 | $this->container['router']->group( 235 | $this->getRouteAttributes($id, $attributes), 236 | $this->getRouteFileLoader($file) 237 | ); 238 | } 239 | } 240 | } 241 | 242 | /** 243 | * Get the route file for the application. 244 | * 245 | * @param string $app 246 | * @return string 247 | */ 248 | protected function getRouteFile($app) 249 | { 250 | return base_path("routes/{$app}.php"); 251 | } 252 | 253 | /** 254 | * Get the route file loader. 255 | * 256 | * @param string $file 257 | * @return \Closure 258 | */ 259 | protected function getRouteFileLoader($file) 260 | { 261 | return function ($router) use ($file) { 262 | require $file; 263 | }; 264 | } 265 | 266 | /** 267 | * Get the route attributes for the application. 268 | * 269 | * @param string $app 270 | * @param array|\Closure $attributes 271 | * @return array 272 | */ 273 | protected function getRouteAttributes($app, $attributes = []) 274 | { 275 | if ($attributes instanceof Closure) { 276 | $attributes = $attributes($app, $this) ?: []; 277 | } 278 | 279 | return array_filter(array_merge( 280 | $this->getDefaultRouteAttributes($app), $attributes 281 | )); 282 | } 283 | 284 | /** 285 | * Get the default route attributes for the application. 286 | * 287 | * @param string $app 288 | * @return array 289 | */ 290 | protected function getDefaultRouteAttributes($app) 291 | { 292 | return [ 293 | 'domain' => $this->domain($app), 294 | 'prefix' => $this->prefix($app), 295 | 'middleware' => $this->container['router']->hasMiddlewareGroup($app) ? $app : 'web', 296 | 'namespace' => $this->getRootControllerNamespace($app), 297 | ]; 298 | } 299 | 300 | /** 301 | * Get the root controller namespace for the application. 302 | * 303 | * @param string $app 304 | * @return string 305 | */ 306 | protected function getRootControllerNamespace($app) 307 | { 308 | $namespace = $this->container['url']->getRootControllerNamespace() 309 | ?: 'App\Http\Controllers'; 310 | 311 | return trim($namespace.'\\'.Str::studly($app), '\\'); 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /src/AppsServiceProvider.php: -------------------------------------------------------------------------------- 1 | registerMacros($this->app); 18 | 19 | if ($this->app->runningInConsole()) { 20 | $this->publishAssets(); 21 | } 22 | } 23 | 24 | /** 25 | * Publish assets from package. 26 | * 27 | * @return void 28 | */ 29 | protected function publishAssets() 30 | { 31 | $this->publishes([ 32 | __DIR__.'/../config/apps.php' => config_path('apps.php'), 33 | ], 'laravel-apps'); 34 | } 35 | 36 | /** 37 | * Register the service provider. 38 | * 39 | * @return void 40 | */ 41 | public function register() 42 | { 43 | $this->setupAssets(); 44 | 45 | $this->registerAppManager(); 46 | 47 | $this->registerConfiguredProviders(); 48 | 49 | $this->setupConfiguration(); 50 | } 51 | 52 | /** 53 | * Setup package assets. 54 | * 55 | * @return void 56 | */ 57 | protected function setupAssets() 58 | { 59 | $this->mergeConfigFrom(__DIR__.'/../config/apps.php', 'apps'); 60 | } 61 | 62 | /** 63 | * Register app manager singleton. 64 | * 65 | * @return void 66 | */ 67 | protected function registerAppManager() 68 | { 69 | $this->app->singleton('apps', function ($app) { 70 | return new AppManager($app); 71 | }); 72 | 73 | $this->app->alias('apps', AppManager::class); 74 | } 75 | 76 | /** 77 | * Register the configured service providers. 78 | * 79 | * @return void 80 | */ 81 | protected function registerConfiguredProviders() 82 | { 83 | $providers = $this->app['config']->get('apps.providers', []); 84 | 85 | if ($this->app->runningInConsole()) { 86 | $providers = array_unique(Arr::flatten($providers)); 87 | } else { 88 | $providers = (array) Arr::get($providers, $this->app['apps']->id()); 89 | } 90 | 91 | foreach ($providers as $p) { 92 | $this->app->register($p); 93 | } 94 | } 95 | 96 | /** 97 | * Setup application configurations. 98 | * 99 | * @return void 100 | */ 101 | protected function setupConfiguration() 102 | { 103 | $booting = $this->app->isBooted() ? 'booted' : 'booting'; 104 | 105 | $this->app->$booting(function ($app) { 106 | $config = $app['config']; 107 | 108 | if (! $app->configurationIsCached()) { 109 | $config->set($config->get('apps.config.default', [])); 110 | } 111 | 112 | if ($appId = $app['apps']->id()) { 113 | $config->set($config->get('apps.config.'.$appId, [])); 114 | } 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Facades/Apps.php: -------------------------------------------------------------------------------- 1 | register( 18 | $container['url'], 19 | 'getRootControllerNamespace', 20 | function () { 21 | /* @var $this \Illuminate\Routing\UrlGenerator */ 22 | return $this->rootNamespace; 23 | } 24 | ); 25 | 26 | $this->register( 27 | $container['router'], 28 | 'hasMiddlewareGroup', 29 | function ($name) { 30 | /* @var $this \Illuminate\Routing\Router */ 31 | return array_key_exists($name, $this->middlewareGroups); 32 | } 33 | ); 34 | } 35 | 36 | /** 37 | * Register a macro to the class. 38 | * 39 | * @param string|object $class 40 | * @param string $method 41 | * @param object|callable $macro 42 | * @return void 43 | */ 44 | public function register($class, $method, $macro) 45 | { 46 | if (! method_exists($class, $method)) { 47 | $class = is_object($class) ? get_class($class) : $class; 48 | 49 | call_user_func([$class, 'macro'], $method, $macro); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | id(...func_get_args()); 24 | } 25 | } 26 | 27 | if (! function_exists('app_url')) { 28 | /** 29 | * Generate an absolute URL to a path for the given application identifier. 30 | * 31 | * @param string $app 32 | * @param string $path 33 | * @param mixed $parameters 34 | * @return string 35 | */ 36 | function app_url($app = '', $path = '', $parameters = []) 37 | { 38 | return app('apps')->url($app, $path, $parameters); 39 | } 40 | } 41 | --------------------------------------------------------------------------------