How to detect language preference in Laravel 5 - laravel-5

I want to detect my client language by getting the browser recommended language.
For Example, if you open the browser in Japan it will give me country code or country name current user opened like "en-jp" or "japan".
I try this code but it seems to display the language that I previously selected and by default it's English.
I set a Middleware and I need to exclude the api part because I have some routers pinging this address and router browser does not have language information which brick the system.
class BeforeMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
protected $except_urls = [
'api/*'
];
public function handle($request, Closure $next)
{
$langArr = array("en", "fr");
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$languages = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
} else {
$languages[0] = "en";
}
if (Session::has('locale')) {
App::setLocale(Session::get('locale'));
} else {
if (in_array($languages[0], $langArr))
App::setLocale($languages[0]);
}
return $next($request);
}
} /* end class */
Thank you for you help.

Or you can use Illuminate\Http\Request::getPreferredLanguage
Like this, in middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Session\Session;
use Illuminate\Http\Request;
class Locale {
const SESSION_KEY = 'locale';
const LOCALES = ['en', 'cs'];
public function handle(Request $request, Closure $next) {
/** #var Session $session */
$session = $request->getSession();
if (!$session->has(self::SESSION_KEY)) {
$session->put(self::SESSION_KEY, $request->getPreferredLanguage(self::LOCALES));
}
if ($request->has('lang')) {
$lang = $request->get('lang');
if (in_array($lang, self::LOCALES)) {
$session->put(self::SESSION_KEY, $lang);
}
}
app()->setLocale($session->get(self::SESSION_KEY));
return $next($request);
}
}

To simply get the locale from the header, you can grab the http-accept-language value from the request. This is accessible via a facade or you can use the request variable in your middleware:
Request::server('HTTP_ACCEPT_LANGUAGE')
// OR
$request->server('HTTP_ACCEPT_LANGUAGE');
This returns a string which looks like this: en-GB,en;q=0.8. You can then parse it (perhaps using explode()?) and grab the language from there.
However, this sort of thing can sometimes get complicated. If you need to do something more advanced, there's a package which can do all of this for you:
https://github.com/mcamara/laravel-localization

I just made a Middleware for this, it may be useful.
First you set $availableLangs the array of the available languages in your app, you may use config\app.php instead of initializing the array in the middleware as I did.
If the first language is available in the request language data, it sets the locale, if not, it will search the next one, and so on.
class GetRequestLanguage
{
public function handle($request, Closure $next)
{
if (Session::has('locale')) {
App::setLocale(Session::get('locale'));
} else {
$availableLangs = ['pt', 'en'];
$userLangs = preg_split('/,|;/', $request->server('HTTP_ACCEPT_LANGUAGE'));
foreach ($availableLangs as $lang) {
if(in_array($lang, $userLangs)) {
App::setLocale($lang);
Session::push('locale', $lang);
break;
}
}
}
return $next($request);
}
}

In a middleware do:
$acceptableLocales = config('app.available_locales');
$userLocales = $request->getLanguages();
if(!empty($userLocales)) {
foreach ($userLocales as $lang) {
$langToSearch = str_replace('_','-',$lang);
if(in_array($langToSearch, $acceptableLocales)) {
app('translator')->setLocale($langToSearch);
break;
}
}
}
return $next($request);
If you want to return the language in the response to the client, create another middleware and do:
/** #var Response $response */
$response = $next($request);
$response->header('Content-Language', app('translator')->getLocale());
return $response;

This is old question but i'll apply my anwser.
If you want to automatic localization use the bellow code in your Middleware
public function handle(Request $request, Closure $next)
{
$asTranslation = ['en','pt'];
$userLocales = $request->getLanguages();
$locale = Str::before($request->getPreferredLanguage($userLocales),'_');
//check if the locale is in the array of available locales
if (!in_array($locale, $asTranslation)) {
$locale = 'en';
}
//remove the brackets from the locale
app()->setLocale( Str::before($locale, '_'));
return $next($request);
}

I use a middleware too, but a lit bit smaller.
<?php
namespace Transfer\Http\Middleware;
use Closure;
class SetLanguage
{
public function handle($request, Closure $next)
{
$locale = $request->getLocale();
if ($request->session()->has('locale')) {
$locale = $request->session()->get('locale');
}
config(['app.locale' => $locale]);
return $next($request);
}
}

Detect language in Route and redirect to /en, /es, or ...
File web.php
use Illuminate\Support\Facades\Request;
Route::get('/', function () {
$lang = config('app.locale');
try {
$lang = Request::getPreferredLanguage(config('app.locales'));
} catch (\Exception $e) {
}
return redirect()->to($lang);
});
File config/app.php
'locale' => 'es',
'locales' => ['es','en'],

For Laravel 8 you can use $request->segment(1)
Example of Request:
Request::server('HTTP_ACCEPT_LANGUAGE') = en-DE,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,de;q=0.6
Code:
$request->segment(1)
Result:
'en'
So easily set the locale: \App::setlocale($request->segment(1));

Related

Gate Define not working for other users except whose role_id is 1

The Below code in middleware is working fine when user has role_id 1, When I did the dd on role->permissions I get then the response is array.
But on this middleware line
if (in_array($role->permissions, $permission)) {
I get this error for all other users whose role_id is different
in_array(): Argument #2 ($haystack) must be of type array, string given where array was passed
My Roles Model has
protected $casts = [
'permissions' => 'array',
];
My User Model has
protected function role()
{
return $this->hasOne(Roles::class, 'id', 'role_id');
}
My web Middlewaregroup has
\App\Http\Middleware\RolePermissionCheck::class,
My Meddlware has
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
class RolePermissionCheck
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!empty(Auth::user()->role_id)) {
$role = Auth::user()->role;
Gate::before(
function () {
if (Auth::user()->role_id === 1) {
return true;
}
}
);
// dd($role->permissions);
foreach ($role->permissions as $permission) {
Gate::define(
$permission,
function ($role) use ($permission) {
if (in_array($role->permissions, $permission)) {
return true;
}
}
);
}
}
return $next($request);
}
}
Do not mark my answer as correct as user Autista_z told you the fix, I am going to share something to have a better code, really simple and "Laravel way" stuff to do.
As user Autista_z said: "the problem will be in foreach loop for Gate defining. In first iteration of loop (based on your sample array) you would have $action = 0 and $roles = 'admin_role_manage'. So the name of gate would be 0. So of course, then #can('admin_role_manage') is false".
So, You are setting up or getting a lot of stuff that is not needed, or worded other way, you can have a clearer code (at least for your Middleware class).
If you know (maybe you don't), you can cast models properties to a type you want, so instead of doing json_decode($user_role->permissions) you can simply do foreach ($user_role->permissions as $role), but before you do so you need to cast it.
So your model would have a property $casts as this:
protected $casts = [
'permissions' => 'array',
];
This will allow you to do $model->permissions = ['role_1', 'role_2', ...]; without the need of doing $model->permissions = json_encode(['role_1', 'role_2', ...]); (and also this is the Laravel way of handling this).
So your middleware would end like:
namespace App\Http\Middleware;
use App\Models\User;
use App\Models\Roles;
use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
class RolePermissionCheck
{
public function handle($request, Closure $next)
{
if ($role = Auth::user()->role)) {
Gate::before(
function (User $user) {
if ($user->role_id === '1') {
return true;
}
}
);
foreach ($role->permissions as $permission) {
Gate::define(
$permission,
function ($role) use ($permission) {
if (in_array($permission, $role->permissions)) {
return true;
}
}
);
}
}
return $next($request);
}
}
See that I have changed the wording of $role->permissions as $roles to $role->permissions as $permission, your role could be "Writer" and your permissions are newsletter_manage, brand_logos, quote_manage, and more. So this are not roles but permissions.
Also have in mind that I think you can even have better code for the foreach/define part.

How to access controller via both admin guards and normal user guards without using roles

I need to download the form posted from the supervisor. I can access the page but not the form since I can't download it. It's giving me the ERR_INVALID_RESPONSE error, but the supervisor can download it easily.
Could it be something wrong with the middleware? I struggled a lot but still can't download, please if anyone might know the problem please help me.
Controller
class DutiesController extends Controller
{
public function assignSupervisor(Request $request, $id)
{
$assignS = new Duty;
$assignS->student_id = $id;
$assignS->user_id = $request->supervisor_id;
$assignS->student_name = $request->student_name;
$assignS->save();
return back();
}
public function assignInstructor(Request $request, $id)
{
$assignS = Duty::where('student_id', $id)->first();
$assignS->admin_id = $request->instructor_id;
$assignS->save();
return back();
}
public function duties($id)
{
$duty = Duty::where('student_id', $id)->orWhere('user_id', $id)->orWhere('admin_id', $id)->first();
return view('Duty.show', compact('duty'));
}
public function assign(Request $request, $id)
{
$assign = Duty::findOrfail($id);
if ($request->hasFile('duty')) {
$this->validate($request, [
'duty' => 'required|file|mimes:pdf,doc'
]);
$fileNameWithExt = $request->file('duty')->getClientOriginalName();
$fileName = pathinfo($fileNameWithExt, PATHINFO_FILENAME);
$extension = $request->file('duty')->getClientOriginalExtension();
$fileNameToStore = $fileName.'_'.time().'.'.$extension;
$path = $request->file('duty')->storeAs('public/duty', $fileNameToStore);
$assign->duty = $fileNameToStore;
}
$assign->marks = $request->marks;
$assign->save();
return back();
}
public function getduty($id) // my download function
{
$download = Duty::findOrfail($id);
return Storage::download("/public/duty/".$download->duty);
}
public function assignSupervisorInstructor()
{
$users = User::with('campus')->where('role_id', 4)->get();
$supervisors = User::with('campus')->where('role_id', 2)->get();
$instructors = Admin::where('role_id', 3)->get();
return view('Assigning.index', compact('users', 'supervisors', 'instructors'));
}
}
Routes
Route::group(['middleware' => 'auth:web,admin'], function () {
//Now this routes can be accessible by both admin as well as
Route::get('/duties/downloads/{id}', 'DutiesController#getduty');
Route::post('/duties/assign/{id}', 'DutiesController#assign');
Route::get('/duties/myduties/{id}', 'DutiesController#duties');
Route::get('/duties/mydutty/{id}', 'DuttyController#duties');
Route::post('/duties/{id}', 'DutiesController#assignSupervisor');
Route::get('/assign', 'DutiesController#assignSupervisorInstructor');
Route::post('/duties/inst/{id}', 'DutiesController#assignInstructor');
});
Blade
<td>
<a href="/duties/downloads/{{$duty->id}}">
<button class="btn btn-success"><i class="fa fa-download"></i> Dowload Document</button>
</a>
</td>
I hope this is what you mean but this is how I differentiate between user types via Middleware.
Basically creating custom middleware (I guess you can also do it via the built in functionality but I prefer this), for example:
1) The MiddleWare:
app/Http/Middleware/CheckUserType.php:
namespace App\Http\Middleware;
use Auth;
use Closure;
use App\Usergroup;
class CheckUserType
{
/**
* This middleware checks if the user is logged in as a specific type
*
* #var array
*/
public function handle($request, Closure $next, ...$userGroups)
{
if (in_array(UserGroup::where('id', Auth::user()->groupid)->first()->title, $userGroups)) {
return $next($request);
} else {
return response('Unauthorized...', 401);
}
}
}
In my case I have a usergroups table that links to the user->groupid so I use that Model to cross-reference the id to the group title I give in in my router.
But you can modify this obviously.
Also note that I do ...$userGroups so I can iterate over multiple user types if I want to in the router (see below).
2) Register in your kernel:
Then register in app/Http/Kernel.php:
add to the protected $routeMiddleware:
checkUserType' => CheckUserType::class
Make sure to include you custom middleware (use App\Http\Middleware\CheckUserType;)
3) Routes:
So then at last in my router I have for example the following:
/**
* #section Routes that require the user to be a Manager or an Administrator
*/
Route::group(['middleware' => 'checkUserType:Managers,Administrators'], function () {
//
});
or:
/**
* #section Routes that require the user to be an Administrator
*/
Route::group(['middleware' => 'check.usertype:Administrators'], function () {
// User Routes
Route::delete('/users/delete/{id}', 'UserController#deleteUser');
});

Get Language from construct in laravel

i'm trying to get selected language in my construct to use in any function in that class:
my route:
Route::group(['prefix' => 'admin', 'middleware' => ['AdminMiddleWare','auth','localization']], function(){
Route::get('/', 'AdminController#index')->name('admin.index');
});
My Middleware:
public function handle($request, Closure $next)
{
if (Session::has('locale') AND array_key_exists(Session::get('locale'), Config::get('languages'))) {
App::setLocale(Session::get('locale'));
}
else {
App::setLocale(Config::get('app.locale'));
}
return $next($request);
}
My controller :
public $lang;
public function __construct()
{
$this->lang = Language::where('lang','=',app()->getLocale())->first();
}
public function index()
{
$lang = $this->lang;
return $lang;
}
but i'm getting only the default locale;
but if i change the controller to this:
public function index()
{
$lang = Language::where('lang','=',app()->getLocale())->first();
return $lang;
}
it will work...
how to get in construct and use it in all functions??
In Laravel, a controller is instantiated before middleware has run. Your controller's constructor is making the query before the middleware has had a chance to check and store the locale value.
There are multiple ways you can set up to work around this - the important thing is to make the call after middleware runs. One way is to use a getter method on your controller:
class Controller
{
/**
* #var Language
*/
private $lang;
public function index()
{
$lang = $this->getLang();
// ...
}
private function getLang()
{
if ($this->lang) {
return $this->lang;
}
return $this->lang = Language::where('lang','=',app()->getLocale())->first();
}
}

Locale Middleware and View Composer Service Provider

I'm developing a multi language website with Laravel 5.4 and I'm having trouble setting the middleware and the service provider logic. I already realized that servide provider is executed before the middleware. The problem is that I set the application locale on the middleware. On other hand, I have a service provider to feed localized data into the application's nav bar and footer. My problem is that by the time the service provider is executed, it doesn't know what the real locale is.
This is my handle function on Locale middleware
public function handle($request, Closure $next, $guard = null)
{
$locales_arr = Language::localesArr();
// if there isn't a slug on URL
if(!session()->has('app.locale') && $request->segment(1) === null)
{
// browser preference
$client_locale = substr(\Request::server('HTTP_ACCEPT_LANGUAGE'), 0, 2);
// if it is a valid locale
if(in_array($client_locale, $locales_arr))
{
$locale = $client_locale;
}
else {
$locale = config('app.fallback_locale');
}
}
else
{
$locale = in_array($request->segment(1), $locales_arr) ? $request->segment(1) : config('app.fallback_locale');
}
// set locale prefix
$locale_prefix = ($locale != config('app.fallback_locale')) ? $locale : '';
// set locale
app()->setLocale($locale);
config(['app.locale_prefix' => $locale_prefix]);
session(['app.locale' => $locale]);
session()->save();
setlocale(LC_TIME, $locale . "_" . strtoupper($locale));
return $next($request);
}
And this is my boot function on ViewComposerServiceProvider
public function boot()
{
/* this doesn't work properly!!! */
dump(app()->getLocale());
}
Can someone please help? I'm really stuck on this issue...

Validate Authorization in Laravel 5.2

My roles are dynamic and their User's permission are also dynamic. I have two approaches to validate if the user is authorized to access the particular page.
Approach 1
class BaseController extends Controller
{
public function __construct() {
if(!\Auth::user()->IsPredefined) {
$result = $this->ValidateAuthorization();
if(!$result) {
\Auth::logout();
return redirect()->route("login");
}
}
}
private function ValidateAuthorization() {
$ActionName = \Route::getCurrentRoute()->getPath();
switch ($ActionName) {
case "ChangePassword":
$ModuleID = ModuleEnum::AccountManagemenet;
$ActionID = AccountActionEnum::ChangePassword;
return CheckUsePermissions($ModuleID, $ActionID);
}
}
private function CheckUsePermissions($ModuleID, $ActionID) {
$User = MySession::UserPermissions();
foreach($User->UserRolePermissions as $UserRolePermission) {
$CurrentActionID = $UserRolePermission->RolePermission->Permission->ActionID;
$CurrentModuleID = $UserRolePermission->RolePermission->Permission->ModuleID;
if($CurrentActionID == $ActionID && $CurrentModuleID == $ModuleID &&
$UserRolePermission->IsActive == true) {
return true;
}
}
return false;
}
}
Approach 2
Use Authorize method in Request class
public function authorize()
{
return true;
}
Confusion
If Approach 2 is good, should I create Request class for each Get, Put, Delete and POST?
Is there any better approach to validate authorization?
For Dynamic Roles I do something like this:
Let suppose I have following permissions:
Manage Users
Manage CMS
Manage Hotels
Manage Packages
Manage Roles
Now the Super Admin (who obviously has all the above permissions) can create a role, say, Hotel Manager and assign permission for only Manage Hotels.
Now for a route like:
Route::get('/admin/hotels', 'HotelsController#index')->name('admin.hotels.index');
I will put it in a Route Group with a middleware in it:
Route::group(['prefix' => '/admin/hotels', 'middleware' => ['permission:manage_hotels']], function () {
Route::get('/', 'HotelsController#index')->name('admin.hotels.index');
Route::post('/', 'HotelsController#create')->name('admin.hotels.create');
});
Then I will create a Permission middleware:
class Permission
{
public function handle($request, Closure $next, $permission)
{
//if not super admin (super admin role_id is 1)
if(auth()->user()->role_id != 1) {
//explode extra param passed to this middleware (here manage_hotels)
$permission_array = explode('_', $permission);
foreach ($permission_array as $key => $value) {
$permission_array[$key] = ucfirst($value);
}
$permission_name = implode(' ', $permission_array);
$permitted = auth()->user()->role->permissions()->where('name', $permission_name)->first();
if(! $permitted) {
return redirect()->route('admin.dashboard')->withErrors(['error' => ['message' => 'You are not allowed to perform this action.']]);
}
}
return $next($request);
}
}
Of course you'll need to wrap these routes into Admin Middleware which will ensure that user is logged in and has an admin role.
You should use middleware and use in routes
Route::group(['middleware' => ['auth', 'admin']], function () { });
Or you can use in every controller constructor
public function __construct()
{
$this->middleware('auth');
$this->middleware('admin');
}
Admin middleware like
class AdminAuthenticate
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (Auth::check()) {
if (Auth::user()->IsPredefined) {
$result = $this->ValidateAuthorization();
if(!$result) {
return redirect('/logout');
}
}
}
return $next($request);
}
}

Resources