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.
Related
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.
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 got a problem with my Laravel application - I am not able to change my app language and keep it set. After the next request to the server it goes back to default language set. The only thing which is possible is to change the default language in app.php file. I have recently updated my app to Laravel 5.22 - could it be connected with the problem mentioned above?
Would you have some kind of advice on this?
Thank you in advance for any kind of help
in you route group each time load the language
Route::group(['namespace' => 'Language'], function () {
require (__DIR__ . '/Routes/Language/Language.php');
});
in language.php(i have loaded in different route directory )
Route::get('lang/{lang}', 'LanguageController#swap');
in LaunguageController store in session to persist the selection
class LanguageController extends Controller
{
/**
* #param $lang
* #return \Illuminate\Http\RedirectResponse
*/
public function swap($lang)
{
session()->put('locale', $lang);
return redirect()->back();
}
}
So I have an api-version middleware that I use in laravel 5 that I am trying to use in lumen. However it uses getAction() and setAction() in the middleware which isn't available to lumen ("yet"). If I do $request->route()[1] I can get the current route but updating that with the new route does no good. I have thought about cloning the request and modifying it but I can't tell what part of the request object I would need to "update".
Here are my routes:
$app->group(['middleware' => ['api-version']], function() use ($app) {
$app->get('users', '{api-namespace}\UsersController#index');
$app->get('vips/{per_page?}', '{api-namespace}\VipsController#index');
$app->get('vip/{id}/profile', '{api-namespace}\VipsController#showProfile');
$app->get('vip/{id}', '{api-namespace}\VipsController#show');
});
Can anyone tell me how i can update the request with my simple route update?
Found this in my search for a solution. Here's what I came up with using middleware in Lumen 5.4.
First, I created an ExtractApiVersionMiddleware that extracts the version from the Accept header. I used the accept header because not all headers passed are trusted and it is becoming "Best Practice": to pass API versions in this manner:
<?php
namespace App\Http\Middleware;
use Closure;
class ExtractApiVersionMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
*
* #return mixed
*/
public function handle($request, Closure $next)
{
$app = app();
$version = "0";
foreach (explode(';', $request->header('accept')) as $frag) {
if (stristr($frag, "version=")) {
$version = str_replace("version=", "", $frag);
break;
}
}
if ($version != 0) {
$app->group(['namespace' => "App\Http\Controllers\V{$version}\Reductions"],
function () use ($app, $version) {
require base_path() . "/routes/web/v{$version}.php";
});
} else {
$app->group(['namespace' => 'App\Http\Controllers\V0'], function () use ($app, $version) {
require base_path() . "/routes/web/v0.php";
});
}
return $next($request);
}
}
Second, I namespaced my code according to versions (the legacy codebase hasn't died yet).
Third, I pass the version that the call uses in the Accept header
Accept: application/json;version=1
Fourth, I have separate route files for each supported version. So, instead of having a web.php for all my routes, I created a web folder under routes and I placed my version files in there v0.php & v1.php
So, when I make a request, the Middleware extracts the API version and based on the version number enables the appropriate route group. This keeps the versions clean and separate but still enables me to get 'creative' with my Models.
Hope this helps. I had to apply this solution because I wasn't sure about using Dingo API in pre-release form
I'm building a multi languages site using Laravel 5.
I knew that in Laravel, I can define prefix for route like:
http://domain/en/users/1
http://domain/en/shop/1
And in Middleware, I can get the segment of url path to detect the language and set locale of current Request.
But I can't find anyway to add lang parameter in default for route like folowings:
http://domain/users/1?lang=en
http://domain/shop/1?lang=en
Or are there anyways to hook into route function of Framework to append default parameter ?lang=jainto all route when I call ?
(ja is current locale of application which was set in middleware before )
Thanks !
You can create a middleware that sets up a default 'lang' query parameter if the request doesn't have one. It will work for all the requests to your app, so you can get the lang parameter in every route handler.
Create a middleware LangFilter in the console (while in the project directory) :
php artisan make:middleware LangFilter
Then open up ./app/Http/Kernel.php and add :
\App\Http\Middleware\LangFilter::class
to the $middleware array. Now open up the middleware you created, i.e ./app/Http/Middleware/LangFilter.php and add the checking and setting code :
<?php
namespace App\Http\Middleware;
use Closure;
class LangFilter
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// Checks for lang in the parameters.
if($request->input('lang') == null) {
// Adds the default one since it doesn't have one.
$request->merge(array('lang' => 'en'));
}
return $next($request);
}
}
If you want to have this kind of filtering for just a subset of all the routes you have, you need to register the middleware differently in Kernel.php.
UPDATE
For making a helper that generates routes with current locale :
Create a folder app/Support.
Create the helpers file app/Support/helpers.php
Open up helpers.php, and add this code to add the helper :
<?php
function locale_route($name, $parameters = [], $absolute = true) {
return route($name, array_merge($parameters, [
'lang' => App::getLocale()
]), $absolute);
}
?>
Add the helpers file to composer autoload in composer.json:
"autoload" : {
"files" : [
"app/Support/helpers.php"
]
}
Now run in the console :
composer dumpautoload
Now you can call locale_route with the same parameters you give to route to create urls that has the current locale added in query params.
I hope this is what you are looking for. Generating a route with a query string parameter