The content you're looking at is not published yet!

DRAFT

[Laravel Multi-Tenant] Routing for multi-domain app

This article is part of a series. The first part is explaining the details of the app we're building, please read it before going further. As a reminder, this is the 3 parts of the apps and the domain we use locally to access them.

App part Domain
Marketing site eventlist.test
Public API api.eventlist.test
User sites parisrockshows.test,...

Starting point

So we have our 3 main parts of the apps: marketing webite, public API, and User sites. All have different domains, and user sites can have any domains.

 1<?php
 2
 3/*
 4|--------------------------------------------------------------------------
 5| Web Routes
 6|--------------------------------------------------------------------------
 7|
 8| Here is where you can register web routes for your application. These
 9| routes are loaded by the RouteServiceProvider within a group which
10| contains the "web" middleware group. Now create something great!
11|
12*/
13
14use App\Event;
15
16
17// Marketing website hosted on eventlist.com
18
19Route::get('/', 'MarketingController@home');
20Route::get('/features', 'MarketingController@features');
21Route::get('/pricing', 'MarketingController@pricing');
22
23
24// API endpoints all accessible via api.eventlist.com
25// Auth user via default Laravel auth
26
27Route::get('/', function () { return ['up' => true]; });
28Route::get('/event/{id}', function ($id) {
29    return Event::find($id);
30});
31
32// USER GENERATED Sites
33// parisrockshows.com, concerts-madrid.com
34
35Route::get('/', 'Tenant\EventController@home');
36Route::get('/events', 'Tenant\EventController@index');
37Route::get('/event/{event}', 'Tenant\EventController@show');

Routing for known domains

In this app, we know the marketing website and api domains. We can setup this part with the domain method.

We're introduce two new configuration keys here. Make sure marketing_domain and api_domain are added to your config/app.php file. The point is to be able to handle domains for local development environment.

 1// Marketing website hosted on eventlist.com
 2Route::domain(config('app.marketing_domain'))->group(function () {
 3   
 4    Route::get('/', 'MarketingController@home');
 5    Route::get('/features', 'MarketingController@features');
 6    Route::get('/pricing', 'MarketingController@pricing');
 7    
 8});
 9
10
11// API endpoints all accessible via api.eventlist.com
12// Auth user via default Laravel auth
13
14Route::domain(config('app.api_domain'))->group(function () {
15    
16    Route::get('/', function () { return ['up' => true]; });
17    Route::get('/event/{id}', function ($id) {
18        return Event::find($id);
19    });
20
21});

In config/app.php, you would typically add:

 1return [
 2    // ...
 3	
 4    'marketing_domain' 	=> env('MARKETING_DOMAIN', 'eventlist.com',
 5    'api_domain 		=> env('API_DOMAIN', 'api.eventlist.com',
 6	
 7    // ...
 8];

Handling custom user domains

Those domains are dynamics: they exists in your database, so you don't know them when you are setting up your routes. The domain is a variable here, just like {id} in the API route.

In Laravel official docs you'll only find the subdomain use case but it can be adapted to work with domains.

 1// USER GENERATED Sites
 2// concerts-paris.com, concerts-madrid.com
 3
 4Route::domain('{user_domain}')->group(function() {
 5   
 6    Route::get('/', 'Tenant\EventController@home');
 7    Route::get('/events', 'Tenant\EventController@index');
 8    Route::get('/event/{event}', 'Tenant\EventController@show');
 9    
10});

If you try to access you site via parisrockshows.test, you'll get a 404. I believe this feature was really built with subdomains in mind. Laravel is not able to match the user_domain to a full domain name (with extension or even subdomains).

In the RouteServiceProvider, we'll access the Router from the container, and explicitely pass the Router a regex to match the {user_domain} variable.

 1// In src/Providers/RouteServiceProvider.php
 2
 3public function boot()
 4{
 5  $this->app['router']->pattern('user_domain', '.*');
 6
 7  parent::boot();
 8}

TO BE CONTINUED...