Laravel routing - two url addresses with similar names - php

I'm doing forum with Laravel.
I decides to have routes like this:
Route::get('/{topicName}', 'ForumController#showTopic');
Route::get('/{postSubject}', 'ForumController#showPost');
I have also another routes, but this two are on the bottom, because, when I write sth in URL (and laravel doesn't find passing address) then everything fall into this routes (especially the first one). I don't know how to programm this to work this two last routes.
When someone add my Topic name, then he's going on the site:
http:/forum/php
or
http:/forum/javaScript
Then user see all posts to this Topic. But when user want to see one specific post, then I want to be in url like this:
http:/forum/post_subject_name
And now user can see specific post.
How to do this, because now everything fall to my first controller - ForumController#showTopic'). Is this possible?

You can't, both those routes have the same requirement, if they are at the bottom of your routes, the topicName one will always take priority.
You should have routes such as
Route::get('/topics/{topicName}', 'ForumController#showTopic');
Route::get('/posts/{postSubject}', 'ForumController#showPost');
That way you can distinguish between them

Problem is that both routes consist of one parameter and nothing else. How is it supposed to know if the given parameter is a topic name or a post subject?
However, what you could do is having one route and do the rest in the controller method:
Route::get('/{topicOrPost}', 'ForumController#showTopicOrPost');
public function showTopicOrPost($topicOrPost)
{
$topic = Topic::where('name', $topicOrPost)->first();
if ($topic !== null) {
// show the topic
} else {
$post = Post::where('subject', $topicOrPost)->first();
if ($post !== null) {
// show the post
} else {
// neither topic or post found
}
}
}
But then, of course, you'd have to ensure that there aren't topic and post with the same name/subject.

in my opinion you could use pattern in route
Regular Expression Constraints
some example :
// This is what you might have right now
Route::get('users/{id}', 'UserController#getProfile')->where('id', '[\d+]+');
Route::get('products/{id}', 'ProductController#getProfile')->where('id', '[\d+]+');
Route::get('articles/{slug}', 'ArticleController#getFull')->where('slug', '[a-z0-9-]+');
Route::get('faq/{slug}', 'FaqController#getQuestion')->where('slug', '[a-z0-9-]+');
// and many more, now imagine you'll have to change the rule
// Instead, you could have a handy list of patterns and reuse them everywhere:
// Patterns
Route::pattern('id', '\d+');
Route::pattern('hash', '[a-z0-9]+');
Route::pattern('hex', '[a-f0-9]+');
Route::pattern('uuid', '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}');
Route::pattern('base', '[a-zA-Z0-9]+');
Route::pattern('slug', '[a-z0-9-]+');
Route
::pattern('username', '[a-z0-9_-]{3,16}');
and for you I think you could use something like this:
Route::get('/{any}','SearchController#showPost')->where('any','^[post_]+$');
Route::get('/{any}','SearchController#showTopic');
but you have to use ShowTopic route after showpost

Related

Nested Controllers and routes

I'm creating a dashboard where the user can create Clients
Each Client will have: Categories, Employees, ...
Now I'm wondering how to structure the routes.
For example if I create the following: (pseudo code)
Route::get('clients/{id}/');
Route::get('clients/{id}/categories');
Route::get('clients/{id}/categories/{category}');
Route::get('clients/{id}/categories/{category}/questions/{question}');
This seems like a lot of unnecessary parameters..
How do you guys go about this? I really want to split the categories on a seperate page, the employees on a seperate page.
Thank you.
in all of my projects, i avoid using a lot of nested entities in the URL, so i access each one apart, this was also recommended by #jeffrey_way at Laracasts, the training website for laravel.
so, i would do the following:
Route::get('clients/{id}/');
Route::get('categories/{client_id}');
Route::get('categorie/{category}'); //not that i have removed the plural s from categorie(s)
Route::get('question/{question}');
Good luck
It honestly depends on how big your application is going to become, I would probably group them, so still keeping the same structure.
Route::group('clients/{id}', function()
{
Route::get('/');
Route::group('categories', function()
{
Route::get('/');
Route::get('{category}');
Route::get('{category}/questions/{question}');
})
})
Same as yours but I feel it a little cleaning for later if you expand on the categories or clients.
Here in such case I would rather prefer to use only one route will all parameters as in GET method. So, I would have add just one param as bellow:
Route::get('client/create', 'ClientController#store');
So, all the parameters will be maintained by the store method of ClientController like below:
public function store(Request $request){
$category = $request->get('category')
//......
//get other get parameters like this when required
}
When I need to trigger this route I would just do something like below:
Create link
As you know, we can pass our parameters here using our old global friend GET variable.

Redirect specific routes to page if they don't exist with laravel 4

I have a route that needs to be redirected to another page if the data they're pulling doesn't exist. The route is:
Route::get('{link}/{data}', 'LinkController#getLink');
Where {link} and {data} are model bound with:
Route::model('link', 'Link');
Route::model('data', 'Data');
As is, when the data for this link doesn't exist it 404's, and if it does exist, it's taken to the page as it should. What I would like to do is redirect to another page if the link would otherwise 404. I've found suggestions on how to do this globally, but I only want it to happen on this one route.
Any ideas?
// Link Controller
public function getLink($linkId, $dataId)
{
if ( is_null($link) or is_null($data) ) {
return Redirect::to('some/path');
}
}
If either of the passed models are null when it hits your controller method, just redirect them. As for your /{link} route that you refer to but don't show code for, do something similar in whatever closure/controller you handle that in.
Get rid of the model binding - you've left the cookie cutter realm.
Route::get('{link}/{data?}', 'LinkController#getLink');
// note I made the data ^ parameter optional
// not sure if you want to use it like this but it's worth pointing out
Do all of the model checking in the controller, something like this:
public function getLink($linkId, $dataId)
{
$link = Link::find($linkId);
$data = Data::find($dataId);
if(is_null($link)){
throw new NotFoundHttpException;// 404
}
elseif(is_null($data)){
return Redirect::to('some/view');// redirect
}
// You could also check for both not found and handle that case differently as well.
}
It's hard to tell from your comments exactly how you'd like to treat missing link and/or data records, but I'm sure you can figure that out logically. The point of this answer is that you don't need to use Laravel's model binding since you can do it yourself: find the record(s) else redirect or 404.

Custom route not working | Codeigniter Routing

I am working on an API via which I embed images of country flags on my website & several others.
I am taking in 3 parameters i.e
Country (Name of Country - ISO Code or Full Name)
Size (Dimension of Image)
Type (Styles like flat flag, shiny round flag etc...)
Now, have everything setup correctly but stuck in handling URI.
Controller -> flags.php
Function -> index()
What I have now is :
http://imageserver.com/flags?country=india&size=64&style=round
What I want
http://imageserver.com/flag/india/64/round
I went through some articles and made this route but all of them failed
$route['flag/(:any)/(:num)/(:any)'] = "welcome/index/country/$1/size/$2/style/$3";
$route['flag/(:any)/(:num)/(:any)'] = "welcome/index/$1/$2/$3";
$route['flag/(:any)/(:num)/(:any)'] = "welcome/index?country=$1&size=$2&style=$3";
I have also been having trouble with routes while writing my custom cms. Reading through your question, I see a couple issues that might very well be the answer you are looking for.
For starters, let's look at the routes you have tried:
$route['flag/(:any)/(:num)/(:any)'] = "welcome/index/country/$1/size/$2/style/$3";
$route['flag/(:any)/(:num)/(:any)'] = "welcome/index/$1/$2/$3";
$route['flag/(:any)/(:num)/(:any)'] = "welcome/index?country=$1&size=$2&style=$3";
If you want to run the index method from your flags class, which it looks like you do, you don't want to route to the welcome class at all. Currently, however, you are. Your routes should look like:
$route['flag/(:any)/(:num)/(:any)'] = "flags/index";
That way, Codeigniter will run the index method from your flags class. You don't have to worry about the country, size, or style/type in the route. The best option there would be to use the URI segment function like this:
$country = $this->uri->segment(2); //this would return India as per your uri example.
$size = $this->uri->segment(3); //this would return 64 as per your uri example.
$style = $this->uri->segment(4); //this would return round as per your uri example.
You could then use those variables to query your database and get the correct flag or whatever else you need to do with them.
So to restate my answer with a little more explanation as to why:
The routes you have currently are running the welcome controller/class and the index function/method of that controller/class. This, obviously, is not what you want. So you need to make sure your routes are pointing to the correct controller and function like I did above. The extra segments of the URI don't need to be in your route declaration, so you would then just use the uri_segment() function to get the value of each segment and do what you need with them.
I hope this helps you. I may not have found an answer for my problem, but at least I could provide an answer for someone else. If this seems confusing to you, check out the user guide at http://ellislab.com/codeigniter/user-guide. The main links you need for this are:
http://ellislab.com/codeigniter/user-guide/libraries/uri.html
and
http://ellislab.com/codeigniter/user-guide/general/routing.html
Let me know if you need more help or if this helped solve your problem.

Dynamic routing

I want to be able to choose a controller based on data gathered form the uri.
I have a categories table and a subcategories table. Basically I have a URL in the following format (:any)/(:any). The first wildcard is a city slug (i.e edinburgh) and the second is going to be either a category or a subcategory slug.
So in my route I search for categories with that route, if I find it, I want to use controller: forsale and method: get_category. If it's not a category I'll look up subcategories, if I find it in there I want to use controller: forsale and method: get_subcategory. If it's not a subcategory I want to continue looking for other routes.
Route::get('(:any)/(:any)', array('as'=>'city_category', function($city_slug, $category_slug){
// is it a category?
$category = Category::where_slug($category_slug)->first();
if($category) {
// redirect to controller/method
}
// is it a subcategory?
$subcategory = Subcategory::where_slug($category_slug)->first();
if($subcategory) {
// redirect to controller/method
}
// continue looking for other routes
}));
First off I'm not sure how to call a controller/method here without actually redirecting (thus changing the url again).
And secondly, is this even the best way to do this? I started using /city_slug/category_slug/subcategory_slug. But I want to only show city_slug/category|subcategory_slug but I need a way to tell which the second slug is.
Lastly, there may be other URL's in use that follow (:any)/(:any) so I need it to be able to continue looking for other routes as well.
Answer to your questions in order:
1. Instead of using different controller#action's you could use a single action and based on the second slug (category or subcategory), render a different view (although I don't like this approach, see #2 and #3):
public class Forsale_Controller extends Base_Controller {
public function get_products($city, $category_slug) {
$category = Category::where_slug($category_slug)->first();
if($category) {
// Do whatever you want to do!
return View::make('forsale.category')->with(/* pass in your data */);
}
$subcategory = Subcategory::where_slug($category_slug)->first();
if($subcategory) {
// Do whatever you want to do!
return View::make('forsale.sub_category')->with(/* pass in your data */);
}
}
}
2. I think /city_slug/category_slug/subcategory_slug is way better than your method! You should go with this one!!
3. Again, you should revise your routes. I always try to make my routes in a way that they don't confuse me, neither Laravel!! Something like /products/city/category/subcategory is much more clear!
Hope it helps (my code is more like a psudocode, it's not been tested )!

CodeIgniter route overridden by controller/method

I have in my routes.php:
$route['ctrller1/method1/video/(:num)'] = 'ctrller2/method2/$1';
I also have a controller that is named ctrller1 that has a method:
function method1 ($str = NULL) {
// do something
}
The problem is I have to use controller2 coz I can't or shouldn't edit controller1. What I want is seemingly simple but, apparently, CI doesn't want to work with me.
When the url:
domain.com/ctrller1/method1/edit
is invoked, I want the method inside ctrller1 to be called, if domain.com/ctrller1/method1/videos/1
is invoked I want the method in ctrller2 called.
It all seems correct to me but it won't work. So, I must be missing something. I've tried adding this to the routing:
$route['ctrller1/method1/(edit)'] = 'ctrller1/method1/($1)';
But it's a no go. Anyone see anything wrong here?
At any time when you work with routes, just like permissions (firewall, etc;) order is important. Typically you want to organize your routes in this order:
MOST DEFINED
LESS DEFINED
GENERAL / FALLBACK
To clarify, that means your order for routes should be like this:
$route['ctrller1/method1/videos/view/(:num)'] = 'ctrller2/method3/$1';
$route['ctrller1/method1/videos/(:num)'] = 'ctrller2/method2/$1';
$route['ctrller1/(:num)'] = 'ctrller2/method1/$1';
When the URL is called, the route table goes through and finds the FIRST closest match, ELSE it traverses to the next route.
In this case what you want is something like this:
domain.com/ctrller1/method1/videos/1
domain.com/ctrller1/method1/edit
Reasoning for that is, the video's route is more specific, and also is a SPECIAL CASE, as you route it to another controller behind the scenes.
Here is what your routes should look like then (not tested, but should be it):
$route['ctrller1/method1/videos/(:num)'] = 'ctrller2/method2/$1';
$route['ctrller1/method1/edit'] = 'ctrller1/method1';
As a side, note, I am curious why you format it ctrller1/method1/videos/ and not something like ctrller1/videos/view/12355 or ctrller1/videos/edit/12355, the method1 seems confusing. But again I don't have all the details here.
Hope that works for you, if not comment, and I will revisit your question if you clarify it a little more.
Well you have video on one place and videos on another?
Either change to
$route['ctrller1/method1/videos/(:num)'] = 'ctrller2/method2/$1';
or try url: domain.com/ctrller1/method1/video/1

Categories