Dynamic routing - php

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 )!

Related

Laravel routing - two url addresses with similar names

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

How to visualize pages from further down the url?

This being the worst title imaginable, let me explain my concern as best as I can. So basically in my framework I use URLs of the type site.com/controller/method/parameters
I'll take this page for example site.com/news/edit/12
Then, if I apply no routing, my controller's name has to be News, the called method will be edit with a single parameter 12. Then if, for the sake of demonstrating my concern, I decide to do editing of the news title and body in different pages, I would have to navigate to site.com/news/edit/12/title. Which is where it gets messy and is basically what I want to ask:
What is the proper way of including pages that depend on parameters passed to the controller?
IMHO this looks pretty ugly, and what if I have further more such page separations down the page tree
class News {
public function edit($id, $section){
if(method_exists($this, $section){
return $this->$section($id);
}
}
private function title($id){
// Display page to edit news title
}
private function body($id){
// Display page to edit news body
}
}
Not to mention that this can also cause potential problems with calling existing methods that are not supposed to be called.
Please don't execute methods dynamically based on get params. This can be very dangerous.
One simple way with standard routing:
// site.com/news/edit-title/12
public function editTitle($id);
// or depending on your framework
public function edit_title($id);
Another perhaps more cleaner solution would be to build your custom routes:
// site.com/news/edit/title/12
public function editTitle($id);

how to create a method on the fly in ci

I'm writing a control panel for my image site. I have a controller called category which looks like this:
class category extends ci_controller
{
function index(){}// the default and when it called it returns all categories
function edit(){}
function delete(){}
function get_posts($id)//to get all the posts associated with submitted category name
{
}
}
What I need is when I call http://mysite/category/category_name I get all the posts without having to call the get_posts() method having to call it from the url.
I want to do it without using the .haccess file or route.
Is there a way to create a method on the fly in CodeIgniter?
function index(){
$category = $this->uri->segment(2);
if($category)
{
get_posts($category); // you need to get id in there or before.
}
// handle view stuff here
}
The way I read your request is that you want index to handle everything based on whether or not there is a category in a uri segment. You COULD do it that way but really, why would you?
It is illogical to insist on NOT using a normal feature of a framework without explaining exactly why you don't want to. If you have access to this controller, you have access to routes. So why don't you want to use them?
EDIT
$route['category/:any'] = "category/get_posts";
That WOULD send edit and delete to get_posts, but you could also just define those above the category route
$route['category/edit/:num'] = "category/edit";
$route['category/delete/:num'] = "category/delete";
$route['category/:any'] = "category/get_posts";
That would resolve for the edit and delete before the category fetch. Since you only have 2 methods that conflict then this shouldn't really be that much of a concern.
To create method on the fly yii is the best among PHP framework.Quite simple and powerful with Gii & CRUD
http://www.yiiframework.com/doc/guide/1.1/en/quickstart.first-app
But I am a big CI fan not Yii. yii is also cool though.
but Codeigniter has an alternative , web solution.
http://formigniter.org/ here.

How to apply codeigniter's URL_TITLE value to the URL

I'm completely new to ci,
I have a url something like this:
http://localhost/mvc/post/prod_id/1
And I want it to be:
http://localhost/mvc/post/my-best-product
So far I'm able to manage to route all that to home/post/ and learned the segment function also.
But my question is how do I really get the url_title out to the actual url.
I couldn't find any information on this particular subject. All I could find is how to use the url_title and how to route in ci. But they don't explain how we can actually change the base url name.
Please guide me to the right direction.
solution Example:
public function my_method($product_slug)
{
$product1 = "training-for-recruitment";
$product2 = "training-for-od";
if($product_slug==$product1)
{
$this->load->view('prod1');
}else if($product_slug==$product2)
{
$this->load->view('prod2');
}else{
show_404();
}
}
This is not what exactly I'm going to do. It is just for others to understand the workaround of Slugs.
Generate a unique slug for each of your product. Add a field for it on product table and every time, while selecting a product from table, use that slug instead of getting the product from primary id.
So, your function becomes like:
function product($product_slug)
{
//get product by slug from database
//load view page
}
Now, in config/routes.php
$route['your_controller_name/(:any)'] = "your_controller_name/product/$1";
You need to do this:
Set up some logic that translates "my-best-product" to "1"
Set up routing in CI that calls your 'prod_id' controller and passes the URI vars

How can I achieve this URI scheme in CodeIgniter?

I'm making a blog site with CodeIgniter, and I'd like to use the first URI segment as a dynamic category.
So all of these URI's:
http://example.com/category1/some-post
http://example.com/category2/some-post
http://example.com/category3/some-post
Would all go to the "categories" controller, where it would find the category from the database.
I can achieve this easily enough with $routes['(:any)'] = 'categories'; , but of course then all of my other static controllers wouldn't work, such as admin, user, etc.
So how can I achieve this URI scheme while allowing other static pages to not be routed?
You can't unless you create specific routes for each category manually.
For instance:
$route['php'] = 'categories';
$route['java'] = 'categories';
If you look at wordpress they start categories with /category/ and all pages are anything else so you should probably do it that way round.
You will, as Francois said, have to create a route for each category. This can be slightly simplified with a simple array as below:
$categories = array('cat1','cat2','etc...');
foreach($categories as $cat)
{
$routes["$cat/(:any)"] = "category/$cat/\$1"
}
A possible alternative is to use a pre-controller hook in which you could query your database to see if the segment is a category and then direct the request appropriately.
If you'll have a lot of categories or they will be added to and updated frequently, I'd recommend the second option, otherwise, maintaining a simple array isn't too bad.
The other option is to declare all your static controllers in your routes...

Categories