Multiple routes defaults to controller in Laravel - laravel

I want to be able to get to an article by a short-link, like this articles/ID, but I also want to get there with the full link articles/ID/category/slug. But I can not get the following to work:
// Route file:
Route::pattern('id', '[0-9]+');
Route::pattern('cat', '^(?!create).*');
Route::pattern('slug', '^(?!edit).*');
Route::get('articles/{id}/{cat?}/{slug?}', ['as' => 'articles.show', 'uses' => 'ArticlesController#show']);
// Controller file:
public function show($id, $cat = null, $slug = null)
{
dd('1: ' . $cat . ' | 2:' . $slug);
}
The following link articles/28/ullam/vel-repellendus-aut-est-est-esse-fugiat gives the result:
string(53) "1: ullam/vel-repellendus-aut-est-est-esse-fugiat | 2:"
I don't understand why it's not split, if I remove the ? in my route definition it works.
I have tried this solution https://stackoverflow.com/a/21865488/3903565 and that works, but not when directed at a controller. Why?
Update; I ended up rearranging my routes file:
Route::pattern('id', '[0-9]+');
// Articles
Route::get('articles/create', ['as' => 'articles.create', 'uses' => 'ArticlesController#create']);
Route::get('articles/edit/{id}', ['as' => 'articles.edit', 'uses' => 'ArticlesController#edit']);
Route::get('articles/{id}/{category?}/{slug?}', ['as' => 'articles.show', 'uses' => 'ArticlesController#show']);
Route::get('articles/{category?}', ['as' => 'articles.index', 'uses' => 'ArticlesController#index']);
Route::resource('articles', 'ArticlesController', ['only' => ['store', 'update', 'destroy']]);

Is that your only route? Or you have a different one pointing to show too? I just added this one as my first route:
Route::get('articles/{id}/{cat?}/{slug?}', function($id, $cat, $slug)
{
dd($id.': ' . $cat . ' | 2:' . $slug);
});
And got this result:
28: ullam | 2:vel-repellendus-aut-est-est-esse-fugiat
Change the order of your routes to have the first one as your most specific routes and the last as your most generic one. Also, if you have any patterns, they can be inteferring with your results.
Looks like there's a problem with some patterns and optional parameters (?), so if you do
Route::pattern('id', '[0-9]+');
Route::pattern('cat', '^(?!create).*');
Route::pattern('slug', '^(?!edit).*');
Route::get('articles/{id}/{cat}/{slug}', function($id, $cat, $slug)
{
dd($id.': ' . $cat . ' | 2:' . $slug);
});
It will work fine. My advice is to not reuse routes too much, if you have an specific route, you create a route command for it and add it before your most generic ones:
Route::get('articles/{id}/create', function($id)
{
dd('this is the create route');
});
Route::get('articles/{id}/{cat}/{slug}', function($id, $cat, $slug)
{
dd($id.': ' . $cat . ' | 2:' . $slug);
});
The cost of creating new routes is low in comparison to the complexity of looking to regex patterns and try to find your way amongst them. Having new collaborators on projects, or even for your future self, the simpler, the better.
In Laravel 4.3 you'll have access to route caching, which makes routes being fired almost instantly.

The problem is only in the lookahead patterns.
You need $ and class excluding / in order to make it work.
So here they are:
Route::pattern('id', '[0-9]+');
Route::pattern('cat', '^(?!create$)[^/]*');
Route::pattern('slug', '^(?!edit$)[^/]*');
Route::get('articles/{id}/{cat?}/{slug?}', function($id, $cat, $slug)
{
dd($id.': ' . $cat . ' | 2:' . $slug);
});

Related

Identify Route's Group in Middleware

My ultimate objective is to limit accesses to the group of routes by validating permissions provided to the user.
These target 'group of routes' have ONE COMMON PARENT GROUP and may have zero or more sub-groups, such that, if access to these target 'group of routes' is permitted/accessible to the user then, all its sub-route groups are also accessible to the user.
To achieve this, I believe I need to differentiate these target group of routes by any uniqueString/parameter in middleware, which is indeed answered here.
But, I want to generalize this further, by applying middleware to common SINGLE PARENT GROUP of all these target group of routes and identify these target group of routes by any means in the middleware.
So, my question is how do I identify/differentiate these target group of routes in the middleware? Is there any way to do so?
Sample Code of what I am trying to describe:
Route::group(['prefix' => 'singleParent','middleware' => 'permissionMiddleware'], function (){
Route::group(['prefix' => 'target-group-1', 'groupUniqueString' => 'tsg1'], function (){
Route::group(['prefix' => 'sub-group-1.1'], function (){
});
Route::group(['prefix' => 'sub-group-1.2'], function (){
});
});
Route::group(['prefix' => 'target-group-2', 'groupUniqueString' => 'tsg2'], function (){
Route::get('route-1','Controller#method-of-Route1');
});
});
So, to specify a route group in your middleware to handle some actions, you can do it in this way :
Route::group(['prefix' => 'singleParent','middleware' => 'permissionMiddleware'], function (){
Route::group(['prefix' => 'target-group-1', 'as' => 'tsg1.'], function (){
//...
});
});
This will generate route names with the prefix : tsg1
Now in your middleware you can do like this to get the route group :
function getCurrentRouteGroup() {
$routeName = Illuminate\Support\Facades\Route::current()->getName();
return explode('.',$routeName)[0];
}
Updated
and to check :
if ($request->route()->named('name')) {
//
}
return $next($request);
Or in another approach you can achieve :
To get the prefix of a route group you can do something like this :
$uri = $request->path();
// this will give you the url path like -> if this is the url :
// http://localhost:8000/foo/bar you will get foo/bar
And then :
$prefix = explode('/',$uri)[0];
// and you will get 'foo'
Let me know if this works for you.

How to add dynamically prefix to routes?

In session i set default language code for example de. And now i want that in link i have something like this: www.something.com/de/something.
Problem is that i cant access session in routes. Any suggestion how can i do this?
$langs = Languages::getLangCode();
if (in_array($lang, $langs)) {
Session::put('locale', $lang);
return redirect::back();
}
return;
Route::get('blog/articles', 'StandardUser\UserBlogController#AllArticles');
So i need to pass to route as prefix this locale session.
If you want to generate a link to your routes with the code of the current language, then you need to create routes group with a dynamic prefix like this:
Example in Laravel 5.7:
Route::prefix(app()->getLocale())->group(function () {
Route::get('/', function () {
return route('index');
})->name('index');
Route::get('/post/{id}', function ($id) {
return route('post', ['id' => $id]);
})->name('post');
});
When you use named routes, URLs to route with current language code will be automatically generated.
Example links:
http://website.com/en/
http://website.com/en/post/16
Note: Instead of laravel app()->getLocale() method you can use your own Languages::getLangCode() method.
If you have more questions about this topic then let me know about it.
Maybe
Route::group([
'prefix' => Languages::getLangCode()
], function () {
Route::get('/', ['as' => 'main', 'uses' => 'IndexController#index']);
});

route() helper function in a different locale

I'm trying to create a language switcher, so that you can easily change language on the fly (without being redirected to the home page).
The thing I can't seem to accomplish is getting a url from a route name in a different locale.
Imagine your on mydomain.com/events (or mydomain.com/en/events since these are the same) and I want to get url in french (mydomain.com/fr/evenements)...
In my routes.php I have:
$locale = Request::segment(1);
if ( !array_key_exists($locale, Config::get('translatable.languages'))) {
$locale = null;
App::setlocale(Config::get('translatable.fallback_locale'));
}else{
App::setLocale($locale);
}
Route::group(array('prefix' => $locale), function(){
Route::get(Lang::get('routes.events'), array('as' => 'events.index', 'uses' => 'EventController#index'));
});
I have tried
setting the application locale right before calling the route($route_name) helper function
Setting a session variable with the requested locale (in this example 'fr'), then calling the route($route_name) function. For this I also added at the top of routes.php:
if( Session::has('requested_locale') ){
$locale = Session::pull('requested_locale');
}
I have no idea's left on how to handle this...
I know there is a package Laravel Localization that can do this, but I need it to work without that package (if possible)...
My solution on Laravel 5.1 was...
Defined locales in the config/app.php to create routes using the language files
'locale' => 'en',
'locales' => [
'en'=>'English',
'fr'=>'Français'
],
I've defined my routes in the resources/lang/ directory ('en/routes.php' and 'fr/routes.php' in my case)
return [
'about' => 'a-propos',
];
Defined routes using the language keys, example:
Route::get(trans('routes.about'), ['as' => 'about', function () {
return view('pages.'.App::getLocale().'.about');
}]);
Then I've created dummy route that would be used only for switching to another locale, ONLY after the current route has been matched to request
`
Route::matched(function($route) {
$route = Route::current();
$route_name = $route->getName();
$route_uri = $route->uri();
$locales = Config::get('app.locales');
foreach ($locales as $locale_code => $locale_name) {
if ($locale_code == App::getLocale()) {
continue;
}
if (Lang::has('routes.'.$route_name, $locale_code)) {
$route_uri = $locale_code . '/' . Lang::get('routes.'.$route_name, [], $locale_code);
}
else {
$route_uri = $locale_code . '/' . substr($route_uri, 3);
}
Route::get($route_uri, ['as' => 'change_locale_to_'.$locale_code, function(){
}]);
}
});
`
Finally, I use the dummy route in the view I want. $locale_toggle is a Locale eloquent Model that I'm setting in a Middleware for handling the language with a language code route prefix.
<a href="{{ route('change_locale_to_'.$locale_toggle->code, Route::current()->parameters()) }}" class="dropdown-toggle">

Add prefix to all routes, links and urls

Is it possible to add some prefix to all routes, urls and links in the whole application at one place including blades?
For example I have route
Route::get('/', 'HomeController#showWelcome');
but instead of it I want to have
Route::get($prefix . '/', 'HomeController#showWelcome');
and in blade
{{ HTML::style('css/bootstrap.min.css') }}
to
{{ HTML::style($prefix . 'css/bootstrap.min.css') }}
I tried
Route::group(array('prefix' => $prefix), function() {});
but it did not apply prefix to links in blades.
I found simple solution. I created .env.php folder inside project which looks like
return array(
'ROUTES_PREFIX' => 'prefix',
);
then added this code to all routes
Route::group(array('prefix' => $_ENV['ROUTES_PREFIX']), function() {
// routes here
});
and created custom macro for style, script and image. For example my macro for style looks like this
HTML::macro('extendedStyle', function($url, $attributes = array(), $secure = null) {
$prefix = $_ENV['ROUTES_PREFIX'] == '' ? '' : $_ENV['ROUTES_PREFIX'] . '/';
return HTML::style($prefix . $url, $attributes, $secure);
});
I hope, someone will find this useful.

Laravel, Routing Wildcards to filter and then controller

I am trying to capture a wildcard from URL and then first pass it to a filter then route to controller. I am not sure how to plot the question exactly but here is what I've tried so far.
Route::get('test/(:any?)', array('as' => 'testroute', 'uses' => 'test#result', 'before' => "test_filter:$1"));
Route::filter('test_filter', function($id = NULL)
{
if($id)
echo "This id is " . $id; // Prints "This id is $1"
});
and
Route::get('test/(:any?)', array('as' => 'testroute', function($id = NULL)
{
if($id)
echo "this id is " . $id; // Does not do anything
}, 'uses' => 'test#result'));
Basically, I want to check if there is an id appended to the URL and set a cookie if there is one. But regardless of the case, I want this route to be handled by a controller no matter if there is any id appended or not.
I have to do the same thing with so many routes so I'd prefer something like a filter rather than modifying the controller's codes.
I know that I can directly pass the wildcard element to a closure, or I can feed this as a parameter to any controller but in that case I'll have to modify the controller codes, which I can't at the moment.
Can I do it through filters ? or any other way in which i wont have to modify the controller codes ?
Try passing an anonymous right after the before
Route::get('test/(:any?)',
array(
'as' => 'testroute',
'uses' => 'test#result',
'before' => "test_filter",
function($my_cookie_value)
{
// Set cookie here
}
)
);
Taken from here
I'd use a middleware http://laravel.com/docs/5.1/middleware (or filter for older Laravel versions) and add the route/s into a group which has the middleware as you can see in here http://laravel.com/docs/5.1/routing#route-group-middleware.
The middleware will be executed before the route code, where you can add a logic to manage your cookie.

Resources