├── .editorconfig ├── .styleci.yml ├── LICENSE.md ├── README.md ├── composer.json ├── config ├── .gitkeep └── multisite.php ├── database ├── migrations │ └── 2017_03_01_100000_create_sites_table.php └── seeds │ └── SitesTableSeeder.php └── src ├── Composers ├── CurrentSiteComposer.php └── OverwriteViewComposer.php ├── Middleware └── CurrentSite.php ├── MultisiteServiceProvider.php ├── Site.php └── helpers.php /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_size = 4 9 | indent_style = space 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | 3 | disabled: 4 | - single_class_element_per_statement 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) Appstract 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 13 | > all 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 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Multisite 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/appstract/laravel-multisite.svg?style=flat-square)](https://packagist.org/packages/appstract/laravel-multisite) 4 | [![Total Downloads](https://img.shields.io/packagist/dt/appstract/laravel-multisite.svg?style=flat-square)](https://packagist.org/packages/appstract/laravel-multisite) 5 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) 6 | [![Build Status](https://img.shields.io/travis/appstract/laravel-multisite/master.svg?style=flat-square)](https://travis-ci.org/appstract/laravel-multisite) 7 | 8 | With this package it is possible to build multiple sites/(sub)domains on one codebase. 9 | 10 | ## Installation 11 | 12 | You can install the package via composer: 13 | 14 | ```bash 15 | composer require appstract/laravel-multisite 16 | ``` 17 | 18 | ### Config (hosts, homestead) 19 | 20 | You need to add the sites to your `/etc/hosts` file and `Homestead.yaml`. For example, `mywebsite.dev` and `blog.mywebsite.dev`. In the `Homestead.yaml`, you need to map the sites to the same folder. 21 | 22 | ### Publish 23 | 24 | By running `php artisan vendor:publish --provider="Appstract\Multisite\MultisiteServiceProvider"` in your project all files for multisite will be published. The files that will be published are: a migration, a seeder and a config file. 25 | 26 | ### Seeder 27 | 28 | The seeder will be published but needs to be run when running `php artisan db:seed`, so you need the add `$this->call(SitesTableSeeder::class);` to your `DatabaseSeeder.php` file. After migrating and seeding the sites are now present in the database. 29 | 30 | ## Usage 31 | 32 | This is the main part, within your `routes/web.php` you can set routes for your sites within route groups, like this: 33 | 34 | ```php 35 | Route::group([ 36 | 'domain' => 'blog.'.config('multisite.host'), 37 | 'as' => 'blog.', 38 | 'middleware' => 'site:blog' 39 | ], function () { 40 | Route::get('/', 'BlogController@homepage')->name('homepage'); 41 | }); 42 | ``` 43 | 44 | The magic happens with the site middleware `site:blog`. This will tell your app that the routes within the group are belonging to the blog. It will provide a variable called `$currentSite` in all your views. There is also a config available, which you can access with `Config::get('multisite.site')`. 45 | 46 | ## Testing 47 | 48 | ``` bash 49 | $ composer test 50 | ``` 51 | 52 | ## Contributing 53 | 54 | Contributions are welcome, [thanks to y'all](https://github.com/appstract/laravel-multisite/graphs/contributors) :) 55 | 56 | ## About Appstract 57 | 58 | Appstract is a small team from The Netherlands. We create (open source) tools for webdevelopment and write about related subjects on [Medium](https://medium.com/appstract). You can [follow us on Twitter](https://twitter.com/teamappstract), [buy us a beer](https://www.paypal.me/teamappstract/10) or [support us on Patreon](https://www.patreon.com/appstract). 59 | 60 | ## License 61 | 62 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 63 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "appstract/laravel-multisite", 3 | "description": "Multisite setup for Laravel", 4 | "keywords": [ 5 | "appstract", 6 | "laravel-multisite" 7 | ], 8 | "homepage": "https://github.com/appstract/laravel-multisite", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Gijs Jorissen", 13 | "email": "hello@appstract.team", 14 | "homepage": "https://appstract.team", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=5.6" 20 | }, 21 | "require-dev": { 22 | "phpunit/phpunit": "5.*" 23 | }, 24 | "autoload": { 25 | "files": [ 26 | "src/helpers.php" 27 | ], 28 | "psr-4": { 29 | "Appstract\\Multisite\\": "src" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "Appstract\\Multisite\\Test\\": "tests" 35 | } 36 | }, 37 | "scripts": { 38 | "test": "vendor/bin/phpunit" 39 | }, 40 | "config": { 41 | "sort-packages": true 42 | }, 43 | "extra": { 44 | "laravel": { 45 | "providers": [ 46 | "Appstract\\Multisite\\MultisiteServiceProvider" 47 | ] 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appstract/laravel-multisite/168db6fa4bbbaa032b0fd79167695a3bda815fc8/config/.gitkeep -------------------------------------------------------------------------------- /config/multisite.php: -------------------------------------------------------------------------------- 1 | env('MULTISITE_HOST', 'localhost.dev'), 16 | 17 | /* 18 | |-------------------------------------------------------------------------- 19 | | Site 20 | |-------------------------------------------------------------------------- 21 | | 22 | | The site that needs to be set when nothing is set. 23 | | 24 | */ 25 | 26 | 'site' => env('MULTISITE_SITE'), 27 | 28 | /* 29 | |-------------------------------------------------------------------------- 30 | | Views 31 | |-------------------------------------------------------------------------- 32 | | 33 | | - overwrite: Enable/disable overwrites for views 34 | | - overwriteable: Which views can be overwritten 35 | | - sites: Where are your site views stored (relative to resources/views) 36 | | 37 | */ 38 | 39 | 'views' => [ 40 | 'overwrite' => true, 41 | 'overwriteable' => 'partials.*', 42 | 'sites' => 'sites', 43 | ], 44 | 45 | /* 46 | |-------------------------------------------------------------------------- 47 | | Model 48 | |-------------------------------------------------------------------------- 49 | | 50 | | It is possible to extend \Appstract\Multisite\Site for any purpose, like 51 | | changing database table name or improve functionality. 52 | | 53 | */ 54 | 55 | 'model' => '\\Appstract\\Multisite\\Site', 56 | 57 | ]; 58 | -------------------------------------------------------------------------------- /database/migrations/2017_03_01_100000_create_sites_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name'); 19 | $table->string('slug'); 20 | $table->string('route')->nullable(); 21 | $table->string('url')->nullable(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists('sites'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/seeds/SitesTableSeeder.php: -------------------------------------------------------------------------------- 1 | null, 18 | 'slug' => null, 19 | 'url' => null, 20 | ], 21 | ]; 22 | 23 | foreach ($sites as $site) { 24 | Site::create($site); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Composers/CurrentSiteComposer.php: -------------------------------------------------------------------------------- 1 | site) { 27 | $modelClass = Config::get('multisite.model', Site::class); 28 | 29 | $this->site = call_user_func([$modelClass, 'query']) 30 | ->where('slug', Config::get('multisite.site')) 31 | ->first(); 32 | } 33 | 34 | $view->with('currentSite', $this->site); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Composers/OverwriteViewComposer.php: -------------------------------------------------------------------------------- 1 | currentSite = call_user_func([$modelClass, 'query']) 33 | ->where('slug', Config::get('multisite.site')) 34 | ->first(); 35 | 36 | $this->sitesFolder = Config::get('multisite.views.sites'); 37 | } 38 | 39 | /** 40 | * Bind data to the view. 41 | * 42 | * @param View $view 43 | * @return void 44 | */ 45 | public function compose(View $view) 46 | { 47 | if ($this->overwriteDisabled($view)) { 48 | return $view; 49 | } 50 | 51 | $this->currentPath = collect(explode('/views/', $view->getPath()))->last(); 52 | 53 | if (\View::exists($this->getNewView())) { 54 | $view->setPath($this->getNewPath()); 55 | } 56 | 57 | return $view; 58 | } 59 | 60 | /** 61 | * Get the new path. 62 | * 63 | * @return string 64 | */ 65 | protected function getNewPath() 66 | { 67 | return $this->getAbsolutesitesFolder().DIRECTORY_SEPARATOR.$this->currentSite->slug.DIRECTORY_SEPARATOR.$this->currentPath; 68 | } 69 | 70 | /** 71 | * Get the new view. 72 | * 73 | * @return string 74 | */ 75 | protected function getNewView() 76 | { 77 | if ($this->currentSite) { 78 | return str_replace( 79 | ['.blade.php'], 80 | [''], 81 | $this->sitesFolder.'.'.$this->currentSite->slug.'.'.$this->currentPath 82 | ); 83 | } 84 | 85 | return $this->currentPath; 86 | } 87 | 88 | /** 89 | * Get absolute path to sites folder. 90 | * 91 | * @return string 92 | */ 93 | protected function getAbsolutesitesFolder() 94 | { 95 | return base_path('resources/views'.DIRECTORY_SEPARATOR.$this->sitesFolder); 96 | } 97 | 98 | /** 99 | * Check if overwrites are disabled. 100 | * 101 | * @param View $view 102 | * @return bool 103 | */ 104 | protected function overwriteDisabled($view) 105 | { 106 | return ! Config::get('multisite.views.overwrite') || $view->overwrite === false; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Middleware/CurrentSite.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 19 | $this->publishes([ 20 | __DIR__.'/../database/migrations' => database_path('migrations'), 21 | ], 'migrations'); 22 | 23 | $this->publishes([ 24 | __DIR__.'/../database/seeds' => database_path('seeds'), 25 | ], 'seeds'); 26 | 27 | $this->publishes([ 28 | __DIR__.'/../config/multisite.php' => config_path('multisite.php'), 29 | ], 'config'); 30 | } 31 | 32 | $this->registerMiddleware($router); 33 | 34 | $this->registerComposers(); 35 | } 36 | 37 | /** 38 | * Register the application services. 39 | */ 40 | public function register() 41 | { 42 | // App 43 | $this->app->singleton(\Appstract\Multisite\Composers\CurrentSiteComposer::class); 44 | 45 | // Config 46 | $this->mergeConfigFrom(__DIR__.'/../config/multisite.php', 'multisite'); 47 | } 48 | 49 | /** 50 | * Register middleware. 51 | * 52 | * @param object $router 53 | * @return void 54 | */ 55 | protected function registerMiddleware($router) 56 | { 57 | if (method_exists($router, 'aliasMiddleware')) { 58 | $router->aliasMiddleware('site', CurrentSite::class); 59 | } else { 60 | $router->middleware('site', CurrentSite::class); 61 | } 62 | } 63 | 64 | /** 65 | * Register view composers. 66 | * 67 | * @return void 68 | */ 69 | protected function registerComposers() 70 | { 71 | View::composer( 72 | '*', 73 | \Appstract\Multisite\Composers\CurrentSiteComposer::class 74 | ); 75 | 76 | View::composer( 77 | Config::get('multisite.views.overwriteable'), 78 | \Appstract\Multisite\Composers\OverwriteViewComposer::class 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Site.php: -------------------------------------------------------------------------------- 1 | slug == $slug; 37 | } 38 | 39 | /** 40 | * Get the url. 41 | * 42 | * @param string $value 43 | * @return string 44 | */ 45 | public function getUrlAttribute($value) 46 | { 47 | return $value 48 | ? $value 49 | : route($this->route); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | where('slug', Config::get('multisite.site')) 18 | ->first(); 19 | } 20 | } 21 | --------------------------------------------------------------------------------