I use this guide: https://mydnic.be/post/how-to-build-an-efficient-and-seo-friendly-multilingual-architecture-in-laravel-v2
I have these routes and middleware:
$locale = request()->segment(1);
Route::middleware('localized')->prefix($locale)->group(function() {
Route::get('/contacts', 'ContactController#index');
Route::get('/page/{page}', 'PageController#index');
});
And middleware localized from Kernel.php -> routeMiddleware:
public function handle(Request $request, Closure $next)
{
if (!array_key_exists($request->segment(1), config('translatable.locales'))) {
$segments = $request->segments();
$segments = Arr::prepend($segments, config('app.fallback_locale'));
if($request->session()->has('language')) {
$segments[0] = session('language');
}
return redirect()->to(implode('/', $segments));
}
return $next($request);
}
When I access to: site.test/contacts he redirect me on locale: site.com/en/contacts
When I access to site.test/page/test I got 404 not found, If I access to: site.com/en/page/test then page working. Problem with redirect on locale with route model binding.
In Controller Page I have:
public function index(Page $page)
{
return view('page', compact('page'));
}
In translatable.php:
'locales' => [
'de' => 'Deutsch',
'en' => 'English',
],
In AppServiceProvider:
public function boot()
{
if(array_key_exists(request()->segment(1), config('translatable.locales'))) {
app()->setLocale(request()->segment(1));
}
}
I was able to work around this but this cost us access to the session, which you checked for to know if the session has "language" if yes, then prepend locale value should be set to the language value in the session. Below is what I did;
In Routes File
$locale = request()->segment(1);
Route::prefix($locale)->group(function() {
Route::get('/contacts', function(){
echo "hello contact";
});
Route::get('/page/{page}', function(Request $request, $page){
return "hello Page {$page}";
});
});
In MiddleWare File I did
public function handle(Request $request, Closure $next)
{
if (!array_key_exists($request->segment(1), config('translatable.locales'))) {
$segments = $request->segments();
$segments = Arr::prepend($segments, config('app.fallback_locale'));
// Remove the session check as we don't have access to session yet
/*if($request->session()->has('language')) {
$segments[0] = session('language');
}*/
return redirect()->to(implode('/', $segments));
}
return $next($request);
}
Now in Kennel.php File for Middleware, Add your middleware class in the $middleware array
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* #var array
*/
protected $middleware = [
...
\App\Http\Middleware\Localized::class, //This is the Localized Middlewere class
];
Then try access site.test/page/1000 ... it will be redirected to site.test/{app.fallback_locale}/page/1000. In my case site.test/en/page/1000
UPDATE
After reading through this issue on git for laravel, I then thought of adding the session before the Localized middle class and it worked. I'm not sure if this is good practice but it got the job done.
In MiddleWare File I did
public function handle(Request $request, Closure $next)
{
if (!array_key_exists($request->segment(1), config('translatable.locales'))) {
$segments = $request->segments();
$segments = Arr::prepend($segments, config('app.fallback_locale'));
// We can have session back on
if($request->session()->has('language')) {
$segments[0] = session('language');
}
return redirect()->to(implode('/', $segments));
}
return $next($request);
}
Now in Kennel.php File for Middleware, Add Session middleware before Localized Middleware class
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* #var array
*/
protected $middleware = [
...
\Illuminate\Session\Middleware\StartSession::class,
\App\Http\Middleware\Localized::class, //This is the Localized Middlewere class
];
Related
I can't use localization prefix because of my client's needs. I'm trying to store locale data into session and group my routes based on that 'locale'. But i cant access session data inside web.php.
$locale = Session::get('locale');
if($locale == 'tr') {
Route::get('/kurumsal/{slug}', 'App\Http\Controllers\CorporateController#index')->name('corporate');
} elseif ($locale == 'eng){
Route::get('/corporate/{slug}', 'App\Http\Controllers\CorporateController#index')->name('corporate');
}
LanguageController middleware
class LanguageController extends Controller
{
public function index($locale = ''){
if ($locale == '') {
Session::put('locale', 'eng');
} else {
Session::put('locale', $locale);
}
return redirect()->back();
}
}
You don't need to conditionally set routes if there's no conflicts.
The following works just fine:
Route::get('/kurumsal/{slug}', 'App\Http\Controllers\CorporateController#index')->name('corporate');
Route::get('/corporate/{slug}', 'App\Http\Controllers\CorporateController#index')->name('corporate');
If you want to restrict non-localised routes from being accessed then you do need session access but you can have that via middleware e.g.:
class LocaleRestricted {
public function handle($request, $next, ...$localesAllowed) {
if (!in_array(Session::get('locale'), $localesAllowed)) {
abort(404, 'This route is not available for this locale');
}
return $next($request);
}
}
You then need to register this locale in your app/Http/Kernel.php:
// ...
protected $routeMiddleware = [
// ...
'locale_restricted' => LocaleRestricted::class
];
Then you can use it as:
$locale = Session::get('locale');
Route::middleware('locale_restricted:tr')->group(function () {
Route::get('/kurumsal/{slug}', 'App\Http\Controllers\CorporateController#index')->name('corporate');
});
Route::middleware('locale_restricted:eng')->group(function () {
Route::get('/corporate/{slug}', 'App\Http\Controllers\CorporateController#index')->name('corporate');
});
For Get Data from session you can use
$value = $request->session()->get('your-key-name');
But I'm not sure It works in Web.php or not..!! But you can use this one in Controller, Middelware etc many more places.
For More Details See Laravel Official Documentation
I have a view (resources/view/front/auth/profile.blade.php) and my route in file web.php is:
Route::get('/profile/{user}','UserController#edit')
->name('profile')
->middleware('profilecheck');
My problem is that when a user logs in and gets redirected to their own profile page (http://exmaple.com/profile/2), he/she can change the URL to http://exmaple.com/profile/3 and see other users' profile.
I want to use a middleware to check authenticated users id with URL parameter {user}. The $user->id will passed to the {user}, but I have no idea how.
Middleware UserProfile.php:
<?php
namespace App\Http\Middleware;
use App\User;
use Closure;
class UserProfile
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// $request->user()->id
// Auth::user()->id
return $next($request);
}
}
You can protect the route simply by removing the user id from the URL, but getting it through the authentication session instead.
So, your route signature should goes from:
Route::get('/profile/{user}', 'UserController#edit')->name('profile');
To this:
Route::get('/profile', 'UserController#edit')->name('profile');
So, in your controller, instead of getting the user id from the request:
public function edit(Request $request)
{
$user = User::findOrFail($request->id);
// ...
}
You could get the logged-in User through the Auth facade:
use Illuminate\Support\Facades\Auth;
public function edit(Request $request)
{
$user = Auth::user();
// ...
}
or just the auth() helper:
public function edit(Request $request)
{
$user = auth()->user();
// ...
}
This way, you are masking the URL to avoid a malicious user of doing things that he/she shouldn't.
You need to do something like this.
Your route
Route::get('/profile', [
'uses' => 'UserController#profile',
'middleware' => 'profilecheck'
]);
Your middleware
class CheckUserMiddleware
{
public function handle($request, Closure $next)
{
if(!auth()->user()) {
return redirect()->route('login');
}
return $next($request);
}
}
// Controller
public function index()
{
if (Auth::check() && Auth::user()->role->id == 2) {
return view('author.setting.settings');
} else {
Toastr::info('you are not authorized to access', 'Info');
return redirect()->route('login');
}
}
// Route
Route::group(['as'=>'user.','prefix'=>'user','namespace'=>'Author','middleware'=>['auth','user']], function (){
Route::get('/setting','SettingsController#index')->name('settings.settings');
});
I have a simple problem here.
Here's my code
route
Route::post('change-language', 'LanguageController#changeLanguage')->name('changeLanguage')->middleware('localization');
localization middlewware
public function handle($request, Closure $next)
{
if (session()->has('locale') && \App\Language::get('lang')->pluck('lang')->contains(Route::getFacadeRoot()->current()->parameter('locale'))) {
$lang = Route::getFacadeRoot()->current()->parameter('locale');
App::setLocale($lang);
session()->put('locale', $lang);
return $next($request);
}elseif(session()->has('locale') && !\App\Language::get('lang')->pluck('lang')->contains(Route::getFacadeRoot()->current()->parameter('locale'))){
$lang = 'id';
App::setLocale($lang);
session()->put('locale', $lang);
return $next($request);
}
}
changeLanguage functtion
public function changeLanguage(Request $req)
{
return redirect()->back();
}
As you can see in localization middleware, I change the language based on the route url. when I change the return of changeLocalization something like return redirect($req->lang) it work like a charm. but i want to redirect back to previous route. Is it possible to add or overriding the route paramater in back() function ?
The changeLanguage function should be responsible for actually changing the language and not just redirecting
Here's an example of how I do it
routes/web.php
Route::get('lang/{locale}', 'LocaleController#update')
->name('locale')
->where('locale', '(en|fr|ar)');
// Further filter in route regex (accepts only English, French and Arabic)
LocaleController.php
<?php
namespace Caddy\Http\Controllers;
class LocaleController extends Controller
{
public function update($locale)
{
// Check if the $locale passed is in the array config/app.php => locales
if (in_array($locale, config('app.locales'))) {
// Put the $locale in session with the same name as key
session(['locale' => $locale]);
}
// redirect back
return back();
}
}
Locale.php middleware
<?php
namespace Caddy\Http\Middleware;
use Closure;
class Locale
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$session_locale = session('locale');
if (in_array($session_locale, config('app.locales'))) {
// Keep the locale if already in session
$locale = $session_locale;
} else {
// Fallback to English if not defined
$locale = config('app.locale');
}
app()->setLocale($locale);
return $next($request);
}
}
Apply the middleware globally
Http\Kernel.php
protected $middlewareGroups = [
'web' => [
\Caddy\Http\Middleware\Locale::class,
],
Here's my config/app.php
'locale' => 'en',
'locales' => ['en', 'fr', 'ar'],
Testing
Go to domain.tld/about
Page is in English
Change the URL to domain.tld/lang/fr
You're redirected back to /about and page is in French
Hope this helps
What I'm trying to do is group multiple prefixes with one middleware:
Route::group(['middleware' => ['centralize']], function () {
Route::group(['prefix' => \App\Utilities\Centralization::setLocale()], function () {
Route::group(['prefix' => \App\Utilities\Centralization::setCountry()], function () {
Route::group(['prefix' => \App\Utilities\Centralization::setCity()], function () {
Route::group(['prefix' => \App\Utilities\Centralization::setArea()], function () {
//routes
});
});
});
});
});
I have added the centralize middleware to kernel:
protected $routeMiddleware = [
...
'centralize'=> \App\Http\Middleware\Centralize::class,
...
];
But the middleware is not being called at all, is there is something missing here?
EDIT: it goes directly to the view of the home, I am doing dd inside of the middleware but it never reaches there!
EDIT: middleware code:
<?php
namespace App\Http\Middleware;
use App\Utilities\Centralization;
use Closure;
use Illuminate\Http\RedirectResponse;
class Centralize
{
/**
* #var \Illuminate\Http\Request
*/
private $request;
/**
* #var array
*/
private $params;
private $location;
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(\Illuminate\Http\Request $request, Closure $next)
{
$this->request = $request;
dd('hit');
$this->params = explode('/', $request->getPathInfo());
$this->figureLocale();
$this->figureCountry();
$this->figureCity();
$this->figureArea();
$this->figureLocation();
$redirection = implode('/', $this->params);
if ($request->getPathInfo() !== $redirection) {
return new RedirectResponse($redirection, 302, ['Vary' => 'Accept-Language']);
}
\View::share([
'countries' => Centralization::getCountries(),
'cities' => Centralization::getCities(),
'areas' => Centralization::getAreas(),
'location' => $this->location
]);
return $next($request);
}
private function figureLocale()
{
//...
}
private function figureCountry()
{
//..
}
private function figureCity()
{
//...
}
private function figureArea()
{
//...
}
private function figureLocation()
{
//...
}
}
EDIT: I also have tried:
Route::middleware(['centralize'])->group(function () {
Route::group(['prefix' => \App\Utilities\Centralization::setLocale()], function () {
Route::group(['prefix' => \App\Utilities\Centralization::setCountry()], function () {
Route::group(['prefix' => \App\Utilities\Centralization::setCity()], function () {
Route::group(['prefix' => \App\Utilities\Centralization::setArea()], function () {
//..
});
});
});
});
});
But with the same result , it never hits the middleware!
EDIT: clearing cache using artisan and manual doesn't work either!
Group route middleware called like this:
In routes/web.php
Route::group(['middleware' => ['centralize']], function () {
Route::group(['prefix' => \App\Utilities\Centralization::setLocale()], function () {
Route::group(['prefix' => \App\Utilities\Centralization::setCountry()], function () {
//routes
});
});
});
In app/Http/kernal.php
protected $routeMiddleware = [
'centralize' => \App\Http\Middleware\CentralizeMiddleware::class,
];
In app/Http/Middleware/CentralizeMiddleware.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class CentralizeMiddleware
{
public function handle($request, Closure $next)
{
if (//Your Condition) {
//If true
}
else {
return $next($request);
}
}
}
You mentioned in one edit that:
it goes directly to the view of the home
So the URL you are visiting is actually /. And as clarified in the comments, the "home" route was defined nested inside the prefixes. But that means it is really a route for something like /fr/france/paris/somewhere/, and not a route for /.
The prefix in the route means "this route applies to URIs with this prefix". If a URI does not include that prefix, this routing is not applied.
So that means there is no route for /, and visiting it will:
not fire the centralize middleware;
not find a matching route, so throw a 404;
If you want to apply your centralize middleware to your home route, you'll need to place it inside that group, but outside the prefixes:
Route::middleware(['centralize'])->group(function () {
Route::get('/', 'HomeController#index')->name('home');
Route::group(['prefix' => \App\Utilities\Centralization::setLocale()], function () {
// ...
});
});
If i go to http://www.yourdomain.com/admin/login i see my login page.
If i go to http://www.yourdomain.com/admin/example i have the redirect to http://www.yourdomain.com/login without the admin.
My web routes:
Auth::routes();
Route::prefix('admin')->group(function() {
Route::get('/login','Auth\AdminLoginController#showLoginForm')->name('admin.login');
Route::post('/login','Auth\AdminLoginController#login')->name('admin.login.submit');
Route::get('/manifiesto','AdminController#getIndex')->name('admin.dashboard');
Route::get('/logout','Auth\AdminLoginController#logout')->name('admin.logout');
Route::get('/trabajadores','AdminController#showTrabajadores')->name('admin.trabajadores');
Route::get('/clientes','AdminController#showClientes')->name('admin.clientes');
Route::get('/proyectos','AdminController#showProyectos')->name('admin.proyectos');
Route::get('/administradores','AdminController#showAdmins')->name('admin.administradores');
});
When i put some url with the /admin before and user isn't logged, i want to redirect to /admin/login.
Thanks.
More info:
App/http/Controllers/Auth/AdminLoginController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Auth;
class AdminLoginController extends Controller
{
protected $loginPath = 'admin/login';
public function __construct()
{
$this->middleware('guest:admin', ['except' => ['logout']]);
}
public function showLoginForm()
{
return view('backend.public.pages.login');
}
public function login(Request $request)
{
//validate the form data
$this->validate($request, [
'email' => 'required|email',
'password' => 'required|min:6'
]);
//attempt to log the user in
if (Auth::guard('admin')->attempt(['email' => $request->email, 'password' => $request->password], $request->remember)){
//if successful, then redirect to their intended location
return redirect()->intended(route('admin.dashboard'));
}
return redirect()->back()->withInput($request->only('email','remember'));
}
public function logout()
{
Auth::guard('admin')->logout();
return redirect('/');
}
}
App\Http\Middleware\AdminAuthenticate.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class AdminAuthenticate
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest())
{
if ($request->ajax())
{
return response('Unauthorized.', 401);
}
else
{
return redirect()->guest('admin/login'); // <--- here
}
}
return $next($request);
}
}
Create an middleware
php artisan make:middleware AuthAdmin
Check for guest in the handle method of the middleware
public function handle($request, Closure $next)
{
if (Auth::guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('admin/login');
}
}
return $next($request);
}
Add a key to the middleware in app/Http/Kernel.php in $routeMiddleware array
'auth_admin' => \App\Http\Middleware\AuthAdmin::class
Attach the auth_admin middleware to the group
Route::group(['prefix' => 'admin', 'middleware' => 'auth_admin'], function() {
// Your admin routes except login
});
write bellow code in your route.php file
Route::group(array('prefix' => 'admin'), function() {
Route::controller('login', 'AdminloginController');
});
Route::group(array('before' => 'admin_ajax', 'prefix' => 'admin'), function()
{
//route for pages which are render after login
});
Route::get('/admin', function() {
return View::make('admin.loginform');
});
And Write bellow code in your filter.php file
Route::filter('admin_ajax', function() {
if (!Auth::admin()->check()) {
return Redirect::to('admin/login');
} else {
}
});
And if you are using laravel 5.4
Route::get('/manage', function () {
return redirect('manage/login');
});
Route::group(['prefix' => 'manage'], function() {
//login bypass for the below listed controllers
Route::resource('login', 'AdminLoginController#showLoginForm');
Route::post('dologin', 'AdminLoginController#login');
});
All you can do is add the auth middleware like this :
Route::group(['prefix' => 'admin', 'middleware' => 'auth'], function() {
Route::get('/login','Auth\AdminLoginController#showLoginForm')->name('admin.login');
Route::post('/login','Auth\AdminLoginController#login')->name('admin.login.submit');
Route::get('/manifiesto','AdminController#getIndex')->name('admin.dashboard');
Route::get('/logout','Auth\AdminLoginController#logout')->name('admin.logout');
Route::get('/trabajadores','AdminController#showTrabajadores')->name('admin.trabajadores');
Route::get('/clientes','AdminController#showClientes')->name('admin.clientes');
Route::get('/proyectos','AdminController#showProyectos')->name('admin.proyectos');
Route::get('/administradores','AdminController#showAdmins')->name('admin.administradores');
});
But by default this will redirect to /login, if you want to override this you have two chocies depending on if you have other type of users that uses the /login route or not !!
If NO ONE uses /login route
1- You need to modify App\Http\Middleware\Authenticate::handle() method and change /login to admin/login.
2- Then you need to add $loginPath property to your \App\Http\Controllers\Auth\AuthController class.
Authenticate
namespace App\Http\Middleware;
class Authenticate {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest())
{
if ($request->ajax())
{
return response('Unauthorized.', 401);
}
else
{
return redirect()->guest('admin/login'); // <--- here
}
}
return $next($request);
}
}
AuthController
namespace App\Http\Controllers\Auth;
class AuthController extends Controller
{
protected $loginPath = 'admin/login'; // <--- here
// ... other properties, constructor, traits, etc
}
If there is someone using /login route
You must create you own middleware and do what it takes for auth checking in the handle method with redirecting to your admin/liging route :)
In this case :
Add the following line in $routeMiddleware property at app/Http/Kernel.php file
'adminAuth' => \App\Http\Middleware\YourNewMiddleware::class,
Don't forget to add your new middleware in route group as follow
Route::group(['prefix' => 'admin', 'middleware' => 'adminAuth'], function()
{
// your admin routes
});
Make an another middleware for admin. follow the step
Make a file named AdminAuthenticate in app/Http/Middleware location and copy the content of Authenticate in New file
change the Class name as AdminAuthenticate
Change the content of handle function as show below
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('/admin/login');
}
}
return $next($request);
}
Add the following line in $routeMiddleware array at app/Http/Kernel.php file
'AdminAuth' => \App\Http\Middleware\AdminAuthenticate::class,
Now everything is okay. just add your new middleware in route group as follow
Route::group(['prefix' => 'admin', 'middleware' => 'AdminAuth'], function()
{
// all admin routes
});
Or you can add new middleware to constructor function of every admin controller as like below
$this->middleware('AdminAuth');