How to set up case insensitive routes with Slim framework? - php

I have the following association between a route and a callback in my application:
$api->slim->post('/:accountId/Phone-Numbers/', function($accountId) use ($api) {
$api->createPhoneNumber($accountId);
});
What I want to avoid is having the route myhost/a7b81cf/phone-numbers/ return a 404 response because Slim understands the route myhost/a7b81cf/Phone-Numbers/ as being different, due to the usage of uppercase letters.
How can I avoid setting up two separate routes that trigger the same callback function?

This is an old question, but I wanted to provide a clear answer for this problem.
There is a 'routes.case_sensitive' configuration that allows you to do that. I don't know exactly why this is not in the docs (http://docs.slimframework.com/#Application-Settings), but if you look at the framework's source code (specifically, in getDefaultSettings() at Slim.php) you can see that is there.
I just tested it and it works fine.
Summarizing, the solution is to apply the 'routes.case_sensitive' configuration like this:
$configurations = [
// ... other settings ...
'routes.case_sensitive' => false
];
$app->config($configurations);

You could try with route validation/conditions:
$api->slim->post('/:accountId/:phoneNumbers/', function ($accountId) {
$api->createPhoneNumber($accountId);
})->conditions(array('phoneNumbers' => '(p|P)hone\-(n|N)umbers'));
Or if you want more global change you could override Route->matches method in Route class to be case insensitive but it will affect whole application.

You can mimic case-insensitive routing in Slim by registering a hook and modifying the incoming routes to match the case of your defined routes.
In the example, all of your defined routes should be lowercase and strtolower is called on all incoming paths:
$app->hook('slim.before.router', function () use ($app) {
$app->environment['PATH_INFO'] = strtolower($app->environment['PATH_INFO']);
});

Related

How to map shorthanded routes to the same controller in Slim 3

I am working on a REST API project using Slim 3, and I was wondering if there is an easy way to implement the following routing without creating separate routes for the shorthands.
The shorthand is ../me for ../users/{id} where the id is the current users ID.
So far its easy, I just create the two routes, and map them to the same controller method; but there are many more endpoints which use the same logic for example:
../users/{id}/posts should use the same as ../me/posts,
../users/{id}/groups/{gid} should use the as ../me/groups/{gid}, etc.
I used the double dots to indicate that there are preceding URI parts (version, language etc.).
I hope you get the idea now.
So my question is this: is there a way to reroute these kind of requests, or maybe is there a route pattern that would fit my needs and i missed it, maybe even I have to fiddle in a middleware to achieve this?
Thanks
There is a way to take advantage of Slim's FastRoute router. Put a regular expression into the variable part of your route and do the extra parsing inside the controller:
$app->get('/whatever/{id:users/\d+|me}', function ($request, $response, $args) {
if (preg_match('%^users/(\d+)$%', $args['id'], $parsed)) {
// This is /users/{id} route:
$user = $parsed[1];
} else {
// This is /me route:
$user = 'automagically recognized user';
}
return $response->withStatus(200)->write('Hello '.$user);
});
However I'd find that strange and would recommend mapping the same controller to two individual routes, as you do now. Two reasons come to my mind:
You can put the user ID lookup for 'me' route only into the one where it's needed (by having another controller adding this logic on top of the main one).
It's easier to comprehend for other developers on the team.
Hope it helps!
Try it
$app->get('/users[/{id}/groups[/{msgid}]]', function ($request, $response, $args) {
}
and see the oficial documentation in http://www.slimframework.com/docs/objects/router.html

Laravel 5 redirect to path with parameters (not route name)

I've been reading everywhere but couldn't find a way to redirect and include parameters in the redirection.
This method is for flash messages only so I can't use this.
return redirect('user/login')->with('message', 'Login Failed');
This method is only for routes with aliases my routes.php doesn't currently use an alias.
return redirect()->route('profile', [1]);
Question 1
Is there a way to use the path without defining the route aliases?
return redirect('schools/edit', compact($id));
When I use this approach I get this error
InvalidArgumentException with message 'The HTTP status code "0" is not valid.'
I have this under my routes:
Route::get('schools/edit/{id}', 'SchoolController#edit');
Edit
Based on the documentation the 2nd parameter is used for http status code which is why I'm getting the error above. I thought it worked like the URL facade wherein URL::to('schools/edit', [$school->id]) works fine.
Question 2
What is the best way to approach this (without using route aliases)? Should I redirect to Controller action instead? Personally I don't like this approach seems too long for me.
I also don't like using aliases because I've already used paths in my entire application and I'm concerned it might affect the existing paths if I add an alias? No?
redirect("schools/edit/$id");
or (if you prefer)
redirect("schools/edit/{$id}");
Just build the path needed.
'Naming' routes isn't going to change any URI's. It will allow you to internally reference a route via its name as opposed to having to use paths everywhere.
Did you watch the class Illuminate\Routing\Redirector?
You can use:
public function route($route, $parameters = [], $status = 302, $headers = [])
It depends on the route you created. If you create in your app\Http\Routes.php like this:
get('schools/edit/{id}', 'SchoolController#edit');
then you can create the route by:
redirect()->action('SchoolController#edit', compact('id'));
If you want to use the route() method you need to name your route:
get('schools/edit/{id}', ['as' => 'schools.edit', 'uses' => 'SchoolController#edit']);
// based on CRUD it would be:
get('schools/{id}/edit', ['as' => 'schools.edit', 'uses' => 'SchoolController#edit']);
This is pretty basic.
PS. If your schools controller is a resource (CRUD) based you can create a resource() and it will create the basic routes:
Route::resource('schools', 'SchoolController');
// or
$router->resource('schools', 'SchoolController');
PS. Don't forget to watch in artisan the routes you created

cakephp 3 prefix routing

I'm trying to set up a routing prefix in cakephp 3 so any URLs starting with /json/ get the prefix key set and I can change the layout accordingly in the app controller. Other than that, they should use the usual controller and action. I have added the following to routes.php
$routes->prefix('json', function($routes) {
$routes->connect(
'/:controller/:action/*',
[],
['routeClass' => 'InflectedRoute']
);
});
I want to direct all requests with json as first url segment to controller specified in second url segment. e.g. /json/users/add_account_type/ goes to users controller. However when accessing this URL I get the message:
Error: Create the class UsersController below in file:
src/Controller/Json/UsersController.php
whereas I want it to be using
src/Controller/UsersController.php
I think this should be possible but I can't quite see what I'm doing wrong when consulting the book. Have partly based my code on: CakePHP3.x controller name in url when using prefix routing
Thanks a lot in advance
That's simply how prefix routing now works in 3.x, as explained in the docs, prefixes are being mapped to subnamespaces, and thus to separate controllers in subfolders.
http://book.cakephp.org/3.0/en/development/routing.html#prefix-routing
If you'd wanted to change that behavior (I don't really see why), one way would be to implement a custom ControllerFactory dispatcher filter.
http://book.cakephp.org/3.0/en/development/dispatch-filters.html
On a side note, the RequestHandler component supports layout/template switching out of the box, so maybe you should give that a try.
http://book.cakephp.org/3.0/en/controllers/components/request-handling.html
http://book.cakephp.org/3.0/en/views/json-and-xml-views.html
Prefix routing is a way of namespacing parts of your routes to a dedicated controller. It seem that what you want is a scope and not a prefix, for what you describe:
Router::scope('/json', function($routes) {
$routes->fallbacks('InfledtedRoute')
});

Laravel 4 routing - the ability to skip a route if conditions are not met

So we have a load of content within the database, let's call these Articles. The paths for the Articles start from the application root and can contain slashes. So we want to search the DB to see if we match an Article, and if not skip the route and give other routes the opportunity to respond.
In Sinatra (which I believe has inspired the routing within Laravel) you have the option to pass to the next route. It might be this that's leading me astray.
Route::get( '{uri}', function( URI $uri ) {
// check database, if we find a record we'll need to pass it to the appropriate controller
// we'll have a class that handles this responsiblity
// pass if we don't find anything
} )->where('uri', '.*');
Route::get( 'other/page', 'OtherController#sayHello' );
The issue Allow skip the route based on conditions #1899 talks about this, although I can't see how filters cater for this, they will simply intercept and give you an opportunity to stop route execution (throw exception, redirect to route specifically etc.), you can't return FALSE; without error. There is an example chaining a condition method to a route, although this method doesn't seem to exist (in 4.2).
Any ideas?
In addition to this, we're also are thinking about containing this logic within a package so it can be shared across applications, and wonder if you can influence the order of execution of routes provided by package?
You can have a group of routes, which you contain within all the routes that match it. If it fails that group then it skips to the next section.
Route::group(array('before' => 'auth'), function()
{
Route::get('/', function()
{
// Has Auth Filter
});
Route::get('user/profile', function()
{
// Has Auth Filter
});
});
http://laravel.com/docs/4.2/routing

What does the 'uses' keyword mean in regards to laravel routing?

I'm new to laravel and trying my best to RTM but having difficulty understanding a few things. I think there is an expected level on context that I am not aware of when it comes to routing. In reviewing the documentation for routing I see that the uses keyword allows one to Attach(ing) A Filter To A Controller Action, but what does that mean? I have an existing site that is using the uses keyword but I am at a loss at to what it is actually doing. Can someone explain (a tab more thoroughly than the laravel documentation does) and show a very simple example what this actually does?
The keyword uses of routing is where you define which action (controller method or anonymous function) will be used to process that particular route. Take this controller method example:
Route::get('user', array('uses' => 'UserController#showProfile'));
It says that uses will call the method showProfile in your UserController class, this would be the class:
class UserController extends Controller {
public function showProfile
{
return "Hi! I'm the showProfile method!";
}
}
So, if you hit the
http://localhost/user
You should see the message
Hi! I'm the showProfile method!
Because your route execute that action you defined in the uses.
An anonymous function (closure) example would be:
Route::get('user', array('uses' => function() {
return "Hi, I'm a closure!";
}));
Antonio's answer answers your (exact) question pretty perfectly, but I figured I'd add my comments to the wider context of the question, as I feel you don't actually want to know what uses is for, but instead are generally confused by the way routing works (you mention uses is for filters, which it isn't, but its use is a side-effect of adding filter configuration). So allow me to explain:
The router methods are actually kinda simple, but you just need to know what syntax they expect. There are two main forms, the unconfigured and the configured:
Unconfigured:
// syntax:
Route::get($route, $action);
// controller method
Route::get('some/route', 'Controller#action');
// closure
Route::get('some/route', function () {
return View::make(/* etc. */);
});
But as soon as you need to add configuration to a given route (including filters, naming, etc.) you need to use a slightly different syntax:
// syntax:
Route::get($route, $config); // where $config is an array
// controller method
Route::get('some/route', array(
'as' => 'my.route.name',
'uses' => 'Controller#action',
));
// closure
Route::get('some/route', array(
'before' => 'some.filter',
'uses' => function () {
return View::make(/* etc. */);
}
));
// closure alternative
Route::get('some/route', array(
'before' => 'some.filter',
function () {
return View::make(/* etc. */);
}
));
So, as soon as you need to name your route (using as) or add a filter (using before and after) you need to turn that more simple syntax into the slightly more complex array syntax. And when you make it an array, you still need a way to tell Laravel what to actually use for the execution - that's what uses is for.

Categories