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?
Let's build,
Create a new Laravel project name “multi-domain”
composer create-project --prefer-dist laravel/laravel multi-domainConnect 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 devRun the application
php artisan serveVisit 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 migrateAdd 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.meHere 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.meClear cache
php artisan config:cacheLet'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 updateWe 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 DomainCheckapp\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
2 Comments
Great Blog. Very Helpful. Thanks a lot. <3 <3 <3
ReplyDeleteThanks
Delete