I already have the answer, I would like to share the solution with those who need it.
How to use multiple domains with different pages and routes in Laravel?
I spent many hours looking for solutions, but nothing concrete, always with complex and messy codes, in the end I developed a practical solution with clean code.
1 - Firstly, it is necessary to centralize the laravel in a single domain, then you must point the other domains to the main domain, you can access your dns manager and use the CNAME record for this.
2 - In your Laravel you must create a Controller the home page with the following content, replacing what is necessary:
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$origin = array("mydomain.com", "mydomain2.com");
$domain = parse_url(request()->root())['host'];
if (in_array($domain, $origin)) {
if ($domain === 'mydomain.com') {
return view('myview'));
}
if ($domain === 'mydomain2.com') {
return view('myview2'));
}
} else{
return view('unauthorized');
}
}
3 - Finally (optional), create a route with the urls that will be accessible only by that domain, do so:
Route::group(array('domain' => 'mydomain.com'), function () {
/* routes here */
Route::get('/', 'YouController#index');
});
Route::group(array('domain' => 'mydomain2.com'), function () {
/* routes here */
Route::get('/', 'YouController#index');
});
You must change mydomain.com and mydomain2.com to the domain you want, else{} you must replace unauthorized with a valid view, this is what will appear when the domain is not listed, if you want you can do the o server also shows nothing.
Related
I am currently developing a project where people can come into my Laravel project and create their own one-page website.
Now I would like to have them be to able to point their own custom domain to a specific Laravel route.
There's only one path that will "receive" all the domains, for example
[Customer 1]
have ABC.com
ABC.com -> point to -> mylaravelproject.com/route1
[Custom 2]
have DEF.com
DEF.com -> point to -> mylaravelproject.com/route1
And I will just let the code in Laravel route file detect the domain, and display the content dynamically
/routes/web.php
$thankyoupages = Thankyoupage::whereNotNull('domain')->get();
foreach( $thankyoupages as $ty ) {
Route::group(['domain' => $ty->domain], function() use ($ty) {
Route::get("/", function() use($ty) {
$data = [
"ty" => $ty,
"pixels" => json_decode($ty->pixels)
];
return view('thankyous.thankyoupage', $data);
});
});
}
My questions are:
[1] Are there any (easy) ways to achieve this?
[2] Do I have to alter Vhosts of Apache dynamically? or do I need reverse proxy?
[3] How to config the domain in order to achieve this? Just point A record to server's IP? or should I use CNAME?
Thank you in advance
For handle dynamically domains in one laravel project I came to the following solution after a lot of searching and experimenting in one of my projects. It's not ideal you may impove on your own.
First all your domain must be added A record or CName in dns records
from DNS managers. A record pointed to your server IP works perfectly
Add your route list new group by domain example:
Route::group(['domain' => '{domain}'], function () {
$url = Request::getHost();
$full_url = url('/');
Helper::domain($url, $full_url);
// Your routes list ...
}
Also, we will need send dynamically domain name in diferent part of your application for instance in controller. For that I added new helper function to Helper class:
class Helper
{
public static $domain;
public static $full_domain;
public static $autoload_static_data;
public static $position;
public static function domain($domain,$full_domain)
{
Helper::$domain=$domain;
Helper::$full_domain=$full_domain;
$domain_info=domain_info();
if ($full_domain==env('APP_URL') || $full_domain==env('APP_URL_WITHOUT_WWW')) {
return true;
}
if ($domain==env('APP_PROTOCOLESS_URL') || str_replace('www.','',$domain)==env('APP_PROTOCOLESS_URL')) {
return true;
}
$domain=str_replace('www.','',$domain);
Helper::$domain=$domain;
if (!Cache::has(Helper::$domain)) {
$value = Cache::remember(Helper::$domain, 600,function () {
$data=\App\Domain::where('domain',Helper::$domain)->where('status',1)->first();
if (empty($data)) {
abort(404);//or you can make custom 404 page
}
$info['domain_id']=$data->id;
$info['user_id']=$data->user_id;
$info['domain_name']= Helper::$domain;
$info['full_domain']= Helper::$full_domain;
$info['plan']=json_decode($data->information);
return $info;
});
}
}
You must save all your domain in database. In my case it was domains table and Domain model. You can verify domain and get all related information separately by domain.
Inside your server to handle all your domains in single webroot folder you can use ServerAlias * (wildcards)
Nginx in your configuration inside server block for server_name you can use regex
If you do not understand or have additional questions, you can ask via comment
I have two domains directing to a single laravel application.
test_en.site
test_fr.site
MY REQUIREMENT
test_en.site need to load the English content by default and test_fr.site need to load the French content.
(If a user accesses to test_en.site, still the user can change the language to French, and if a user accesses to test_fr.site user can change the language to English.)
WHAT I HAVE DONE SO FAR
In order to check the domain and load the correct language accordingly, in my Middleware, Localization.php I have added the following condition.
app/Http/Middleware/Localization.php
<?php
namespace App\Http\Middleware;
use App;
use Closure;
class Localization
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (session()->has('locale')) {
App::setLocale(session()->get('locale'));
return $next($request);
}
// load english by default if the root is test_en.site or else load french for other domains
else {
$locale = $request->root() == 'http://test_en.site' ? 'En' : 'Fr';
App::setLocale($locale);
return $next($request);
}
}
}
PROBLEM
I created two virtual hosts for the same project with two test domains and tried in my local then it works well...
But when I tested this out on the live server it keeps loading the English for the French domain too.
$locale = $request->root() == 'http://test_en.site' ? 'En' : 'Fr';
App::setLocale($locale);
return $next($request)
I even tried using the getHost() method instead of root() but that too works only in the local server...
Where am I doing wrong and How can I fix this, as this code works fine in the local I'm struggling to find the solution...
your code is correct, I think you forgot to add it into Kernel.php
if it still not work
try this snippet instead
p/s edited
// get subdomain
$url_array = explode('.', parse_url($request->url(), PHP_URL_HOST));
$subdomain = $url_array[0]; // in your case it should be test_en/test_fr
$languages = ['test_en' => 'en', 'test_fr' => 'fr'];
App::setLocale($languages[$subdomain]);
return $next($request);
You can try use https://github.com/movemoveapp/laravel-localization localization package. Your problem describe here https://github.com/movemoveapp/laravel-localization#localization-switch-by-domain-names.
How to use?
In your case you have:
test_en.site - En version
test_fr.site - Fr version
Install package and add to .env file new environments
LOCALIZATION_DOMAIN_NAME_EN=test_en.site
LOCALIZATION_DOMAIN_NAME_FR=test_fr.site
A next step modify your web routes in routes/web.php, like to
Route::group([
'middleware' => [ 'localizationDomainRedirect' ]
], function()
{
Route::get('/', function()
{
return View::make('index');
});
});
So, by http://test_en.site/ opened En version, by http://test_fr.site - Fr.
I want to prevent access to some of my app routes from other domain except listed. It success using below code:
$loginRoutes = function() {
Route::get('/', 'HomeController#index')->name('home');
};
Route::domain('domain1.com')->group($loginRoutes);
Route::domain('domain2.com')->group($loginRoutes);
Route::domain('localhost')->group($loginRoutes);
But the problem is when I call {{route('home')}}, the URL always becomes the domain at the last line of the routes.php(at above case is http://localhost ). How to make it to current domain?
My current solution:
if (isset($_SERVER["HTTP_HOST"]) && $_SERVER["HTTP_HOST"] == "domain1.com") {
Route::domain('domain1.com')->group($loginRoutes);
}elseif (isset($_SERVER["HTTP_HOST"]) && $_SERVER["HTTP_HOST"] == "domain2.com") {
Route::domain('domain2.com')->group($loginRoutes);
}
It's work but I think it's dirty. I have a lot of domains/subdomain and also the routes too.
I need solution on route directly, because I have a lot of routes, if I update each controller it's will take a long time. Maybe edit route provider or laravel vendor code is also no problem.
I am also using PHP 7.3 and Laravel 5.7
I actually use this routing for my domains.
Maybe this is not exactly what you asked, but you can try something like this
// To get the routes from other domains
// Always add the new domains here
$loginRoutes = function() {
Route::get('/', 'HomeController#index')->name('home');
};
Route::group(array('domain' => 'domain1.com'), $loginRoutes);
Route::group(array('domain' => 'domain2.com'), $loginRoutes);
Route::group(array('domain' => 'domain3.com'), $loginRoutes);
If you want to handle something at the domain level. In your controller (HomeController#index), you can get the current domain and do whatever you want. To get exact domain I have used like this:
class HomeController extends Controller
{
public function index()
{
$domain = parse_url(request()->root())['host'];
if ($domain == 'domain1.com'){
// do something
}
...
}
...
}
That way I can handle different things for each domain.
Just to make it more complete, we can take the domains from a table/query and dynamically create the routes.
$domains = Cache::get('partners')->where('status', '=', 'A')->where('domain', '<>', '')->all();
$loginRoutes = function() {
Route::get('/', 'HomeController# index')->name('home');
};
foreach ($domains as $domain) {
Route::group(array('domain' => $domain->dominio_externo), $loginRoutes);
}
It has been working for me. I hope to help you.
You can maybe try something like this :
Route::pattern('domainPattern', '(domain1.com|domain2.com|localhost)');
$loginRoutes = function() {
Route::get('/', 'HomeController#index')->name('home');
};
Route::group(['domain' => '{domainPattern}'], $loginRoutes);
If I understand your issue, you just want to filter domains. Using regex, you can do it. You could try the following code:
Route::domain('{domain}')->group($loginRoutes)->where('domain', '\b(domain1\.com|domain2\.com|localhost)\b');
Details:
\b: we get exactly the string.
\.: in regex, the character . means any character. So, we have to escape . using backslash.
Note:
You might get an error, because I can not check the results. Let me know any errors you encounter.
I want to prevent access to some of my app routes from other domain
except listed. It success using below code:
I think you are right with your thoughts about a better, more laravel-core based solution for this problem.
Every route handling method you define in a controller file recieves a request. In standard laravel this is an object of type Illuminate\Http\Request.
You can extend this class with a custom class - let's say "AdminRequest". This extended class than offers authorization methods which will check if the Auth:user has the correct role, session values or whatever you want in your app.
I guess this is more flexible and clean - in your controller you only have to change the definition of the request you recieve in that controller method. Validation messages and everything else can be wrapped in the custom request class.
See this also:
How to Use custom request (make:request)? (laravel) Method App\Http\Requests\Custom::doesExistI does not exist
Extend Request class in Laravel 5
for preventing access to a certain route, its a bad design to inject a Route into these structure:
Route::domain('domain1.com')->group($loginRoutes);
Route::domain('domain2.com')->group($loginRoutes);
Route::domain('localhost')->group($loginRoutes);
since it defines route multiple time, and only the last will be override the others.
you can check this by php artisan route:list .
the laravel way to handle this situation (access management ) is to use middleware
class DomainValid
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$domain = $request->server->get('SERVER_NAME');
if (in_array($domain , ['domain1.com','domain2.com','localhost'] )) {
return $next($request);
}
abort(403);
}
}
and use it like this:
use App\Http\Middleware\DomainValid;
Route::get('/', 'HomeController#index')->name('home')->middleware(DomainValid::class);
so it will be only ONE home route.
I have an admins table that has some columns like id, admin_type, name, etc. Here I have two types of admin_type: one is "admin" and the other is "hod".
Now the problem is that I want admins that login with (admin_type == "admin") to be able to access all the admin URLs, but when admin_type == "hod" I want to limit the URLs the user can access.
I am using Laravel 5.2. Can anyone help me to resolve this issue?
For example, if a user with admin_type=="hod" accesses these links
www.example.com/admin/add-user
www.example.com/admin/edit-user
www.example.com/admin/add-customer
www.example.com/admin/edit-customer
and many more URLs, I want to show some message like **You have no rights to access this link **
Here is my database structure:
I would implemented a Middleware for such a use case. Just execute php artisan make:middleware yourNameHere in your Laravel working directory and artisan generates the corresponding middleware class for you.
Then you need code like this. It's a simple if condition with an 403 abort in case that the user is no admin.
class AdminMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($request->user()->admin_type != 'admin') {
return abort(403, "No access here, sorry!");
}
return $next($request);
}
}
Your routes file (routes/web.php):
...
Route::group(["middleware" => 'admin'], function () {
//Your admin routes should be declared here
});
...
I want to add a functionality on my web app where users visit the same URL and get different pages depending if they are logged in or not. The way I'm doing this now is using a middleware to redirect logged in users to /home. But, I want to do something like facebook does..
When someone types http://facebook.com, it analyzes if the person is logged in, if they are, it shows their home, if they are not, it shows the registration page on the same URL (you can see that the address in the bar does not change)
I'm trying to use this code on my route:
Route::get('/', array('as'=>'home', 'uses'=> (Auth::check()) ? "usercontroller#home" : "homecontroller#index" ));
Found Here: https://stackoverflow.com/a/18896113/2724978
But it just shows the second controller method ("homecontroller#index") no matter if the user is logged in or not.
Is it just me or can't you just do as #AJReading has suggested and use an ordinary controller method to handle this?
Set up like so:
In your HomeController.php:
class HomeController extends Controller
{
/**
* Show a different view depending on whether or not the user is logged-in.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
if (Auth::check()) {
// logged-in
return view('home.index.authorised')->with('user', Auth::user());
} else {
// not logged-in
return view('home.index.guest');
}
}
}
Then create your alternate views e.g. resources/views/home/guest.blade.php
here is exactly what you want:
Route::get('/', function() {
$guest = Auth::guest();
if($guest)
{
$controller = $this->app->make('App\Http\Controllers\TaskController');
return $controller->callAction('guest', $parameters = array());
}
else
{
$controller = $this->app->make('App\Http\Controllers\TaskController');
return $controller->callAction('user', $parameters = array());
}
});
Just replace the names with yours.
Tested on: Laravel Framework version 5.1.35 (LTS).
Should be improved further by looking at the namespacing.
Did came up with a better solution using middleware - but didn't save it and can't recreate it now.
Answer derived from/based on:
Laravel single route point to different controller depending on slugs
http://laravel.io/forum/10-16-2014-l5-controller-does-not-exist