Multi-domain generally used by large scale and highly customized web applications. It helps to give your user a custom domain URL. Multi-vendor eCommerce is a great example of a multi-domain app where every user get their own shop name and shop URL, like shop1.example.com, shop2.example.com

What is a multi-domain system?

Suppose you have an application where you want to give a unique identity(URL) to every user. If a user registers as a user name “test” then our application generates a domain for this user “test.yourapp.com”. Now, this user can use our app in this domain.

Let's build,

Create a new Laravel project name “multi-domain”

composer create-project --prefer-dist laravel/laravel multi-domain

Connect with database

.env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=multi_domain
DB_USERNAME=root
DB_PASSWORD=

Install Laravel Authentication

composer require laravel/ui
php artisan ui vue --auth
npm install
npm run dev

Run the application

php artisan serve

Visit http://127.0.0.1:8000/



We add extra columns “username, subdomain” at our user’s table for the subdomain. When users register we use their username as a subdomain and save it to our database.

User migration file

Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->string('username');
            $table->string('subdomain');
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
});

Migrate database

php artisan migrate

Add input field for username

resources\views\auth\register.blade.php

<div class="form-group row">
   <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Username') }}</label>
   <div class="col-md-6">
      <input id="username" type="text" class="form-control @error('username') is-invalid @enderror" name="username" value="{{ old('username') }}" required autocomplete="username" autofocus>
      @error('username')
      <span class="invalid-feedback" role="alert">
      <strong>{{ $message }}</strong>
      </span>
      @enderror
   </div>
</div>


Add validation for username

app\Http\Controllers\Auth\RegisterController.php

protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'username' => ['required', 'string','alpha_dash', 'max:50'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }

Change controller to save username & subdomain

Delete create method and add register method

app\Http\Controllers\Auth\RegisterController.php

...
use App\User;
use Illuminate\Http\Request;
use Illuminate\Auth\Events\Registered;
...
public function register(Request $request)
    {
        $this->validator($request->all())->validate();
        $user = new User();
        $user->name = $request->name;
        $user->username = $request->username;
        $user->subdomain = $request->username;
        $user->email = $request->email;
        $user->password = bcrypt($request->password);

        if ($user->save()) {
            event(new Registered($user));
        }
        return redirect()->route('login');
    }

Change model fillable

app\User.php

 protected $fillable = [
        'name','username','subdomain', 'email', 'password',
    ];

Let's build the multi-domain part,

We need to define our main domain add env file and our config/app.php

.env

APP_DOMAIN=lvh.me

Here we use lvh.me why?

Using subdomain in local server

When working locally with a local environment it is really hard to use subdomains inside the application, this is because it is not possible to do stuff like http://test.localhost or http://test.127.0.0.1, neither of them are valid domains. But, thankfully there are several services out there that solve this problem really easily.

For this example we will be using Laravel’s built-in server and a service called lvh.me, this service redirects every request to our localhost’s IP (127.0.0.1), therefore if we have an app running on port 8000, we can go into http://lvh.me:8000 and we should see exactly the same site as going to http://localhost:8000.

config/app.php

/*
    |--------------------------------------------------------------------------
    | Application Domain
    |--------------------------------------------------------------------------
    */
    'domain' => env('APP_DOMAIN', ''),

For multiple domain share sessions, we initialize the session domain at the .env file

.env

SESSION_DOMAIN=lvh.me

Clear cache

php artisan config:cache

Let's create a helper file

Create helpers.php inside app\Http folder

We declare a helper function for the access route when we call it from view

app\Http\helpers.php

<?php
// Route function for client domain
if (!function_exists('domain_route')) {
    function domain_route($name, $params = [], $absolute = true)
    {
        $params['subdomain'] = request()->route('subdomain');
        return app('url')->route($name, $params, $absolute);
    }
}

Change composer.json autoload to load helper file

"autoload": {
        "psr-4": {
            "App\\": "app/"
        },
        "classmap": [
            "database/seeds",
            "database/factories"
        ],
        "files": [
            "app/Http/helpers.php"
        ]
    },

Then update the composer

composer update

We  declare a middleware that keeps track that one user can not access another user domain and automatically load the user domain

php artisan make:middleware DomainCheck

app\Http\Middleware\DomainCheck.php

public function handle($request, Closure $next)
    {

        $subdomain = $request->route('subdomain');
        if (auth()->check() && $subdomain == auth()->user()->subdomain) {
            return $next($request);
        }

        return redirect()->route('home');

    }

Add this middleware to Karnel.php routeMiddleware array

app\Http\Kernel.php

'domain_check' => \App\Http\Middleware\DomainCheck::class,

Rearrange our web route

routes\web.php

Route::group(
    [
        'domain' => '{subdomain}.' . config('app.domain'),
        'middleware' => ['auth', 'domain_check'],
    ], function () {

    Route::get('/home', 'HomeController@index')->name('home');
});

Change login controller, add login method

app\Http\Controllers\Auth\LoginController.php

public function login(\Illuminate\Http\Request $request)
    {
        $this->validate($request, [
            'email' => 'required|email', 'password' => 'required',
        ]);

        $credentials  = array('email' => $request->email, 'password' => $request->password);
        if (auth()->attempt($credentials, $request->has('remember'))){
            return redirect()->route('home', ['subdomain'=> auth()->user()->subdomain]);
        }
        return redirect('/login')
            ->withInput($request->only('email', 'remember'))
            ->withErrors([
                'email' => 'Incorrect email address or password',
            ]);
    }

Now go to http://lvh.me:8000/ and register a user

Login user and you got user domain



Let's create an about view at view directory

resources\views\about.blade.php

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">{{ __('Dashboard') }}</div>

                    <div class="card-body">
                        About
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

Change home view

resources\views\home.blade.php

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">{{ __('Dashboard') }}</div>
                    <div class="card-body">
                        @if (session('status'))
                            <div class="alert alert-success" role="alert">
                                {{ session('status') }}
                            </div>
                        @endif
                        {{ __('You are logged in!') }}
                    </div>
                </div>
                <a href="{{ domain_route('about') }}">About</a>
            </div>
        </div>
    </div>
@endsection

Now we use “domain_route” instead of “route” to link URL

Create a route for about

routes/web.php

Route::group(
    [
        'domain' => '{subdomain}.' . config('app.domain'),
        'middleware' => ['auth', 'domain_check'],
    ], function () {

    Route::get('/home', 'HomeController@index')->name('home');
    Route::get('/about', function (){
        return view('about');
    })->name('about');
});

Now it’s working fine




— Thanks