Laravel. Conflict when building routes - laravel

There are two routes:
Route::get('/{article:slug}', [ArticleController::class, 'showArticlePage']);
and
Route::get('/{user:nickname}', [ProfileInfoController::class, 'getUserByNickname']);
Is there any way for each of the routes to perform its function?You can't change uri
For example:
domain.com/nickname => I have to get the user
There is a search in the table "users"
2.domain.com/my-first-article => I have to get the article
There is a search in the table "articles"
Note that each routes has its own controller and action, but they have a similar uri

You need something to distinguish them it'll be a lot more helpful. E.g use an # in front of usernames.
Another approach would be if you know for sure all slugs will have a hyphen, then you can chain ->where('slug', '...')
See https://pineco.de/handy-regex-constraints-in-laravel-routes/
Otherwise, it'll go through the first defined route.

Related

Laravel - How do I navigate to a url with a parameter to left of subdirectory

I have to test this Route, but I am not sure how to navigate to it.
Route::get('/{type}/Foo/{page}/{date}', 'FooController#index');
I understand that URLs usually have subdirectories defined before the parameters in the URL.
I have worked with URLs like this example
Route::get('/Foo/{type}', 'FooController#index');
which would have an endpoint that looks like
/Foo?type=bar
Does anybody know how to test a route such as the one above?
Well i think that you need to clear out a bit the difference between route and query parameters.
In your case you are trying to use route parameters which will actually look something like:
/{type}/Foo/{page}/{date} => /myType/Foo/15/12-11-2021
Laravel considers the words inside {} as variables that you can retrieve via request so you can do something like:
$request->type
and laravel will return you the string myType as output.
In your second case that you have tried in the past you are referring to query parameters which are also a part of the $request. Consider it as the "body" of a GET request but i don't mean in any way to convert your post routes to GET :)
The thing with your second url is that:
/Foo/{type} is not similar to /Foo?type=bar
instead it should be like: /Foo/bar
In general query parameters are when you want to send an optional field most of the times in your GET endpoint (for filtering, pagination etc etc) and the route parameters are for mandatory fields that lead to sub-directories for example /Foo/{FooId}/Bar/{BarId}
The thing to remember is that you must be careful about your routes because variables can conflict with other routes.
For example a route looking like this:
Route::get('/foo/{fooId}', [FooController::class, 'getFoo']);
Route::get('/foo/bar', [BarController::class, 'getBar']);
will conflict because laravel will consider bar as the variable of the fooId so your second route can never be accessed.
The solution to this is to order your routes properly like:
Route::get('/foo/bar', [BarController::class, 'getBar']);
Route::get('/foo/{fooId}', [FooController::class, 'getFoo']);
So when you give as a route parameter anything else than bar your will go to your second route and have it working as expected.

Laravel 8 route order from bottom to top

I have installed Laravel 8 and work perfectly, and then i tried to learn about routing and try to make some routes like this
Route::view('testing', 'welcome')->name('testingWelcome');
Route::get('testing',[TestingController::class, 'noParameter'])->name('testingNoParam');
Route::view('testing', 'dashboard')->name('testingDashboard');
Some post in here said that routes in web.php work from top to bottom. But, thats not what i get when i called in url http://localhost/laraps/public/testing. it always called the bottom one. i tried to change the order, but still the last one always get called.
Any explanation for this one? or am i made any wrong configuration?
thanks for any help
A short explanation for this would be that each call to Route::{verb} creates a new route entry under your route collection (relevant code). {verb} ban be any HTTP verb e.g. get, or post etc. This entry is created under an array entry [{verb}][domain/url].
This means that when a new route is registered that matches the same URL with the same method it will overwrite the old one.
So in the case
Route::view('testing', 'welcome')->name('testingWelcome');
Route::get('testing',[TestingController::class, 'noParameter'])->name('testingNoParam');
Route::view('testing', 'dashboard')->name('testingDashboard');
Only the 3rd declaration actually "sticks". There are cases where multiple route definitions can match the same URL for example assume you have these routes:
Route::view('testing', 'welcome')->name('testingWelcome');
Route::get('testing/{optionalParameter?}',[TestingController::class, 'parameter'])->name('testingNoParam');
Route::view('testing/{otherParameter?}', 'dashboard')->name('testingDashboard');
In this case all 3 routes are added to the route collection, however when accessing URL example.com/testing the first matched route will be the one that will be called in this case the welcome view. This is because since all 3 routes are declared, once the router finds one matching route, it stops looking for more matches.
Note: There's generally no point in declaring multiple routes with the exact same URL so this is mainly an academic exercise. However there is often a use case for cases like e.g. model/{id} and model/list` to differentiate between getting info for a specific model and getting a list of models. In this case it's important to declare the routes as:
Route::get('model/list', [ ModelController::class, 'list' ]);
Route::get('model/{id}', [ ModelController::class, 'view' ]);
However you can be more explicit in route declarations using:
Route::get('model/{id}', [ ModelController::class, 'view' ])->where('id',
'\d+');
Route::get('model/list', [ ModelController::class, 'list' ]);
in this case the order does not matter because Laravel knows id can only be a number and therefore will not match model/list

laravel advanced routing to multiple controllers

I have in a middleware those two routes so they stay on top of all other routes
Route::get('{slug?}', array(
'as' => 'homeIndex',
'uses' => 'Modules\\Pages\\Controllers\\Pages#index'
))->where('slug', '(.*)?');
Route::get('{company?}', array(
'as' => 'companyProfile',
'uses' => 'Modules\\Company\\Controllers\\Profile#index'
))->where('company', '(.*)?');
what I'm trying to achieve is route all pages through homeIndex and all companies profile through companyProfile all on the first segment.
Is working fine for pages, but for companies profile I get 404.
It's same like facebook if you go on facebook.com/about the result is about page if you replace about with your unique name you get your profile.
Any ideas how to make it work?
Facebook works because about is a route. This works because your unique name can never be about. So they look for the about route first, and if the segment isn't about, they know it's probably a unique name.
Yours is different because your app doesn't know if your first segment is a slug or a company so you need some way to tell it the difference. If you redirected everything to a single function, then in that function did some queries or whatever you need to do to figure out if the first segment is a slug or a company name, then redirect that appropriately, it would work.
Laravel reads routes from the top to the bottom. When you hit the route /some-random-company, Laravel has no idea this is a company. All it knows is it happens to match the first route so it hits the slug route with your company. So another solution would be to update the wheres on your routes so Laravel has some idea if the incoming route parameter is a slug or a company and will know where to route that request to.
I'm terrible at regular expressions and I don't know how you are making slugs or if there is any rules you have setup on what a company name can or can not consist of. What you would have to do is figure out if there is anyway where you can accurately determine if the route parameter is a slug or company. For example, if it has one or more of -, it might be a slug.
Then you'd have to write a regular expression pattern to look for multiple - and then put that pattern into the where for the slug route. Then if the route parameter is a company and it does not match that pattern which was looking for one or more of -, Laravel will know to match this request with the company route.
If there is nothing which you can use to determine if a string is a slug or a company, you will have to update your routes so it looks like company/{company?} and slug/{slug?} and then output your links appropriately. This way Laravel will know for sure where to route that traffic.

Routing to Controller in Laravel4

I am using laravel for the first time and need some help understanding routes. I am setting up a view that will show a list of orders placed by customers. Above this list are 2 search boxes. One for searching by ID, the other for selecting a date. I'd like to have a "default" route so when id/date are NOT included in the route, we see all orders placed so far today.
Routes should be as follows:
orders - Should display all orders placed today.
orders/{id} - Should show only the specific order that belongs to that id.
orders/{date} -
Should show all orders placed on a specific date.
{id} and {date} should be distinguished by regular expressions.
I can get any one of the routes to work by themselves but when I try to create the routes for all 3 and modify my controller accordingly I break the others. One example is this:
Route::get('orders/{id}', 'OrderController#getOrders')->where('id', '[0-9]+');
Which works for getting the order by ID, but if I wanted to allow for dates as well I would have to change the route entirely. From that point I'd like to be able to go even further, for example the route orders/12345/edit should bring me to the view that allows me to edit the order.
How can I properly setup my routes/controller to do this?
Unless you manage to write a regular expression that validates dates or numeric values you have two options:
Write two different routes: one that validates dates and other that validates IDs. Both would point to different methods in the controller.
Use one route that doesn't validate its the parameter and that points to one method in the controller where the type of parameter would be checked for date or ID.
I like the first option better, because I believe both routes are similar yet very different.
EDIT
If you want to use the same form to target to different urls depending on the contents of inputs you have to use javascript, you can change the action in the form using:
$('#form').attr('action', "the_url");
And you'd have to set up a listener for the inputs to know which url to point to:
Detecting input change in jQuery?
I hope this helps you!
just make three routes like laravel documentation
orders route:
Route::get('orders', 'OrderController#getOrders');
orders by id route:
Route::get('orders/{id}','OrderController#getOrdersById')->where('id', '[0-9]+');
orders by data route:
Route::get('orders/{data}', 'OrderController#getOrdersByData')->where('name', '[A-Za-z]+');
also you can create three route into your OrderController like documentation

Laravel 4 Route Filter Never Called

I am sure that I am doing something wrong that is very obvious, but for some reason I cannot get any filters except App::before to work in my test application.
//routes.php
Route::get('site/login',
array(
'before'=>'science',
'as'=>'site/login',
'uses'=>'HomeController#getLogin',
)
);
Route::controller(site, 'HomeController');
//filters.php
App::before(function($request){
//var_dump("Before"); exit;
});
Route::filter('science',function(){
dd("Science B!TCH!");
exit;
});
//HomeController.php
public function getLogin(){
$this->layout->body = View::make('home.login');
}
The object was first to ensure that a user was not logged in so I was trying to use the built-in "guest" filter, but it was never being called. So I later created the "science" filter to test if ANY routes would work. If I uncomment the var_dump line in App::before, it displays "Before" and exits as expected.
Can anyone see what I am doing wrong here? When I go to the /site/login page I should see my Breaking Bad movie quote instead of the actual page. However, I am seeing my login form as if nothing was happening.
Thanks!
UPDATE:
I changed the route to look like this now:
//routes.php
Route::get('site/login', 'HomeController#getLogin')->before('science');
... and it works. I get the debugging string "SCIENCE ..." on the screen.
It also works if I do the following
//HomeController.php
public function __construct(){
$this->beforeFilter('science');
}
Are there any use cases or conditions in which the array version of routes gets ignored?
UPDATE 2:
In my efforts to simplify my original description I neglected to show other routes that were in routes.php. Take a look below.
//routes.php
Route::get('site/login',
array(
'before'=>'science',
'as'=>'site/login',
'uses'=>'HomeController#getLogin'
)
);
Route::post('site/login',
array(
'as'=>'site/login',
'uses'=>'HomeController#postLogin'
)
);
Having the POST route AFTER the GET route is what is causing the problem. When I put the POST route BEFORE the GET route, the GET route works with the filter as expected.
Now, I was under the impression that Laravel treated GET and POST requests differently, hence the usage of the different static methods in Route. However, apparently, this is not true as the filter on the latter affects the filter of the former.
Is this a correct assumption? Should I start a different thread about this? I would love to understand why this is working this way.
Thanks!
UPDATE 3
---- SOLVED ---
This tidbit of information is not specifically stated in the documentation but you cannot have identical route names even though those route names are going to different REST verbs.
//routes.php BEFORE
Route::get('site/login', array('as'=>'site/login','uses'=>'HomeController#getLogin', 'before'=>'science'));
Route::post('site/login', array('as'=>'site/login', 'uses'=>'HomeController#postLogin',));
In the above solution, the 2nd Route OVERRIDES the previous route because the "as" uses the same name. I thought that these would be treated differently since one is GET and the other POST, but this is not the case. The filter assignments must happen by name in the backend and, as such, using identical names will override each other.
//routes.php AFTER
Route::get('site/login', array('as'=>'site/login','uses'=>'HomeController#getLogin', 'before'=>'science'));
Route::post('site/login', array('as'=>'site/postLogin', 'uses'=>'HomeController#postLogin',));
As you can see here, I renamed the 'as' part of the array to 'site/postLogin' and I can now use different filters for each the POST, GET, and probably PUT, DELETE and etc.
For better practice if two or more routes use the same filter, those routes should belong in a group. I have a feeling that will correct the issue.
From http://laravel.com/docs/routing#route-groups
Route::group(array('before' => 'auth'), function()
{
Route::get('/', function()
{
// Has Auth Filter
});
Route::get('user/profile', function()
{
// Has Auth Filter
});
});
---- SOLVED ---
This tidbit of information is not specifically stated in the documentation but you cannot have identical route names even though those route names are going to different REST verbs.
//routes.php BEFORE
Route::get('site/login', array('as'=>'site/login','uses'=>'HomeController#getLogin', 'before'=>'science'));
Route::post('site/login', array('as'=>'site/login', 'uses'=>'HomeController#postLogin',));
In the above solution, the 2nd Route OVERRIDES the previous route because the "as" uses the same name. I thought that these would be treated differently since one is GET and the other POST, but this is not the case. The filter assignments must happen by name in the backend and, as such, using identical names will override each other.
//routes.php AFTER
Route::get('site/login', array('as'=>'site/login','uses'=>'HomeController#getLogin', 'before'=>'science'));
Route::post('site/login', array('as'=>'site/postLogin', 'uses'=>'HomeController#postLogin',));
As you can see here, I renamed the 'as' part of the array to 'site/postLogin' and I can now use different filters for each the POST, GET, and probably PUT, DELETE and etc.

Resources