RESTful trailing slash routes in laravel - php

I have the following routes in my Laravel 3 RESTful api project
Route::delete('/(:any)', 'resources#destroy');
Route::delete('users/(:any)', 'users#destroy');
The problem I am having is when a user sends a delete request to /users/
What I want to happen is that the users#destroy route is called with parameter null. In my controller I have an exception for a user trying to delete a null resource.
What seems to be happening is that the resource#destroy route is called with parameter users. This obviously has the undesired affect of deleting the users resource.
I know I could modify my .htaccess but technically /users/ does belong to the users controller not the resources controller. I want to maintain that relationship.
I was wondering if there is a simple way to solve this from within Laravel?
EDIT: have the above working with the answer below. Now I have an error in my get routes
Route::get('users/(:any?)', 'users#show');
Route::get('users', 'users#index');
/users and /users/ both call users#index which I don't want.
I need GET /users to go to users#index and GET /users/ to go to users#show with null parameter

I worked around the trailing slash by adding a filter to my routes
Route::group(array('before' => 'trailingslash'), function()
{
//routes in here
});
Route::filter('trailingslash', function() {
$current = URI::full();
if(substr($current, -1) == '/'){
return return Response::error('404');
}
});

one point you need to consider.
Routes are defined without a front-slash. The only exception to this is the default route which is represented with only a front-slash.
so Route::delete('/(:any)', 'resources#destroy') will cause undesired result.
Moreover, your order is wrong.
(:any) will match user too and will send the request to resources controller.
so what you need to do is,
change the order (make it reverse).
change the route of resources considering according to the rules. like resources/delete etc.....
What I want to happen is that the users#destroy route is called with parameter null. In my controller I have an exception for a user trying to delete a null resource.
to do this, (after making the changes above....)
change the route of user/(:any) to user/(:any?) which will make the 2nd segment optional.
After that, straight forward.
$foo = URI::segment(2, null);
//Rest will follow.
EDIT
Now, the following code,
Route::get('users/(:any?)', 'users#show');
Route::get('users', 'users#index');
does not make any sense.
if i type user, what the router is suppose to take?
user#show with no optional segment or user#index?
Routes are design so that it removes the ambiguity. You are going in the opposite direction with making it all ambiguity.
just make a simple route.
like show
user/show
delete
user/delete
edit
user/edit
etc....
The type of routing you are applying is confusing for both users and developers as it carries ambiguity.

Related

Laravel how to generate urls using GET route names?

I got this 2 routes in my routes file (web)
Route::get('management/special-fees/add/{userId}', 'Management\SpecialFeeController#create')->name('management/special-fees/add');
Route::post('management/special-fees/add', 'Management\SpecialFeeController#store')->name('management/special-fees/add');
They both share the same name but one is GET and the other is POST, so far so good. But now I want to make an url in my view to open the form, for that I use the method route() like this
route('management/special-fees/add',$user->id )
but when trying to go to the url I get this route
.../management/special-fees/add?5
there is a question mark instead of a "/" so the route is invalid.
I made some tests and I figured out that happens because is trying to go to the POST route instead of the GET one if I change the POST route's url in the web file like this
Route::get('management/special-fees/add/{userId}', 'Management\SpecialFeeController#create')->name('management/special-fees/add');
Route::post('management/special-fees/addSSSS', 'Management\SpecialFeeController#store')->name('management/special-fees/add');
I will in fact get this url
.../management/special-fees/addSSSS?5
So why is the route() method generating a url for the POST route over the GET one? how do I make it to choose the GET route first?
In laravel the routing is prioritized by in the order it is written in your route.php file.
In this case you're writing the Route::post last, which in turn tells Laravel that that one should have the highest priority. Try switching them and the Route::get will have the higher priority.
Like so:
Route::post('management/special-fees/addSSSS', 'Management\SpecialFeeController#store')->name('management/special-fees/add');
Route::get('management/special-fees/add/{userId}', 'Management\SpecialFeeController#create')->name('management/special-fees/add');
I may be wrong, but I think you'll have to re-think route naming. One of the problems route naming helps eliminate is redundant and complex names. For example, if you looked at route:list for Route::resource('something', 'SomethingController') it will have something.index, something.store as route names for Route::get('something') and Route::post('something').
If it's the same name, it will always resolve to the first one and will probably never hit the second route; in your case will hit the POST route and never the GET route.
?5 means 5 is an argument for your get route.
try this
url('management/special-fees/add/'.$user->id)
for get route insted of
route('management/special-fees/add',$user->id )

When to use: redirect('/') vs. redirect()->route('home') vs. redirect()->home()?

When I have this named route:
Route::get('/', 'IndexController#index')->name('home');
Then in any action method of any Controller; when I need to redirect to the named route home; any of these statements redirects properly to the intended route:
return redirect('/');
return redirect()->route('home');
return redirect()->home();
When to use each?
What are the differences?
Are there any benefits of using one over the others?
As the documentation mention :
When you call the redirect helper with no parameters, an instance of
Illuminate\Routing\Redirector is returned, allowing you to call any
method on the Redirector instance. For example, to generate a
RedirectResponse to a named route, you may use the route method
As you can see in the API methods(link below) there is a lot of methods that you can use and also there is one specific helper method home() it's just a shortcut for redirect()->route('home') as highlighted by #ceejayoz.
Now the we will talk about return redirect('/'); and return redirect()->route('home'); the two of them redirects properly to the intended route as you said BUT the second one is really useful if in the future.
Why ?
Because if you want to change the URL structure in the routes file all you would need to change is the route only for example :
Route::get('/', 'IndexController#index')->name('home');
Will be :
Route::get('/home_page', 'IndexController#index')->name('home');
and all the redirects would refer to that route and there is no other thing that you should change => all redirects will still work perfectly.
BUT
If you choose to use the first one (i mean return redirect('/');) then after the change in the route you will need to parse all your controllers to check if there is some redirects that uses then changed route and the change them :p
redirect()->home() is simply a shortcut for redirect()->route('home'). The source code can be seen here.
Named routes are generally better than raw URLs for maintainability purposes. The home route isn't all that likely to change location, but it is possible that you might host a Laravel app in a subfolder, or move the home page from / to /app to make room for a marketing landing page at the root.
redirect('/')
It redirects you to the base URL.
redirect()->route('home')
Redirects to the route named home.
See More about named routes here.
redirect()->home();
Alternative way to redirect to named route.Redirects to 'home' route as well. It does the same thing as above but with slightly different syntax.
I preferred named routes over raw URLs, because if you decide to change the URL later on, you have to make changes into your routes file only.
When you are passing a string it will redirect a user to the domain plus the string you pass.
http://localhost:3000 + string
It will also add / if you forget it, now if you name your routes like you did then you can call it by the name.
An advantage of using named routes is in case you want to change the URI you can do it without worrying about changing a bunch of ahref in your view, redirects in your controllers, etc.
home() is a method from Laravel's Redirector or redirect() so, I don't think you can just call a named route as a method.

Laravel RESTful resource route adds percentages to URI

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

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

Laravel 4 only root route works - other routes return 'Controller method not found'

I've got laravel set up on a domain on a linux host and I have a WAMP local host set up.
The only route that works is the root, when ever I try go to another route such as domain.com/account I get a "Controller method not found." error.
In my routes.php file I have:
Route::controller('','LoginController');
Route::controller('account', 'AccountController');
In my LoginController, I have just two methods. getIndex and postIndex.
After a couple of hours Googling with no results and playing around with the routes file amongst things, still nothing worked.
I tried adding the below route which didn't work either.
Route::any('hello', function(){
return 'hello!';
});
However, I then commented out my Route::controller('','LoginController'); line and the other routes started working!
I then changed it to Route::controller('login','LoginController'); and this and the other routes still worked. I then changed it to Route::any('','LoginController#getIndex'); and the root and other routes still worked. However, doing it this way, when I cliked the login button on my page nothing happened.
So my question really is, is there something wrong with doing Route::controller('','LoginController');? As everything else seems to 'work'
Laravel save an internal collection of registered routes in the $routes member of the Router class. When dispatching a request, a process of picking each element from this collection and test with current request will be executed to find out which route will be handle. This process is affected by the order of your route registering statements.
When testing each route with the current request, the picked route will be compiled and have a regex pattern. This pattern will be use to check with the current URI by the preg_match function as you can see at this line in Laravel source.
When using Route::controller a special route will be add to your routes collection. If your input is Route::controller($uri, $controller) then this special routes will have a regex pattern as ^/$uri/?P<_missing>(.*)$ and it tells Laravel that this request belong to a missing method of the $controller controller class.
In your case, you have set the value of $uri to an empty string which cause the regex of the special route to be ^/?P<_missing>(.*)$ (setting $uri with the string / cause the same effect). Well, this regex will match every URI. So, the internal route looking up process will abort when look to this special route. This is the reason while the exception has been thrown.
You should not use an empty string or the/ string when register with the Route::controller method like the way you did. Instead, use Route::resource or explicit calls (Route::get, Route::post, ...) to handle your top level routes.
I have not tried that, but maybe you could add a "/":
Route::controller('/','LoginController');
Edit
I was able to reproduce the issue and I solved by changing the order of your route lines:
Route::controller('accounts', 'AccountController');
Route::controller('','LoginController');

Categories