CodeIgniter HMVC routes - codeigniter

Currently working on a CodeIgniter application using HMVC
The HMVC app has his own routing.php containing
$route['finance/bill/add'] = 'finance/show_addBill';
$route['finance/transaction/add'] = 'finance/show_addTransaction';
$route['finance/(:any)'] = 'finance/$1';
$route['finance'] = 'finance';
the application has an Finance controller.
When going to
http://localhost/finance** it goes to **public function index(){}
http://localhost/finance/transaction/add DOES NOT go to **public function show_addTransaction() {}
http://localhost/finance/addTransaction DOES goes to **public function show_addTransaction() {}
I can not figure out why above routes aren't working :S

You shouldn't be defining routes in an HMVC application (as a very strong rule of thumb - there are exceptions but it is rare).
You should have a folder structure like:
Modules
- Finance
- - Controllers
- - - finance //should have index, add and an other generic functions.
- - - transaction // transactions specific functions
- - - bill // bill specific functions.
The routing is automatic - along these lines:
url/finance -> look for Modules/Finance/Controllers/Finance/Index()
url/finance/bill -> it will look for Modules/Finance/Controllers/Finance(.php)/Bill() FIRST then Modules/Finance/Controllers/Bill(.php)/index()
So for your scenario you should have:
$route['finance/bill/add']
A bill.php controller - with class bill - with a method add
$route['finance/transaction/add']
A transaction.php controller - with class transaction - with a method add
$route['finance/(:any)']
Doesn't exist - as I said the URL routing is automatic so provided you have the relevant controllers and methods things will be found
$route['finance']
Simple finance.php controller with index method.

Related

Codeigniter 4 Current/Active Controller

In Codeigniter 3 is possible to get current active class and method with this code:
$active_controller = $this->router->fetch_class();
$active_function = $this->router->fetch_method();
Are there such functions in Codeigniter 4?
In CodeIgniter 4
$router = service('router');
$controller = $router->controllerName();
$router = service('router');
$method = $router->methodName();
Its worth saying those classes were never officially part of CI3 (https://codeigniter.com/user_guide/installation/upgrade_300.html?highlight=fetch_class). Bearing in mind CI4 is a lot more flexible and that routes are defined more variably I would look at the routing side of things and extract it from there (https://codeigniter4.github.io/userguide/incoming/incomingrequest.html#the-request-url).
You can use PHP constant or functions that provide you the same:
Get Current Function name:
__FUNCTION__
Get Current Class name:
__CLASS__
OR
get_class()
Codeigniter 4: All the above is working well otherwise use the same code that #mathan answered:
$router = service('router');
echo $router->controllerName();

How to convert CodeIgniter urls into user friendly ones?

I've explored lot of questions and articles regarding this but I can't find how to get this done.
I'm doing a website which provides specifications of several products such as phones, tablets, tv etc. Here's what I've:
Controller - Specs (create and display specification of all products)
Method - Display (fetches detailed specs of selected model and shows)
Method - Index (lists names of all models stored in the table. this is where I build anchor links)
Display method takes three arguments (1, 2, 3).
1 - Type of product (Phones, Tablets, TV etc)
2 - Model Slug (iphone-6, galaxy-tab-s3, bravia-kdl-50w800d etc)
3 - Model ID (1, 4, 13 etc)
My URLs right now are like this:
localhost/sitename/specs/display/phones/iphone-6/1
localhost/sitename/specs/display/tablets/galaxy-tab-s3/4
localhost/sitename/specs/display/tv/bravia-kdl-50w800d/13
What I want to achieve is URLs which are like this:
localhost/sitename/iphone-6
localhost/sitename/galaxy-tab-s3
localhost/sitename/bravia-kdl-50w800d
I don't mind restructuring my tables/controllers/methods or anything else if this can be achieved using whatever.
Thanks for reading.
Edit:
Route.php
$route['default_controller'] = 'Specs/index';
$route['404_override'] = 'Errors/show_404';
$route['translate_uri_dashes'] = FALSE;
This is how I'm building the anchor links (view_file->index.php, called from Index method):
<?php
foreach model(in the table)
echo anchor(specs_controller.display_function.product_type.model_slug.model_id, model_name);
end foreach
?>
I can get the desired URLs with following code in route.php. Only problem is I'm not able to make the 'urlController/urlMethod' return a value in the function which can be assigned to $result variable.
$route['(:any)'] = function ($1)
{
$result = 'urlController/urlMethod/'.$1;
return $result;
};
I'm not sure how to do this. Can someone suggest how I should call 'urlController/urlMethod'?
You could achieve it with CodeIgniter URI Routing. Considering
localhost/sitename/galaxy-tab-s3
maps to
localhost/sitename/specs/display/tablets/galaxy-tab-s3/4
And, model id i.e 4, in this case, is static with respect to galaxy tab s3, as you have not mentioned any such Id in the simplified URL.
My understanding is with every URL localhost/sitename/iphone-6, you need three details about the string 'iphone-6'. i.e. type of product, model-slug, model id. One way could be write something like
$route['sitename/(:any)'] = 'routingController/commonRoutingMethod/$1';
Here, create a new routingController and write some logic into commonRoutingMethod() method, which takes the string like iphone-6 and fetches its all three details i.e. product type, model id etc. And then redirects by building the exact URL using
header('Location: http://localhost/sitename/specs/display/$productType/$modelSlug/$modelId/');
NOTE : There could be more forward ways just using regex match in routes.php, given that you create diffrentiated structure of the string, based on product type and model id e.g p_iphone-6_1 or t_galaxy-tab-s3_4.
Please use below routing code, to achieve it.
localhost/sitename/specs/display/phones/iphone-6/1
localhost/sitename/specs/display/tablets/galaxy-tab-s3/4
localhost/sitename/specs/display/tv/bravia-kdl-50w800d/13
localhost/sitename/iphone-6
localhost/sitename/galaxy-tab-s3
localhost/sitename/bravia-kdl-50w800d
$route['(:any)'] = 'specs/display/$1/$1/$1';
Let me know if you need any help.

How to work with multiple levels of subcategories in Codeigniter?

What is the smartest solution to work with multiple levels of subcategories in Codeigniter? I want to make a web site for travel agency with travel offers, and the structure will look something like this:
- Cat1
- Cat2
--- Subcat1
--- Subcat2
------ Subcat1
--------- Subcat1
--------- Subcat2
------------ Subcat1
------------ Subcat2
--------------- offer1
--------------- offer2
--------------- offer3
--------------- offerN
------------ Subcat3
--------- SubcatN
------ Subcat2
------ SubcatN
--- Subcat3
--- SubcatN
- Cat3
--- offer1
--- offer2
--- offer3
--- offerN
- CatN
This is an example of the nested categories. Every (sub)category should have: Main picture, Description, and list of subcategories OR list of the offers.
I have some idea to do that with a table called Categories (ID, cat_name, parent) and if the parent is 0, that it is top level category, if the parent is some int, than that value is the ID of the parent category...
But I don't have idea how to solve the routing with the controller? I know how to do that with only 2 levels (main category and subcategories) but they are manually added to the routes. I don't know how to do that dynamically so there can be added more categories and subcategories in future through the admin panel.
Of course I will have another table for my offers, I know that, and I don’t have problems with that. The problem is how the controller(s) structure will look like?
For my present project with only 1 level of subcategory and without option to dynamically add subcategories, here is what I have in routes.php
$route[‘cat1’] = “category”;
$route[‘cat1/(:any)’] = “category/subcategory”;
$route[‘cat2’] = “category”;
$route[‘cat2/(:any)’] = “category/subcategory”;
$route[‘cat3’] = “category”;
$route[‘cat3/(:any)’] = “category/subcategory”;
$route[‘cat4’] = “category”;
$route[‘cat4/(:any)’] = “category/subcategory”;
$route[‘cat5’] = “category”;
In other words, cat1,2,3,4,5 are real names of the categories, and :any represents all of their subcategories and when the visitors types example.com/pc_news than it is redirected to the controller named category and it’s index function. if he types example.com/pc_news/hardware than he hell be redirected to the method subcategory of the controller category.
If I want to add more categories and subcategories, I will do that manually at the routes, but if I want to add subsubcategory (example.com/pc_news/hardware/something) I don’t know how to achieve that.
On the new project I want to do, I will have something like:
example.com/summer/turkey/airplane/alanya/hotels # and it will list all the offers
example.com/summer/turkey/airplane/alanya/apartments
example.com/summer/turkey/bus/kushadasi
example.com/winter/bus/france/hotels
example.com/winter/airplane/france/hotels
Anybody with some idea how to do this?
As for the routing, i would just use the routes config to send everything to a method where you can implement any logic you want, if you have methods you still want to access inside that controller, you have to list them before the "catch any" line something like this:
config/routes.php
$route['products/save/(.*)'] = 'products/save/$1';
$route['products/(.*)'] = 'products/index/$1';
Once you have this set up, you will get every parameter inside the Products::index method:
controllers/products.php
class Products extends CI_Controller {
public function index() {
$args = func_get_args();
// now $args holds the url segments
$categories = array();
foreach ($args as $cat_name) {
$categories[] = $this->db
->from('categories')
->where('cat_name', $cat_name)
->get()
->row();
}
}
}
As for storing the categories, if you only have the parent_id in rows you will have to loop or use a bunch of joins to get every category. If you have only 3-4 levels it probably not really a problem, also probably won't change too often so it could be easily cached. If you want a more efficient solution look into the nested set models.
Update (I've expanded the question from the linked forum)
You can use the special 404_override key (at the bottom of the page) to make every request go to a specific controller's index method. Don't forget that you will need to handle 404 url's inside the Products controller!:
config/routes.php
$route['404_override'] = 'products';
The original answer can be expanded to do this, whitelist everything you don't want to send to the Products controller (every other controller's name basically) and have a catch-all route at the end to send other's to the Products controller. 404 url's still need to be handled.
config/routes.php
// place any specific rules on top
$route['about'] = 'main/about';
// have whitelist existing controllers, make them go to their original place like it normaly would
$route['(controller1|controller2|controller3)(/?.*)'] = '$1$2';
$route['(:any)'] = 'products/index/$1'; // the last "catch all" row goes here

EdmFunction must be qualified with model name?

Book "Johnson Glenn - Accessing Data with Microsoft .NET Framework 4 - 2011" contains example of custom function, and call of function:
gv.DataSource = (... select new { ... , ModelDefinedFunctions.Detail(..)});
But on CD in practice tests with that book:
var instructors = from p in context.People
where YearsSince((DateTime)p.HireDate) > 10
select p;
and explanation: You must refer to the custom function using the method name. The method name must not be qualified with the model name. The method requires a DateTime parameter and this must be included.
Don't have VS now, what is the right way?
ModelDefinedFunctions is the name of the class where the method Detail is defined. The two approaches are identical. There is no qualifier for the model name.

Grails chained drop down

I'm trying to implement chained drop down boxes using the tutorial here. My classes are not as straight forward as the ones in the tutorial though.
I want to chain the drop down boxes for the create.gsp view in the Load class. Each load belongs to an account from the Account class, and each account belongs to a user from the User class, and each user has several cargo destinations from the Address class.
My goal is to have the cargo destination field up date based on which account is selected.
I am having trouble understanding the AJAX function in the tutorial (step 3), and how it relates to the Grails function (step 4).
Here is the AJAX code:
function respondToSelect(event)
{
new Ajax.Updater("memberSelect",
"/chainedSelect/family/updateSelect",
{method:'get', parameters: {selectedValue : $F("familySelect")} }
);
}
Here is the Grails method:
def updateSelect = {
def familySelected = Family.find("from Family as family where family.surname=:surname", [surname:params.selectedValue])
render (template:"selectMember", model : ['familySelected' : familySelected])
}
If someone could just explain what the third parameter of the AJAX function is doing I think I can figure the Grails part out.
{method:'get', parameters: {selectedValue : $F("account")}}
If someone could just explain what the third parameter of the AJAX
function is doing
The third argument is an object of parameters that get passed to the Updater that tell it how to make the HTTP request to the server.
Make the request an HTTP GET request:
method:'get'
Pass the following named query parameters:
{selectedValue: $F("account")}
$F is a prototype shortcut to retrieve the value of an element. In this case, it's getting the selected value of the DOM element with id account.
This ultimately results in something like the following request:
GET /chainedSelect/family/updateSelect?selectedValue=someValue
Where "someValue" is the currently-selected item in the "account" select list.

Resources