At some point in the past, my company changed our search tool and with it the format of the search query in the url.
It used to be a typical query parameter:
/product-name-some-digits-12345?q=searchterm
But now it's sent as a fragment:
/product-name-some-digits-12345##search:query=searchterm
We have a small number of visits to pages in the old format - either links out there in the ether, or users searching before the JS which powers the search has loaded. To mitigate this, I'd like to write a route to redirect from the old format to the new.
I thought I could do this by adding something like the below to the route file web.php:
Route::get('{base}?q={query}', function ($base, $query) {
// output for now, write redirect later
dd($base . '#search:query=' . $query);
})->where([
'base' => '[^\?]?',
'query' => '.*',
]);
But the route just isn't being hit.
How can I write a Laravel route to act when a query string is present?
I don't know how Laravel would translate that Route, but I'm also not surprised that the Route wouldn't be accessible. To debug your available routes, you can use this Artisan command:
php artisan route:list
Typically, you don't define Query String parameters directly in the Route like ?q={query}, since you really don't have to; Query Strings are implicit; they can be present on any route, but aren't required.
If you change the route to a simple Route::get('{base}'), and add a check for $request->has('q'), you should be able to handle the redirect:
Route::get('{base}', function ($base) {
if (request()->has('q')) {
return redirect()->url($base . '#search:query=' . request()->input('q'));
}
// ...
});
request() is available app-wide and can be used to check for Query String or Body/Post Parameters. The alternative is to use Injection, like:
Route::get('{base}', function (Request $request, $base){ ... });
Then use $request->has() and $request->input(), but they are syntactically the same.
Another approach would be to wrap your routes with a Middleware that checks the same thing. Syntax would be similar, in that you check for the presence of ?q=whatever via $request->has('q'), and redirect or continue as required. Documentation for that can be found here:
https://laravel.com/docs/9.x/middleware
Related
I am creating APIs for an app. Now app developer wants me to create a fixed base url and pass the ROUTE NAME (Which will point to controller function) as POST variable. Example:
http://example.com/Api
and POST variables like:
action=>'ROUTE_NAME'
But in laravel we can define the routes based upon the url parts as:
http://example.com/Api/ROUTE_NAME
I have tried using a single controller and loading the other controllers based upon SWITCH statements. But that doesn't seem to be a standard practice as i need to add switch condition every time I'll create a new API. Also middleware will not work on the loaded controllers dynamically.
Is there a way in laravel to achieve this? I am using laravel 5.4
You could implement a middleware that listens on the /Api route, which gets the ROUTE_NAME from the $request, then you could use the Route() helper function to find the url of that named route, then redirect the request to that route.
Something like:
// Generating ROUTE_NAME url...
$url = route($request->route_name);
// Redirect to that route...
return redirect()->route($url);
Obviously you'll need to add code to handle if it doesn't find a route etc, maybe return a json response back with a proper error code etc.
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
I'm using Laravel 5.1 and am building a service that can be seen as JSON or HTML. This approach is already done by sites like reddit.
Example
Normal view: http://www.reddit.com/r/soccer
JSON view: http://www.reddit.com/r/soccer.json
As you can see, they simply add .json to an URL and the user is able to see the exact same content either as HTML or as JSON.
I now wanted to reproduce the same in Laravel, however I'm having multiple issues.
Approach 1 - Optional parameter
The first thing I tried was adding optional parameter to all my routes
Route::get('/{type?}', 'HomeController#index');
Route::get('pages/{type?}', 'PageController#index');
However, the problem I was facing here, is that all routes were caught by the HomeController, meaning /pages/?type=json as well as /pages?type=json were redirected to the HomeController.
Approach 2 - Route Grouping with Namespaces
Next I tried to add route groupings with namespaces, to seperate backend and frontend
Route::get('pages', 'PageController#index');
Route::group(['prefix' => 'json', 'namespace' => 'Backend'], function(){
Route::get('pages', 'PageController#index');
});
However, this doesn't work either. It does work, when using api as prefix, but what I want, is that I can add .json to every URL and get the results as json. How can I achieve that in Laravel?
You can apply regular expressions on your parameters to avoud such catch-all situation as you have for HomeController#index:
Route::get('/pages{type?}', 'PageController#index'->where('type', '\.json'));
This way it type will only match, if it is equal to .json.
Then, to access it in your controller:
class PageController {
public function index($type = null) {
dd($type);
}
}
and go to /pages.json
I have a user RESTful resource route in my Laravel App.
Route::resource('backbone.users', 'backbone\UserController');
for the the CRUD operations.
Unfortunately, I get following URIs:
I get {backbone} in the URI that results in %backbone% in the browser but
I want the URL like dev.domain/backbone/users NOT dev.domain/backbone/%backbone%/users
I would need to redirect like:
return Redirect::intended('backbone/{backbone}/users');
How come?
It's hard to tell by the screenshot (or maybe my eyes aren't great) but it looks like backbone/{backbone}/users. If this is the case, that's intended, the {} are indicating that it can take a variable in this position.
So it might be return Redirect::intended('background/1/users');
The controller method to go with it, in this case index of UserController will receive that variable as its argument.
This is normal for Laravel. The {} in your routes list represent url variables.
Take this route for example:
/users/{user}/edit
To use this route you would navigate to
/users/1/edit
Assuming you have your controller method setup similar to:
public function edit($userId)
{
}
The $userId variable would contain '1' from the url. Definitely checkout the docs for more on Laravel routing
I have just started tinkering with Laravel (PHP newbie alert) and I have a doubt on how to implement my REST JSON APIs on this framework.
The desired URL path should be:
http://api.mysite.com/APIKEY/FUNCTION/(ARGUMENTS)*
But what is the best way to do it?
I did not find any enough explanatory guide, because it is assumed that each feature (authentication, search, and so on) are on different routes.
At the moment I managed to create migrations and models for the needed data and I also set a route this way:
Route::get('/{apikey}/{function}/{arg1}/{arg2}/{arg3?}',
//The first two arguments are mandatory, the 3rd optional
function($apikey,$function,$arg1,$arg2)
{
return Response::json(array(
'status'=>'200'),
200);
})
->where(array('function'=>'[A-Za-z]+'));
This should be the correct action flow, but I have some doubts.
Check that apikey is valid, if not return a 404 json response. How do I call such function, where do I define it?
If key check is successful, understand which function is needed (should I use a switch construct or is there a better way to implement this, like a route group? In all examples with Route::group there is a static prefix, but here the prefix is apikey, a variable)
Return data - if available - getting it from the database. I suppose that for each function i should code a specific controller that gets data from the database using the Models.
#1. I think I'd probably use a route filter for this, like so:
Route::filter('apikey', function(){
if (Shared\API::checkIfKeyIsValid(Input::get('key') != true){
return Shared\Errors::handleError("bad-api-key");
}
});
You can see this filter checks to make sure some session variables are set and match, and if it returns ANYTHING, that's failing, and it won't send the user where the route normally goes. You'd call it in the route like so:
Route::get('play/{id}', array('before' => 'loggedin', 'uses' => 'ThingController#doThing'));
#2. I think a route group is how I'd structure this (if I understand what you're asking).
Edit: You've changed the question since I answered, regarding the prefix being a variable. I'd reorder your arguments so the API key is at the end, or you could take a look at this, which might do what you want: https://github.com/jasonlewis/enhanced-router
Route::group(array('prefix' => 'api'), function()
{
//the routes for all API calls
Route::group(array('prefix' => '/v1'), function()
{
//for version 1
Route::group(array('prefix' => '/thing', 'before' => 'loggedin'), function()
{
//for things only
Route::get('/getThing/{id}', 'APIController#getThing');
Route::get('/getDifferentThing/{id}/{aux}/{optional?}', 'APIController#getDifferentThing');
});
});
});
#3. The returning of the data should be done via your controller with the data coming from the model. Either return it as a view, or just return it as JSON like so:
return Response::json($data);