I tried to localization my app, but seems like I'm missing something. I don't have any previous knowledge about this and therefore it's pretty hard to get started. Here is my routes.php
Route::get('/lang/{lang}', 'LangController#index');
And here is my LangController.php
public function index($lang)
{
$langs =['en', 'de'];
if(in_array($lang, $langs)){
Session:set('lang', $lang);
return Redirect::back();
}
}
I set in middleware:(Lang.php)
public function handle($request, Closure $next)
{
if($lang = Session::get('lang')){
\Lang::setLocale($lang);
}
return $next($request);
}
Enable it in Http\Kernel.php:
protected $middleware = [
\App\Http\Middleware\Lang::class,
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
];
In my blade.php
{{ Lang::get('home.hello')}}
CURRENT: {{ Lang::getLocale() }} <br /> <br />
Germany | English
Please help.I do not see what I missing...
In field CURRENT when press Germany it shoul be 'de' and when press English it shoul be 'en' but when press Germany it still stay 'en'... (default is 'en' config/app.php -> 'locale' => 'en',)
Because you are using the session in your middleware, the values you need will not be available until the StartSession middleware sets up the session.
So you should add your middleware somewhere after that, like so:
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class, // Init session
\App\Http\Middleware\Lang::class, // Set locale
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
];
Aside from that you also have a small syntax error in your controller code. You forgot to add the second colon (:) for the scope resolution operator, when assigning the value to the session in your index controller method. So this:
Session:set('lang', $lang);
Should be this:
Session::set('lang', $lang);
Related
I am trying to build an installer such as if laravel catch PDOException of 1049, then it will redirect to a route that will enable the user to input database credentials. Later, I will write those to .env & run artisan command. But stuck at too many redirect error. Also I have a separate route file for this route with no middleware declared.
if ($exception instanceof \PDOException) {
$error_code = $exception->getCode();
if($error_code == 2002 || $error_code == 1049){
return response()->redirectToRoute('installer');
}
}
Got the issue solved. Actually the problem was I had modified the kernel.php and brought
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
to protected $middleware from protected $middlewareGroup => web for session message handling with toastr. Just reversed them as it was and everything works fine.
2nd Solution
Just look at your .env SESSION_DRIVER value. Set it to file. Also you can update 'driver' => env('SESSION_DRIVER', 'file') in your config/session.php for safety. By default, it's the default value. But in which application, I was working, someone changed it to database. That's why I was facing such issues.
EDIT: found the answer to all my questions: https://github.com/codezero-be/laravel-localized-routes (and, of course, 42).
I'm working on a multi-language website where the URL's contain localized strings. For instance:
https://site/en/members/john-doe
https://site/nl/leden/john-doe
(and so for every language)
In web.php, I prefixed my routes. Via middleware, I set the current language (retrieved from the first URL-segment). This works fine for everything (both in controllers and in blade)... except for the URLs generated by route(). They are always in the default language. Even though everything else is correctly localized. If I dump app()->getLocale() in a controller of blade it shows the correct language. Only the URLs generated by the route()-function are always in the default fallback language.
I've tried moving my middleware-class higher up in the middleware-groups list. That didn't make a difference.
web.php
Route::prefix('{lang}')->group(function() {
Route::get(trans('routes.members') . '/{username}/', 'MembersController#profile')
->name('members.show');
}
SetLanguage middleware
...
public function handle(Request $request, Closure $next)
{
$lang = $request->segment(1) ?? app()->getLocale();
//set app language
app()->setLocale($lang);
//set default url parameter
URL::defaults(['lang' => $lang]);
return $next($request);
}
...
App\Http\Kernel.php
...
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Laravel\Jetstream\Http\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\App\Http\Middleware\SetLanguage::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\UserLastSeenAt::class,
],
...
Got it (with a little help from a friend)
In the AppServiceProvider's boot function I've added
public function boot(Request $request)
{
//set app language
app()->setLocale($request->segment(1) ?? app()->getLocale());
}
& the SetLanguage-middleware now only contains
public function handle(Request $request, Closure $next)
{
//set default url parameter
URL::defaults(['lang' => app()->getLocale()]);
return $next($request);
}
That did the trick!
Great! I've done the same as you, but i have a problem when i chose to change the lang from switcher.
The switcher in blade:
<a class="ml-1 underline ml-2 mr-2" href="{{ route('change-lang')}}">
<span>{{ $locale_name }}</span>
</a>
The target route:
Route::get('/lang', [LangController::class, 'changeLang'])->name('change-lang');
The function in LangController:
public function changeLang($locale){
App::setLocale($locale);
session()->put('locale', $locale);
return redirect()->back();
}
Let me Exaplain:
If i navigate, for example, in about section and my url is "webname/it/azienda" and i switch language in EN my url becomes "webname/it/en";
I don't have issues if i change language if I have "webname/azienda" or "/" for home page.
I cannot change only the parameters LOCALE without adding it.
Background: I want to use the same code for multiple templated web sites. I want the code to identify the domain being accessed and then be able to set global variables to be used throughout the app.
At first, I created \config\global.php and had the logic in there working as expected:
$webUrl = url()->current();
/**************************************************
* Set Conference name based on URL
**************************************************/
$confId = 0;
$confName = '';
$confAbbrev = '';
if(strpos($webUrl, 'webdomain1') > 0) {
$confName = 'Domain 1 Full Name';
$confAbbrev = 'Dom1';
$confId = 25;
}
elseif(strpos($webUrl, 'webdomain2') >0) {
$confName = 'Domain 2 Full Name';
$confAbbrev = 'Dom2';
$confId = 35;
}
However, I eventually found that the "url()" was causing errors which prevented me from using "php artisan" commands throughout the app. After consulting my professional web developer co-worker, he said using a "global" config file for global variables was not best practice and recommended Middleware instead. He took control of my laptop and went REALLY fast...
In \app\Http\Kernel.php, he added the SetDomainVariables line at the end of $middlewareGroups:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\SetDomainVariables::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
He then created a new file: \app\Http\Middleware\SetDomainVariables.php
<?php
namespace App\Http\Middleware;
use Closure;
class SetDomainVariables
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
dd($request->getHttpHost());
return $next($request);
}
}
The results of "$request->getHttpHost()" is what I need... it returns the "foo" of www.foo.com. However, I don't know how to assign this value to a variable and return it via "$next" and then how I can put logic around it to set my global variables that I can reference in a Blade partial, etc.
Thank you!
EDIT: If using the \config\global.php is really the right way to accomplish what I want, could I just comment out the "url()" line whenever I want to do a "php artisan" command?
There are a few different ways to approach this problem, the easiest would be to make use of config -- which allows you to get and set configuration values dynamically.
Create a new file called config/sites.php containing an array for each of your sites, ensuring you start each domain with www and replace any . in the domain with - (because . in a config key will not work as Laravel uses a period to access child values).
return [
'default' => [
'id' => 15,
'name' => 'Default Full Name',
'abbreviation' => 'Def',
],
'www-webdomain1-com' => [
'id' => 25,
'name' => 'Domain 1 Full Name',
'abbreviation' => 'Web1',
],
'www-webdomain2-com' => [
'id' => 35,
'name' => 'Domain 2 Full Name',
'abbreviation' => 'Web2',
],
];
You now have configuration values for each of your sites accessible anywhere in your application, e.g: config('sites.www-webdomain1-com.name').
Add the following to your middleware:
public function handle($request, Closure $next)
{
$host = str_slug(starts_with('www.', $request->getHttpHost()));
$configuration = config("sites.{$host}") ?: config('sites.default');
config(['site' => $configuration]);
return $next($request);
}
You've now set the configuration value for the key site to the contents of the site configuration you set in config/sites.php for the request domain.
Anywhere you need to access the active site's configuration option use config('site.property'), e.g:
Hello, welcome to {{ config('site.name') }}
There are better ways to approach this problem, personally I would create a Site model and then use Route Model Binding however the way I've outlined here is very easy to set up for a beginner and should meet your needs.
The point is simple: I have a UserPolicy method that checks if a user wants to edit his/her own profile. So I did like this:
public function update(User $user, User $model)
{
return $user->id === $model->id;
}
And this is called in a UserController as it follows:
public function edit(User $user)
{
$this->authorize('update', $user);
return view('users.edit')->with('user', $user);
}
Everything is the same in a PostController and a PostPolicy, meant to check if a user can edit his/her own post and it works. The only difference is in their signature, since one has two users (the first one is the currently authenticated user injected by Laravel and the other is the instance I want to check it with) and the other has the above seen automatically injected authenticated user and a post instance. Anyway, it throws:
Symfony \ Component \ HttpKernel \ Exception \ AccessDeniedHttpException
This action is unauthorized.
I tried to dd($model) but I got the same exception.
Why? Thanks in advance!
EDIT
In my AuthServiceProvider is all set up, too:
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
Post::class => PostPolicy::class,
User::class => UserPolicy::class,
];
And so is my routes.php:
// Authentication Routes...
$this->post('login', 'Auth\LoginController#login')->name('login');
$this->post('logout', 'Auth\LoginController#logout')->name('logout');
// Registration Routes...
$this->post('register', 'Auth\RegisterController#register')->name('register');
// Password Reset Routes...
$this->get('password/reset', 'Auth\ForgotPasswordController#showLinkRequestForm')->name('password.request');
$this->post('password/email', 'Auth\ForgotPasswordController#sendResetLinkEmail')->name('password.email');
$this->get('password/reset/{token}', 'Auth\ResetPasswordController#showResetForm')->name('password.reset');
$this->post('password/reset', 'Auth\ResetPasswordController#reset');
Route::get('/', 'HomeController#index')->name('home');
Route::resource('posts', 'PostController');
Route::resource('users', 'UserController')->except('index', 'create', 'store');
Everything above is called right here:
#if ($user->id == Auth::id())
<a class="btn btn-link float-right p-0"
href="{{ route('users.edit', Auth::id()) }}">
<i class="fas fa-cog"></i>
Edit profile
</a>
<br><br><br>
#endif
I'm giving an answer myself: I tried to write the model and policy's full paths instead of registering the policies by the classes' names and it works (I don't know why, of course).
I did like this:
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
'App\User' => 'App\Policies\UserPolicy',
'App\Post' => 'App\Policies\PostPolicy',
];
Anyway, thanks everyone for trying to help me. Hope it will help someone else one day!
I just solved the same issue after fighting a whole day. Using full paths for register did not work for me. I fixed it by modifying my routes. I post my solution here hoping it may help someone someday.
If your routes are not protected by the authentication middleware, an AccessDeniedException will be thrown before applying your policies. The reason is that if your request comes in directly, you will never be treated as a logged-in user, so that you will be kicked off when trying to call $this->authorize('update') within the controller.
Route::middleware("auth:sanctum")->group(function () {
Route::post('/member/{id}', [MembersController::class, 'update']);
// ... and other path.
});
I created a middleware class named PathParser which runs on every request. The purpose is to handle requests for "vanity URL paths" that we allowed users to create in our pre-Laravel vanilla PHP app. For example: A user created a URL path such as: http://example.com/i-love-this-place
What PathParser does is to check for 404 responses, and then look to see if the URL path matched one of our old vanity paths. Like this:
class PathParser
{
public function handle($request, Closure $next, $guard = null)
{
$next_response = $next($request);
$status_code = $next_response->getStatusCode();
if ($status_code === 404) {
$script_url = $request->server("SCRIPT_URL");
$vanity_controller = new VanityController();
$found_vanity_id = Search::findVanityPath($script_url);
if (!empty($found_vanity_id)) {
$next_response = response()->make($vanity_controller->one($found_vanity_id));
}
}
return $next_response;
}
}
Assume the following:
A user has never created a URL path that would conflict with any route
I must support a number of existing (pre-Laravel) vanity URL paths that are out in the wild -- posted to social media and so on.
In Kernel.php, I have the following:
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\PathParser::class,
//\Illuminate\Session\Middleware\StartSession::class,
//\Illuminate\View\Middleware\ShareErrorsFromSession::class,
];
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
In the $middleware array I tried added StartSession and ShareErrorsFromSession (uncommenting the 2 lines above), and this works partially. But with two major problems:
Auth::user is null, even for requests made to vanity paths by logged in users
$errors no longer gets populated on form submission (such as on the registration and login pages) when a user submits incorrect/invalid information
Is there a way both to check the route on all requests and get ahold of the authenticated user, also preserving $errors?
I have a feeling I don't understand the request lifecycle well enough to succeed. But maybe there is a way?
If it's not possible to do what I require, then use of 302 redirection to a standardized prefixed path (such as http://example.com/vanity/i-love-this-place) is a fine solution. But I'm hoping there is another means.
A couple suggestions:
If you don't need auth/sessions/etc, then you can just handle the Symfony\Component\HttpKernel\Exception\NotFoundHttpException exception inside your application's exception handler.
In app/Exceptions/Handler.php, modify the render() method to look something like:
public function render($request, Exception $e)
{
if ($e instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) {
// your code that returns a Response
}
return parent::render($request, Exception $e);
}
If you do need auth/sessions/etc, I would suggest creating a "catchall" route at the end of your routes file. For example, as the very last line in your routes/web.php file, put:
Route::any('{catchall}', 'VanityController#handle')->where('catchall', '(.*)');
Then, inside your VanityController, have a handle method that looks like:
public function handle(Request $request, $url)
{
// logic to search and render your vanity page for $url
// if no vanity page was found:
throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException();
}