changing url, and locale on the fly with laravel - laravel

My code in laravel to handle multiple language is:
$languages = array('it-IT','en-GB','fr-FR');
$lingua = Request::segment(1);
if(in_array($lingua, $languages)){
\App::setLocale($lingua);
}else{
$lingua = 'it-IT';
}
Route::group(array('prefix' => $lingua), function()
{
Route::get('/', array('as' => 'home', 'uses' => 'ItemController#menu'));
Route::get('/{idcampo}','ItemController#show');
});
How can i:
1)Make the page start always with it-IT as default. (i need it because I use $lingua to fetch from a database) so i can't have that null. Should I use a redirect::to / to /it-IT?
2) change url and language(app:locale) on he fly with a link in the upper section of every pages. withouth returning to the home.
3) to link pages I learn to use:
URL::route('home')
but how to do it when the link change with the entry of a database (for example my link is {{ URL::to($lingua. '/'. $campo[1].'/') }}) I need to use
URL::action('ItemController#show', ($lingua. '/'. $campo[1].'/'))
EDIT:
OK at the top of my pages there is a link to change language on the fly.
Italian //
English //
French
I create a controller clled LanguageController
<?php
class LanguageController extends BaseController {
public function select($lingua)
{
// Store the current language in the session
Session::put('lingua', $lingua);
return Redirect::back(); // redirect to the same page, nothing changes, just the language
}
}
I create a route:
Route::get('lingua/{lingua}', 'LanguageController#select');
Route::get('/', array('as' => 'home', 'uses' => 'ItemController#menu'));
Route::get('/mondo/','ItemController#mondo');
Route::get('/{idcampo}','ItemController#show');
I have my ItemController#menu
public function menu()
{ $linguadefault='it-IT';
$lingua = Session::get('lingua',$linguadefault);
$data = DB::table('campo')->lists('id');
return View::make('index')->with('campo',$data)->with('lingua',$lingua);
}
1) I don't understand why i need to route at lingua/{lingua} if i never route there but i use a url:action to a controller directly.
2) now i need to add
$linguadefault='it-IT';
$lingua = Session::get('lingua',$linguadefault);
at the beginning of every function to have a lingua variable in my page right?
3) now my language seems stucked to french and i can't change it anymore.

I would not use the language in the URL all the time, you can just switch languages when you need and persist it:
1) Use Session to persist the language chosen:
// Set the default language to the current user language
// If user is not logged, defaults to Italian
$linguaDefault = Auth::check()
? Auth::user()->lingua
: 'it-IT';
/// If not stored in Session, current language will be the default one
\App::setLocale(Session::get('lingua', $linguaDefault));
To have the language always set in your application, you can put this code in your file
app/start/global.php
And you don't need to add this anywhere else. So it will use it in this order:
a) Language stored in Session (selected online)
b) Language user has in database
c) Italian
2) To change the language you create a route:
Route::get('lingua/{lang}', 'LanguageController#select');
Your links
URL::action('LanguageController#select', 'it-IT')
URL::action('LanguageController#select', 'en-GB')
URL::action('LanguageController#select', 'fr-FR');
And in your controller you just have to do:
public function select($lang)
{
// Store the current language in the session
Session::put('lingua', $lang);
return Redirect::back(); // redirect to the same page, nothing changes, just the language
}
3) This way you don't need your language in all your URLs, you don't have to deal with it in all your routes. If your user changes the language in database, you just:
$user->save();
Session::put('lingua', $user->lingua);
return Redirect::route('home'); // or anything else

Related

Laravel 8 - friendly url that call multiple controllers depending on match (products, categories, pages) - How to design it?

i would like to build a route that catch clean seo friendly url and call correct controller to display page. Examples:
https://mypage.com/some-friendly-url-separated-with-dashes [PageController]
https://mypage.com/some-cool-eletronic-ipod [ProductController]
https://mypage.com/some-furniture-drawers [CategoryController]
So I have in app route:
Route::get('/{friendlyUrl}', 'RouteController#index');
Each friendly url is a unique url(string) so there is no duplicate between pages/products/categories. There is also no pattern between urls - they could be any string used in seo(only text plus dashes/ sometimes params).
Is it wise to build one db table that keeps all urls in on place with info what to call ( url | controller_name | action_name) - as an example.
Another question is - how to call different controllers depending on url used? (for above example -> RouteController catch friendly urls -finds match in db table -> then calls correct controller)
Many thanks for any help.
Have a nice day
Mark
There's two approaches you can take to this.
Proactive:
In web.php
$slugs = Product::pluck('slug');
foreach ($slugs as $slug) {
Route::get($slug, 'ProductController#index');
}
$slugs = Category::pluck('slug');
foreach ($slugs as $slug) {
Route::get($slug, 'CategoryController#index');
}
$slugs = Page::pluck('slug');
foreach ($slugs as $slug) {
Route::get($slug, 'PagesController#index');
}
Then you can determine the product in the appropriate controler via e.g.
$actualItem = Product::where('slug', request()->path())->first();
The downside to this approach is that all routes are registered on every request even if they are not used meaning you hit the database on every request to populate them. Also, routes can't be cached when using this approach.
Reactive:
In this approach you use the fallback route:
In web.php:
Route::fallback(function (Request $request) {
if (Page::where('slug', $request->path())->exists()) {
return app()->call([ PageController::class, 'index' ]);
}
if (Category::where('slug', $request->path())->exists()) {
return app()->call([ CategoryController::class, 'index' ]);
}
if (Product::where('slug', $request->path())->exists()) {
return app()->call([ ProductController::class, 'index' ]);
}
abort(404);
});
You need create a table call slugs.
Then create a unique slug (can be auto generated or specified) for each page, product, category.
slug records also have columns to get Controller and params, ex: type and id
It'd be better if you just use a prefix for each type like this:
https://mypage.com/pages/some-friendly-url-separated-with-dashes [PageController]
https://mypage.com/products/some-cool-eletronic-ipod [ProductController]
https://mypage.com/category/some-furniture-drawers [CategoryController]
Then for achieving this, create three routes like this
Route::get('pages/{friendlyUrl}', 'PageController#index');
Route::get('products/{friendlyUrl}', 'ProductController#index');
Route::get('category/{friendlyUrl}', 'CategoryController#index');
These URLs would be SEO friendly

Avoiding Multiple Routes for Different Languages

I'm translating my site into different languages. By the default, the language will be English, which I'm using the following route to return the "welcome" view:
Route::get('welcome', function ()
{
return view('welcome');
});
For my other languages, I'm using this other route:
Route::get('welcome/{locale}', function ($locale)
{
App::setLocale($locale);
return view('welcome');
});
Is there any way I can combine those two routes into one? For example, if the route is "welcome" or "welcome/en", return the "welcome" view in English, the default language.
However, if the route is "welcome/fr", the "welcome" view should be returned in French.
I'm going to have hundreds of routes so I would love being able to combine my routes.
The default language for your application is stored in the config/app.php configuration file. You may modify this value to suit the needs of your application. You may also change the active language at runtime using the setLocale method on the App facade:
Route::get('welcome/{locale}', function ($locale) {
if (! in_array($locale, ['en', 'es', 'fr'])) {
abort(400);
}
App::setLocale($locale);
//
});
You may configure a "fallback language", which will be used when the active language does not contain a given translation string. Like the default language, the fallback language is also configured in the config/app.php configuration file:
'fallback_locale' => 'en',
Occasionally you may need to specify a route parameter, but make the presence of that route parameter optional. You may do so by placing a ? mark after the parameter name. Make sure to give the route's corresponding variable a default value:
Route::get('welcome/{locale?}', function ($locale = null) {
//
});

Laravel: Best way to implement dynamic routing in routes.php based on environment variable?

My aim is to roll out a big re-theming / re-skinning (including new URL routing) for a Laravel v5 project without touching the existing business logic (as much as possible that is).
This is my current approach:
I placed a APP_SKIN=v2 entry in my .env file
My app\Http\routes.php file has been changed as follows:
if (env('APP_SKIN') === "v2") {
# Point to the v2 controllers
Route::get('/', 'v2\GeneralController#home' );
... all other v2 controllers here ...
} else {
# Point to the original controllers
Route::get('/', 'GeneralController#home' );
... all other controllers
}
All v2 controllers have been placed in app/Http/Controllers/v2 and namespaced accordingly
All v2 blade templates have been placed in resources/views/v2
the rest of the business logic remains exactly the same and shared between the "skins".
My question: Is there a "better" way to achieve the above?. Please note that the idea here is to affect as few files as possible when doing the migration, as well as ensure that the admin can simply change an environment variable and "roll back" to the previous skin if there are problems.
Within app/Providers/RouteServiceProvider.php you can define your routes and namespaces, etc. This is where I would put the logic you talked about (rather than in the routes file):
protected function mapWebRoutes()
{
if (App::env('APP_SKIN') === 'v2') {
Route::group([
'middleware' => 'web',
'namespace' => $this->namespace,
], function ($router) {
require base_path('routes/web_v2.php');
});
} else {
// ...
}
}
This way, you can create separate route files to make it a bit cleaner.
Aside from that, I personally can't see a better solution for your situation than what you described as I'm guessing your templates want to vary in the data that they provide, which if that is the case then you will need new controllers - otherwise you could set a variable in a middleware which is then retrieved by your current controllers which could then determine which views, css and js are included. This would mean you would only need to update your existing controllers, but depending upon your current code - this could mean doing just as much work as your current solution.
Routes pass through Middleware. Thus you can achieve this by BeforeMiddleware as follows
public function handle($request, Closure $next)
{
// Get path and append v2 if env is v2
$path = $request->path();
$page = $str = str_replace('', '', $path); // You can replace if neccesary
// Before middleware
if (env('APP_SKIN') === "v2")
{
return $next($request);
}
else
{
}
}

Laravel route acts craizy

Hy i have this http://laravel.io/bin/jaPB
The problem is when i go to:
domain.com -> it serves the homepage (wich is ok)
domain.com/foo -> it serves a subpage (still ok)
but when i go from one of those to:
domain.com/en -> it gives an error (not ok)
But after hitting refresh its ok.
So again when i'm on domain.com/en first time error after refresh ok
Same goes to subpage like domain.com/en/contact first time error after refresh ok
I would point out that the error says first time it tries to go to PublicPageController#subpage
but this shouldn't happen when i go to domain.com/en it should need to go to PublicPageController#homepage
Any idea ?
Thank you all.
My guess here form looking at the way you set up the locale-based routes is that the Session::get('urilang) isn't set the first time you visit, hence the error, and is only set once you've been to a page first.
Now, I haven't yet had to deal with multilingual sites, but as far as I'm aware the way you're doing it is not the correct way. Instead think of the lang key as a URI parameter, and use the filter to validate and set it for the routes. Something a bit like the below code:
// Main and subpage - not default language
Route::group(array('prefix' => '{lang}', 'before' => 'detectLanguage'), function () {
Route::get('', 'PublicPage#homepage');
Route::get('{slug}', 'PublicPage#subpage');
});
// Main and subpage - default language
Route::group(array('before' => 'setDefaultLanguage'), function () {
Route::get('/', 'PublicPage#homepage');
Route::get('/{slug}', 'PublicPage#subpage');
});
Route::filter('detectLanguage', function($route, $request, $response, $value){
// hopefully we could do something here with our named route parameter "lang" - not really on sure the details though
// set default
$locale = 'hu';
$lang = '';
// The 'en' -> would come from db and if there is more i would of corse use in array
if (Request::segment(1) == 'en')
{
$lang = 'en';
$locale = 'en';
}
App::setLocale($locale);
Session::put('uriLang', $lang);
Session::put('locale', $locale);
});
Route::filter('setDefaultLanguage', function($route, $request, $response, $value){
App::setLocale('hu');
Session::put('uriLang', '');
Session::put('locale', 'hu');
});
I don't know if you can use a segment variable in a Route::group prefix, but you should certainly have a go at it as it'd be the most useful.
That said, I wouldn't advise setting up default language routes that mimic specific language routes but without the language segment. If I were you, I'd set up a special root route that will redirect to /{defaultlang}/ just so you have fewer routing issues.

Is there a way to set a default url parameter for the Route class in Laravel 4?

I'm trying to set up sub-domain based routing in Laravel 4 and I've hit a bit of an annoyance...
My route group looks like this:
Route::group(array('domain' => '{company}.domain.com'), function() {
// ...
});
Which seems to work fine, however, I need to specify the company parameter for every route/url I generate. I.e:
{{ HTML::linkRoute('logout', 'Logout', ['company' => Input::get('company')]) }}
Is there any way to specify the company parameter as static/global, so it is automatically added to any links I specify, unless otherwise overwritten/removed?
Unfortunately, no (I haven't seen any evidence in the router or HTMLBuilder that you can). You could, however, make an HTML macro... Example:
HTML::macro('lr', function($link, $title) {
$company = !empty(Input::get('company')) ? Input::get('company') : "";
return HTML::linkRoute($link, $title, ['company' => $company]);
});
Then call it - instead of HTML::linkRoute, use HTML::lr('logout', 'Logout')
Just an idea.

Resources