Master routing page in Laravel - php

Former I had created my own MVC and there was an index.php that all pages passed from the way of it. I mean, I could put a redirect (header('Location: ..');) into index.php, and then none of my website pages can not be opened.
Now I use Laravel and I need a core-page (like index.php). Because my new website supports multi-languages. Here is my current code:
// app/Http/routes.php
Route::get('/{locale?}', 'HomeController#index');
// app/Http/Controllers/HomeController.php
public function index($locale = null)
{
if(!is_null($locale)) {
Lang::setLocale($locale);
}
dd(Lang::getLocale());
/* http://localhost:8000 => output: en -- default
* http://localhost:8000/abcde => output: en -- fallback language
* http://localhost:8000/en => output: en -- defined language
* http://localhost:8000/es => output: es -- defined language
* http://localhost:8000/fa => output: fa -- defined language
*/
}
As you see, in my current algorithm, I need to check the language that the user has set for each route. Also my website has almost 30 routes. I can do that manually 30 times for each route, but I think there is a way which lets me to do that once for all routes. Isn't there?
In other word, how can I set current language (the user has set) for each page? Should I check/set it for each route separately?

There is a smarter way in Laravel for solve your trouble. It is called Middleware. So you can just create LangMiddleware and put your logic inside.
Something like
public function handle($request, Closure $next, $locale = null)
{
if ($locale && in_array($locale, config('app.allowed_locales'))) {
Lang::setLocale($locale);
}
else{
Lang::setLocale(config('app.fallback_locale'));
}
return $next($request);
}

Related

Routes are conflicting in Laravel framework php

I have already
a/{id}
api
Now I want
a/b
But it is not hitting a/b, it is hitting a/{id} and taking b as {id}
How can I create a/b assuming I am not allowed to change a/{id}?
Framework Laravel.
$apiRoutes = [
// Dev routes
'get_a_by_id' => ['get', 'a/{id}','AController#getA'],
// App routes
'fetch_all_b' => ['get','a/b', 'BController#getB'],
]
This is my code route.php
Even reordering also doesn't work.
What you can do is
First place a/b above a/{id} and add ->where(['id' => '[0-9]+'); this will make sure that the route a/{id} will trigger only if there is numeric value.
You can change regex based on your needs.
Route::get('a/b', function () {
//code
});
Route::get('a/{id}', function ($id) {
//code
})->where(['id' => '[0-9]+');
It depends in which order you define them, you need to first create the specific route a/b then below the wildcard one.
Route::get('a/b', function () {
dd('testing b');
});
Route::get('a/{id}', function ($id) {
dd('testing', $id);
});
The short answer is to define a/b before a/{id}
Here's my example:
Route::get('/v1/smsportal/search/{type?}/{search?}', 'SMSPortals#search');
Route::get('/v1/smsportal/{id?}', 'SMSPortals#get');
Route::post('/v1/smsportal', 'SMSPortals#save');
Route::post('/v1/smsportal/{id?}', 'SMSPortals#update');
Route::delete('/v1/smsportal/{id?}', 'SMSPortals#delete');
/v1/smsportal/search will be called before /v1/smspotal/{id} if I call for search.

Laravel 5.5 use of env string in route name screws Route::has()

For absolute testing purpose (it's probably not a consistent behaviour I'll keep on my code) I try to use a custom admin pannel route set with the .env file (As it seemed relevant in securing the whole thing, but not so sure anymore).
As I want that people visiting the site can see that admin pannel in guest mode, I thought it would be cool to set a rerouting middleware that simply put a guest after that .env set admin route. Guests who try to get to /admin/jobs would end on /admin/guest/jobs with lowered permission controller.
The code below works fine without the .env thingy.
It's set like this in \Route\web.php
Route::namespace('Admin')->middleware('auth')->group(function () {
$adminRoute = config('app.admin-route');
$adminRoute = (preg_match('/[\/].*/', $adminRoute)) ? $adminRoute : '/' . $adminRoute;
Route::middleware('isadmin')->group(function () use ($adminRoute) {
Route::get($adminRoute, 'AdminController#index')->name('adminPanel');
Route::get($adminRoute . '/test', function () {
echo 'test';
});
});
Route::get($adminRoute . '/guest/{where?}', 'AdminController#guest')->where('where', '.*')->name('adminAsGuest');
});
And the corresponding isAdmin middleware:
public function handle($request, Closure $next)
{
$adminRoute = config('app.admin-route');
$adminRoute = (preg_match('/\/.*/', $adminRoute)) ? $adminRoute : '/' . $adminRoute;
// check if authentified
if (Auth::check())
{
// check if admin
if (Auth::user()->role == 1) {
return $next($request);
}
else
{
$route = "/".$request->route()->uri;
// check if route exists
if (Route::has($route)) {
// trim the route after admin and puts a guest inside
$redirect = preg_replace("/\\".$adminRoute."/", "", $route);
return redirect($adminRoute . '\/guest/' . $redirect);
}
else {
// if it doesn't, let it go to the laravel error
return $next($request);
}
}
}
// if auth middleware was used on same route, won't go there anyways
// if not, redirect to root route
return redirect('/');
}
I'm on a struggle there as Route::has($route) doesn't trigger correctly as long as I use env("APP_ADMIN_ROUTE") as the root of the admin routes.
Here's my debugging outputs just before if (Route::has($route))
Auth::check()
true
Auth::user()->role
0
$route
"/admin/test"
Route::has($route);
false
vagrant#ubuntu-xenial:/var/www/html$ wphp artisan route:list
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+------------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+------------------+
| | GET|HEAD | / | | App\Http\Controllers\WelcomeController#index | web |
| | GET|HEAD | admin | adminPanel | App\Http\Controllers\Admin\AdminController#index | web,auth,isadmin |
| | GET|HEAD | admin/guest/{where?} | adminAsGuest | App\Http\Controllers\Admin\AdminController#guest | web,auth |
| | GET|HEAD | admin/test | | Closure | web,auth,isadmin |
I know it would certainly be more consistent to go for admin controllers in which I constantly track the role of the user connected, and give there or not the right to modify things. But I'm really curious to know if there's any possible way to work it like I tried here.
I've seen Zizaco/entrust and it would certainly work great on a simplier approach, and it's my next step if there's no positive answer to my current issue :)
Here's my first question on StackOverflow. I used great search for that precise thing without success. I apologize if I missed an obvious answer somewhere.
Edit: Updated the code after Joel Hinz's comment.
// \Config\app.php
'admin-route' => env('APP_ADMIN_ROUTE', 'admin'),
It's generally better to let the config files read the .env variables, and then load the settings from the config files where you actually need them. That way, they can be cached, and you virtually always know that they've already been read properly instead of having to wonder where they'll work and where they won't.
In your case, just something like this should be enough:
// in .env
APP_ADMIN_ROUTE=something
// in e.g. config/app.php
'admin-route' => env('APP_ADMIN_ROUTE');
// in the middleware
$adminRoute = config('app.admin-route');
I'm not 100 % sure this is actually your issue, but it's worth a shot - and even if it doesn't work, it's still a best practice. :)
Okay, first question, first anwer, looks like I didn't explore all possibilities.
The issue wasn't using variables in route URI definition, but an improper use of Route::has(). This works only with route names, and not route URI. Proper way to check if a route exists, according to https://laracasts.com/discuss/channels/general-discussion/check-if-route-exists is to use a Route::checkRoutes->match($testValue) with a $testValue member of Illuminate\Http\Request.
Route::checkRoutes->match($testValue) will return a Illuminate\Routing\Route instance with the first match, and an empty value if there's no match.
In a middleware handle() context it can articulates like this:
$route = "/" . $request->route()->uri;
// Shape your test route URI in a request
$testValue = $request->create($route);
// check if route exists
if (Route::getRoutes()->match($testValue)) {
// statements here
}
So, the correct middleware complete code is:
public function handle($request, Closure $next)
{
$adminRoute = config('app.admin-route');
$adminRoute = (preg_match('/\/.*/', $adminRoute)) ? $adminRoute : '/' . $adminRoute;
// check if authentified
if (Auth::check())
{
// check if admin
if (Auth::user()->role == 1) {
return $next($request);
}
else
{
$route = "/" . $request->route()->uri;
$nreq = $request->create($route);
// check if route exists
if (Route::getRoutes()->match($nreq)) {
// trim the route after admin and puts a guest inside
$redirect = preg_replace("/\\".$adminRoute."/", "", $route);
return redirect($adminRoute.'/guest' . $redirect);
}
else {
// if it doesn't, let it go to the laravel error
return $next($request);
}
}
}
// if auth middleware was used on same route, won't go there anyways
// if not, redirect to root route
return redirect('/');
}
What leads me in the way of thinking this is only useless code block as the middleware will only trigger on an existing /admin/something route URI, and testing if route exists there makes no point.
Well, anyways, thanks to everyone who took time to read that, see you around.

Laravel route url with query string

On laravel 4 I could generate a url with query strings using the route() helper. But on 4.1 instead of:
$url = url('admin.events', array('lang' => 'en'));
// admineventsurl/?lang=en
I get:
$url = url('admin.events', array('lang' => 'en'));
// admineventsurl/en
I did some research and all laravel methods to generate url are using the parameters like that. How can I generate the url with query strings?
Laravel's route() and action() helper methods support URL query params. The url() helper method, unfortunately does not.
Simply provide an array with key => value pairs to the route parameters. For example:
route('products.index', ['manufacturer' => 'Samsung']);
// Returns 'http://localhost/products?manufacturer=Samsung'
You can also still include your route parameters (such as ID's and models) to accompany these parameters:
route('products.show', [$product->id, 'model' => 'T9X']);
// Returns 'http://localhost/products/1?model=T9X'
Basically, any elements in the array that contain string keys will be treated as query parameter (/products?param=value). Anything with an integer array key will be treated as a URL argument (/products/{arg}).
This is also supported in action methods:
action('ProductController#index', ['manufacturer' => 'Samsung']);
You can also supply query parameters inside the link_to_route() and link_to_action() methods:
link_to_route('products.index', 'Products by Samsung', ['model' => 'Samsung');
link_to_action('ProductController#index', 'Products by Samsung', ['model' => 'Samsung']);
2019 - EDIT:
If you can't use route() or action(), you can generate a URL with query params using the Arr::query() helper:
url('/products?').\Illuminate\Support\Arr::query(['manufacturer' => 'Samsung']);
// Returns 'http://localhost/products?manufacturer=Samsung'
Or:
url('/products?').http_build_query(['manufacturer' => 'Samsung'], null, '&', PHP_QUERY_RFC3986);
// Returns 'http://localhost/products?manufacturer=Samsung'
Or create a simple helper function:
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
function url_query($to, array $params = [], array $additional = []) {
return Str::finish(url($to, $additional), '?') . Arr::query($params);
}
Then call it:
url_query('products', ['manufacturer' => 'Samsung']);
// Returns 'http://localhost/products?manufacturer=Samsung'
url_query('products', ['manufacturer' => 'Samsung'], [$product->id]);
// Returns 'http://localhost/products/1?manufacturer=Samsung'
Side note.
I disagree with #Steve Bauman's idea (in his answer) that one rarely needs querystring urls, and think that Laravel should at least consider adding querystring functionality (back) in. There are plenty of cases when you want a querystring url rather than a param based "pretty url". For example, a complex search filter...
example.com/search/red/large/rabid/female/bunny
...may potentially refer to the same exact set of rodents as...
example.com/search/bunny/rabid/large/female/red
...but any way you look at it (programming, marketing analytics, SEO, user-friendliness), it's kinda terrible. Even though...
example.com/search?critter=bunny&gender=female&temperament=rabid&size=large&color=red
...is longer and "uglier", it actually is better in this not-so-rare case. Net: Friendly URLs are great for some things, querystrings are great for others.
Answer to the original question...
I needed a "querystring" version of url() -- so I copied the function, modified it, and stuck it in /app/start/global.php:
/**
* Generate a querystring url for the application.
*
* Assumes that you want a URL with a querystring rather than route params
* (which is what the default url() helper does)
*
* #param string $path
* #param mixed $qs
* #param bool $secure
* #return string
*/
function qs_url($path = null, $qs = array(), $secure = null)
{
$url = app('url')->to($path, $secure);
if (count($qs)){
foreach($qs as $key => $value){
$qs[$key] = sprintf('%s=%s',$key, urlencode($value));
}
$url = sprintf('%s?%s', $url, implode('&', $qs));
}
return $url;
}
Example:
$url = qs_url('sign-in', array('email'=>$user->email));
//http://example.loc/sign-in?email=chris%40foobar.com
Note: It appears that the url() function is pluggable, that is, you can replace it. Look in vendor/laravel/framework/src/Illuminate/Support/helpers.php: the url function is wrapped in a if ( ! function_exists('url')) conditional. But you would probably have to jump through hoops to do it (i.e. have laravel load it before its version.)
Cheers,
Chris
The following was what I needed to do:
I handle all of my routing in a service provider, where I had defined the following function:
private function registerRestfulController($prefix, $controllerClass)
{
Route::controller($prefix, $controllerClass, $controllerClass::getRouteNames());
}
getRouteNames is a static method on my BaseController that conventionally returns routes so that RESTful controllers can have automatic named routes.
The problem I was running into was that this defined the set of wildcard matchers on the route itself - in order to avoid that, I add the following to the private function above:
foreach ($controllerClass::getRoutesNames() as $name) {
$route = Route::getRoutes()->getByName($name);
$cleanUri = preg_replace('/\/\{\w*\?\}/', '', $route->getUri());
$route->setUri($cleanUri);
}
This loads all the routes you are registering at the time and immediately removes wildcards from the URI. You could easily pass a boolean or "white-list" of route names that you want to preserve wildcards for, so that it doesn't stomp all over the Laravel default without the intention. Once you run this, it automatically starts working with query string variables, which I find far preferable to path variables in this instance.
A simple way to do this, specially to use with jQuery Autocomplete, it's modify the Controller with a condition to check if has 'term' in the $request:
(Controller file)
public function list_for_autocomplete(Request $request)
{
if ($request->has('term')) {
return YourModel::select('column_name as value')
->where('column_name', 'like', '%' . $request->input('term') . '%')
->get()
}
}

How to remove action part in zend framework URLs

I`m using zend framework and my urls are like this :
http://target.net/reward/index/year/2012/month/11
the url shows that I'm in reward controller and in index action.The rest is my parameters.The problem is that I'm using index action in whole program and I want to remove that part from URL to make it sth like this :
http://target.net/reward/year/2012/month/11
But the year part is mistaken with action part.Is there any way ?!!!
Thanks in advance
Have a look at routes. With routes, you can redirect any URL-format to the controller/action you specify. For example, in a .ini config file, this will do what you want:
routes.myroute.route = "reward/year/:myyear/month/:mymonth"
routes.myroute.defaults.controller = reward
routes.myroute.defaults.action = index
routes.myroute.defaults.myyear = 2012
routes.myroute.defaults.mymonth = 11
routes.myroute.reqs.myyear = "\d+"
routes.myroute.reqs.mymonth = "\d+"
First you define the format the URL should match. Words starting with a colon : are variables. After that you define the defaults and any requirements on the parameters.
you can use controller_plugin to control url .
as you want create a plugin file (/library/plugins/controllers/myplugin.php).
then with preDispatch() method you can get requested url elements and then customize that for your controllers .
myplugin.php
class plugins_controllers_pages extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$int = 0;
$params = $this->getRequest()->getParams();
if($params['action'] != 'index' ) AND !$int)
{
$int++;
$request->setControllerName($params['controller']);
$request->setActionName('index');
$request->setParams(array('parameter' => $params['action']));
$this->postDispatch($request);
}
}
}

What's the best logic for switching language in Laravel?

I'm using Laravel localization to provide two different languages. I've got all the path stuff set up, and mydomain.com/en/bla delivers English and stores the 'en' session variable, and mydomain.com/he/bla delivers Hebrew and stores the 'he' session variable. However, I can't figure out a decent way to provide a language-switching link. How would this work?
I've solved my problem by adding this to the before filter in routes.php:
// Default language ($lang) & current uri language ($lang_uri)
$lang = 'he';
$lang_uri = URI::segment(1);
// Set default session language if none is set
if(!Session::has('language'))
{
Session::put('language', $lang);
}
// Route language path if needed
if($lang_uri !== 'en' && $lang_uri !== 'he')
{
return Redirect::to($lang.'/'.($lang_uri ? URI::current() : ''));
}
// Set session language to uri
elseif($lang_uri !== Session::get('language'))
{
Session::put('language', $lang_uri);
}
// Store the language switch links to the session
$he2en = preg_replace('/he\//', 'en/', URI::full(), 1);
$en2he = preg_replace('/en\//', 'he/', URI::full(), 1);
Session::put('he2en', $he2en);
Session::put('en2he', $en2he);
This is a post i posted originally on the laravel forums, but maybe it will help somebody else, so i post it here also.
I had some trouble with building a easy language switcher for my app, and the info on the forums where a little bit old (some posts), so i made this simple piece of code that makes it supereasy to change language on your app on the fly.
I have the language strings in my views as following:
{{ __('languagefile.the_language_string'); }}
And I get the languages with a URL, i think this is the best way, also its good for seo and for links that people share. Example:
www.myapp.com/fi/support (Finnish)
www.myapp.com/en/support (English)
www.myapp.com/sv/support (Swedish)
Ok, so the problem was that i wanted a easy way to change the language on the fly, without having to mess with sessions and cookies. Heres how i made it:
Make a library in your libraries folder called chooselang.php
Insert this code inside:
class Chooselang extends HTML {
/**
* Generate a Language changer link.
*
* <code>
* // Generate a link to the current location,
* // but still change the site langauge on the fly
* // Change $langcode to desired language, also change the Config::set('application.language', 'YOUR-LANG-HERE')); to desired language
* // Example
* echo Chooselang::langslug(URI::current() , $langcode = 'Finnish' . Config::set('application.language', 'fi'));
* </code>
*
* #param string $url
* #param string $langcode
* #param array $attributes
* #param bool $https
* #return string
*/
public static function langslug($url, $langcode = null, $attributes = array(), $https = null)
{
$url = URL::to($url, $https);
if (is_null($langcode)) $langcode = $url;
return '<a href="'.$url.'"'.static::attributes($attributes).'>'.static::entities($langcode).'</a>';
}
}
After this you are ready for getting your url switcher URL:s generated. Simply add them as you whould any other Blade links.
Example how to generate links for Finnish, Swedish and English (with Blade)
{{ Chooselang::langslug(URI::current() , $langcode = 'Fin' . Config::set('application.language', 'fi')); }}
{{ Chooselang::langslug(URI::current() , $langcode = 'Swe' . Config::set('application.language', 'sv')); }}
{{ Chooselang::langslug(URI::current() , $langcode = 'Eng' . Config::set('application.language', 'en')); }}
The above will generate URL:s that are always on the current page, and change the lang slug to the one you want. This way the language changes to the one you want, and the user naturally stays on the same page. The default language slug is never added to the url.
Generated urls look something like:
Fin
Swe
Eng
PS. The links are specially useful if you add them to your master template file.
You could have a Route to hand language change, for example:
Route::get('translate/(:any)', 'translator#set');
Then in the set action in the translator controller could alter the session, depending on the language code passed via the URL.
You could also alter the configuration setting by using
Config::set('application.language', $url_variable');
Controller Example - translate.php
public function action_set($url_variable)
{
/* Your code Here */
}
Just in case for future users if you want to use package for localization There is a great package at https://github.com/mcamara/laravel-localization. which is easy to install and has many helpers.
This question still comes in Google search, so here's the answer if you're using Laravel 4 or 5, and mcamara/laravellocalization.
<ul>
<li class="h5"><strong><span class="ee-text-dark">{{ trans('common.chooselanguage') }}:</span></strong> </li>
#foreach(LaravelLocalization::getSupportedLocales() as $localeCode => $properties)
<li>
<a rel="alternate" hreflang="{{$localeCode}}" href="{{LaravelLocalization::getLocalizedURL($localeCode) }}">
<img src="/img/flags/{{$localeCode}}.gif" /> {{{ $properties['native'] }}}
</a>
</li>
#endforeach
</ul>
NOTE that this example shows flags (in public/img/flags/{{locale}}.gif), and to use it you will need a bit of .css, but you can modify it to display the text if you want...
FYI. The mcamara/laravellocalization documentation has examples and a LOT of helpers, so look through the documentation on github. (https://github.com/mcamara/laravel-localization)
Try use Session's. Somthing like this:
Controller:
class Language_Controller extends Base_Controller {
function __construct(){
$this->action_set();
parent::__construct();
}
private function checkLang($lang = null){
if(isset($lang)){
foreach($this->_Langs as $k => $v){
if(strcmp($lang, $k) == 0) $Check = true;
}
}
return isset($Check) ? $Check : false;
}
public function action_set($lang = null){
if(isset($lang) && $this->checkLang($lang)){
Session::put('lang', $lang);
$this->_Langs['current'] = $lang;
Config::set('application.language', $lang);
} else {
if(Session::has('lang')){
Config::set('application.language', Session::get('lang'));
$this->_Langs['current'] = Session::get('lang');
} else {
$this->_Langs['current'] = $this->_Default;
}
}
return Redirect::to('/');
}
}
In Route.php:
Route::get('lang/(:any)', 'language#set');
I've been doing it like this:
$languages = Config::get('lang.languages'); //returns array('hrv', 'eng')
$locale = Request::segment(1); //fetches first URI segment
//for default language ('hrv') set $locale prefix to "", otherwise set it to lang prefix
if (in_array($locale, $languages) && $locale != 'hrv') {
App::setLocale($locale);
} else {
App::setLocale('hrv');
$locale = null;
}
// "/" routes will be default language routes, and "/$prefix" routes will be routes for all other languages
Route::group(array('prefix' => $locale), function() {
//my routes here
});
Source: http://forumsarchive.laravel.io/viewtopic.php?pid=35185#p35185
What I'm doing consists of two steps:
I'm creating a languages table which consists of these fields:
id | name | slug
which hold the data im gonna need for the languages for example
1 | greek | gr
2 | english | en
3 | deutch | de
The Language model I use in the code below refers to that table.
So, in my routes.php I have something like:
//get the first segment of the url
$slug = Request::segment(1);
$requested_slug = "";
//I retrieve the recordset from the languages table that has as a slug the first url segment of request
$lang = Language::where('slug', '=', $slug)->first();
//if it's null, the language I will retrieve a new recordset with my default language
$lang ? $requested_slug = $slug : $lang = Language::where('slug', '=', **mydefaultlanguage**')->first();
//I'm preparing the $routePrefix variable, which will help me with my forms
$requested_slug == ""? $routePrefix = "" : $routePrefix = $requested_slug.".";
//and I'm putting the data in the in the session
Session::put('lang_id', $lang->id);
Session::put('slug', $requested_slug);
Session::put('routePrefix', $routePrefix );
Session::put('lang', $lang->name);
And then I can write me routes using the requested slug as a prefix...
Route::group(array('prefix' => $requested_slug), function()
{
Route::get('/', function () {
return "the language here is gonna be: ".Session::get('lang');
});
Route::resource('posts', 'PostsController');
Route::resource('albums', 'AlbumsController');
});
This works but this code will ask the database for the languages everytime the route changes in my app.
I don't know how I could, and if I should, figure out a mechanism that detects if the route changes to another language.
Hope that helped.

Categories