Get matched route before execution in Laravel 4 - php

I need to intercept a route so that I can grab one of its parameters, and use that parameter to query a cache at the onset of the page request. I have two questions:
Is there a "matched" event I can listen for?
Is there a way in Laravel to check if a route parameter key exists/isset, or simply get all parameters from the route?
EDIT: Note that the Route::matched() or Event::listen('router.matched') feature requires 4.1.19 or later, earlier versions will not contain these features.

Yes, you can use something like this:
Event::listen('router.matched', function($route) {
$username = $route->getParameter('username');
if($username && $username == 'heera') {
// do something
}
});
For example, I've a route like Route::get('/user/{username}', 'UserController#show') and the url for this route would be something like http://blog.dev/user/heera so, in the matched event listener I'll get username parameter using $route->getParameter('username') and if it's heera then do something, otherwise do nothing. The $route variable is an instance of Illuminate\Routing\Route and you can use all the public methods of this object.
BTW, you may put it in your filters.php file.
Update: It's also possible to register an event for router.matched event using this:
Route::matched(function($route) {
// Do something
});
Or this:
$app['router']->matched(function($route) {
// do something
});
Tested on Laravel Framework version 4.1.19.

Related

Preprocess lumen route parameters with urldecode

I am currently using the lumen framework (5.6) to build an API, this API can be used to request a page by for example its title. The route for this is:
Route::group(["prefix" => '/api/v1', "middleware" => ["ContentTypeJson","Paginator"]], function () {
Route::group(["prefix" => '/{databaseIdentifier}', "middleware"=>"DatabaseIdentifier"], function () {
Route::group(["prefix" => '/pages'], function () {
Route::group(["prefix" => '/{title}'], function () {
Route::get("/", "PageController#getPageByTitle");
Route::get("/parents", "SearchController#getParentalSpecies");
Route::get("/all", "PageController#getPageByTitleWithLinks");
Route::get("/overlap/{overlapProperty}", "PageController#getPagesWithOverlap");
Route::put("/", "PageController#overwritePage");
});
});
});
As you can see the title is used in multiple functions and controllers, the same applies to the databaseIdentifier which is used in the middleware to determine which database needs to be used.
However all url parameters with a space will be converted with %20 instead of a space, which is the expected behaviour. However I would like to convert this back to the raw string, which can be done with urldecode().
But since this is applied in every controller and function I would like to use some kind of preprocessing step for this.
I have tried using a middleware for this to alter the route parameters as suggested here (using $request->route()->setParameter('key', $value);).
Unfortunately this does not work in lumen since the result of $request->route() is an array and not an object. I have tried altering this array but I can not get it to change the actual array in the Request object. No error appears here.
So in short: I am looking for a way to urldecode every URL parameter which is passed to my controllers and functions without putting $param = urldecode($param); everywhere.
If you need more information feel free to ask
Thank you in advance
For anyone who also encounters this issue I have found a solution using middleware.
In the middleware I do the following:
public function handle(Request $request, Closure $next)
{
$routeParameters = $request->route(null)[2];
foreach ($routeParameters as $key=>$routeParameter) {
$routeParameters[$key] = urldecode($routeParameter);
}
$routeArray = $request->route();
$routeArray[2] = $routeParameters;
$request->setRouteResolver(function() use ($routeArray)
{
return $routeArray;
});
return $next($request);
}
This code will decode every route parameter and save it in an array, then I take the whole route array which is created by lumen itself (which contains the url encoded parameters), these are then replaced with the url decoded version of the parameter. This is not enough because this does not affect the route array in the Request object.
In order to apply these changes I alter the routeResolver so it will return the changed array instead of the one created by lumen.

check parameters type before run route in laravel 5

I have 2 kind on route.
like this:
You know for these:
Route::get('/{category_slug}/{article_slug}', 'mController#list');
Route::get('/{category_slug}/{subcategory_slug?}', 'mController#clist');
It run only first route.
I try binding them in RouteServiceProvider boot(){
Route::bind('category_slug', function ($category_slug, $route) {dd('category_slug') }); //works
Route::bind('article_slug', function ($article_slug, $route) { dd('article_slug') }); //works for both article_slug(ok!) and subcategory_slug(wrong!)
Route::bind('subcategory_slug', function ($subcategory_slug, $route) { dd('subcategory_slug') }); //not works
}
Is there a way to check {article_slug} or {subcategory_slug} before loading route and then system choosing right route? for example if first is wrong, then skip it and try to run second route.
for example middleware can do that?
You need to use different url or different method of request in order to fulfil your demand.
Like -
Route::get('/{category_slug}/{article_slug}', 'mController#list');
Route::post('/{category_slug}/{subcategory_slug?}', 'mController#clist');
Use Route::update() if need to modify a data which is already in database or Request::delete() to delete a data in the database.
Or like the answer of tprj29 you need to use different kinds of url
In Laravel slugs are unique per table not unique throughout all tables. I would recommend using something like a keyword to determine what slug is expected. This is way more efficient and cleaner then querying to determine if the slug is of an type article or category of object.
Route::get('/{category_slug}/', 'mController#clist');
Route::get('/{category_slug}/sub/{subcategory_slug}', 'mController#clist');
Route::get('/{category_slug}/sub/{subcategory_slug}/{article_slug}', 'mController#list');
Route::get('/{category_slug}/{article_slug}', 'mController#list');

how to use same route url for multiple action in slim framework

I am creating the API using slim framework. I faced the following problem.
I use one of the routes for given input.That is, json input: { "tagname": "tname"}. Route is
$app->post('/tag',function () use($app, $db){
//code
});
Now, I want to use the same route for another input.json: [{"tid": "1"},{"tid": "2"}]. Route is
$app->post('/tag',function () use($app, $db){
//code
});
How do solve it?
Slim's router can't call different functions for same path based on received content.
In your particular case the simplest way to deal with two different types of input data on one route would be something like this (I assume you are getting data as POST body with application/json which is not processed by Slim2)
$app->post('/tag',function () use($app, $db){
$payload = json_decode(file_get_contents('php://input'));
if(is_array($payload)) {
// code to deal with [{"tid": "1"},{"tid": "2"}]
} else {
// code to deal with { "tagname": "tname"}
}
});
But even easier and logically would be make /tag route for single and /tags for multiple. Or just require to send all tags as array - even single one.
you can pass extra parameter to perform another action in same route and separate your code with if condition

Laravel: Getting Route Parameters in Route::group() Closure

I have an app running on Laravel 4.2, and I am trying to implement a somewhat complex routing mechanism. I have a route group set up using:
Route::group(['domain' => '{wildcard}.example.com'], $closure);
I need to be able to check the $wildcard parameter in the closure for the group -- meaning before the request gets passed to the controller (I need to defined Route::get() and Route::post() depending on the subdomain).
An example of what I'd like to do is as follows:
Route::group(['domain' => '{wildcard}.example.com', function ($wildcard) {
if ( $wildcard == 'subdomain1' ) {
Route::get('route1', 'Subdomain1Controller#getRoute1');
Route::get('route2', 'Subdomain1Controller#getRoute2');
} else if ( $wildcard == 'subdomain2' ) {
Route::get('route1', 'Subdomain2Controller#getRoute1');
Route::get('route2', 'Subdomain2Controller#getRoute2');
}
}
);
Of course, the above does not work. The only parameter passed into a Route::group() closure is an instance of Router, not the parameters defined in the array. However, there must be a way to access those parameters -- I know for a fact that I've done it before, I just don't remember how (and can't find the solution anywhere online).
I know I can always use PHP's lower-level methods to retrieve the URL, explode() it, and check the subdomain that way. But I've done it before using Laravel's methods, and if possible, I'd prefer to do it that way (to keep things clean and consistent)
Does anyone else know the solution? Thanks in advance!
Use the Route::input() function:
Route::group(['domain' => '{wildcard}.example.com', function ($wildcard) use ($wildcard) {
if ( Route::input('wildcard') === 'subdomain1' ) {
Route::get('route1', 'Subdomain1Controller#getRoute1');
Route::get('route2', 'Subdomain1Controller#getRoute2');
} else {
Route::get('route1', 'Subdomain2Controller#getRoute1');
Route::get('route2', 'Subdomain2Controller#getRoute2');
}
}
);
See "Accessing A Route Parameter Value" in the docs.

How to build optional parameters as question marks in Slim?

I've built my first RESTful API ever and used Slim as my framework. It works well so far.
Now I have seen a great API Design Guide which explained, the best way to build an API is to keep the levels flat. I want to do that and try to figure out how to build an URI like this:
my-domain.int/groups/search?q=my_query
The /groups part already works with GET, POST, PUT, DELETE and also the search query works like this:
my-domain.int/groups/search/my_query
This is the code I use for the routing in PHP:
$app->get('/groups/search/:query', 'findByName');
I just can't figure out how to build optional parameters with an question mark in Slim. I wasn't able to find anything on Google.
EDIT:
Since the search not seems to be suitable for my scenario I try to show another way of what I want to realize:
Let's say I want to get a partial response from the API. The request should look like that:
my-domain.int/groups?fields=name,description
Not like that:
my-domain.int/groups/fields/name/description
How do I realize that in the routing?
The parameters supplied with the query string, the GET parameters, don't have to be specified in the route parameter. The framework will try to match the URI without those values. To access the GET parameters you can use the standard php approach, which is using the superglobal $_GET:
$app->get('/groups/test/', function() use ($app) {
if (isset($_GET['fields']){
$test = $_GET('fields');
echo "This is a GET route with $test";
}
});
Or you can use the framework's approach, as #Raphael mentioned in his answer:
$app->get('/groups/test/', function() use ($app) {
$test = $app->request()->get('fields');
echo "This is a GET route with $test";
});
Ok I found an example that does what I need on http://help.slimframework.com/discussions/problems/844-instead
If you want to construct an URI Style like
home.int/groups/test?fields=name,description
you need to build a rout like this
$app->get('/groups/test/', function() use ($app) {
$test = $app->request()->get('fields');
echo "This is a GET route with $test";
});
It echoes:
This is a GET route with name,description
Even though it's not an array at least I can use the question mark. With Wildcards I have to use /
You may also have optional route parameters. These are ideal for using one route for a blog archive. To declare optional route parameters, specify your route pattern like this:
<?php
$app = new Slim();
$app->get('/archive(/:year(/:month(/:day)))', function ($year = 2010, $month = 12, $day = 05) {
echo sprintf('%s-%s-%s', $year, $month, $day);
});
Each subsequent route segment is optional. This route will accept HTTP requests for:
/archive
/archive/2010
/archive/2010/12
/archive/2010/12/05
If an optional route segment is omitted from the HTTP request, the default values in the callback signature are used instead.
Search query is not suitable for url parameters, as the search string might contain url separator (/ in your case). There's nothing wrong to keep it as query parameter, you don't have to push this concept everywhere.
But to answer your question, optional parameters are solved as another url:
$app->get('/groups/search/:query', 'findByName');
$app->get('/groups/search/strict/:query', 'findByNameStrict');
EDIT: It seems you want to use Slim's wildcard routes. You just need to make sure there's only one interpratation of the route.
$app->get('/groups/fields/:fields+', 'getGroupsFiltered');
Parameter $fields will be an array.

Categories