Codeigniter Dynamic Menu/MY_Controller question - php

I generate a menu from a database table using a function, and I've placed this in an extended base controller class:
<?php
class MY_Controller extends Controller {
public function __construct()
{
parent::Controller();
}
public function category_menu()
{
$this->load->model('category_model', 'category');
$categories = $this->category->get_categories();
$menu ="<ul class=\"menu_body\" id=\"nav_categories\">\n";
foreach($categories->result() as $row)
{
$menu .= "\t<li>" . anchor('listing/view' . $row->url, $row->name) . "</li>\n";
}
$menu .= "</ul>\n";
return $menu;
}
}
then naturally my controller looks like ~
<?php
class Site extends MY_Controller {
function __construct()
{
parent::__construct();
}
function index()
{
$data['menu'] = $this->category_menu();
$this->load->view('view', $data);
}
}
this does work, but it seems inefficient to have to do this for ~every~ page/view?
Or is this just a limitation of CI/MVC and there's no other way of doing it.
thanks for any insight

The better way of doing this is rendering content in views. You can have partial templates, you don't need to do string appending in the controller:
$categories = $this->category->get_categories();
$data['menu'] = $this->load->view('menu', array('data'=>$categories), TRUE);
$this->load->view('view', $data);
The TRUE in the call to view tell the function to return the rendered content and not put it into the buffer. You can then pass it to 'view'. You can also get the categories and pass them into the view 'view' and load the partial template from there.

If your menu entries won't be changing much you can probably cache the resulting array (serialized to file, to memcache, wherever) and use that to build your menu instead of querying the database on every pageload. Then you'll query the database only if the cache is not valid.
This way you should either expire the cache every few minutes so you get fresh data from the database, or make sure that whenever you update the menu structure from elsewhere (say, a CMS), you invalidate the cache (delete file, delete memcache key etc) so that next time you call category_menu() you get fresh data.
As a sidenote, Ken Struys raises a very valid point as well and you should consider it. The whole point of Controllers and Views is so that you can separate your code from your html.

Related

Routing independant controllers and models in fat free framework

I am trying to create Main site navigation, Sidebar Navigation and Footer navigation in Fat Free environment. I am just starting to work with frameworks especially with MVC type.
My problem, since my navigation will be almost on every page of the website, I was thinking about creating separate controller and model to handle all this staff, but not sure how it would work without making routing?
Also, I am not sure how to handle join in the model, I could not find any information about this online at all.
Here is my current Category Controller
class Categories extends DB\SQL\Mapper
{
public function __construct(DB\SQL $db)
{
parent::__construct($db, 'categories');
}
public function all()
{
$this->load();
return $this->query;
}
public function getByID($id)
{
$this->load(array('id=?', $id));
return $this->query;
}
public function getBySlug($category_slug)
{
$this->load(array('category_slug=?', $category_slug));
return $this->query;
}
public function add()
{
$this->copyfrom('POST');
$this->save();
}
public function edit($id)
{
$this->load(array('id=?', $id));
$this->copyfrom('POST');
$this->update();
}
public function delete($id)
{
$this->load(array('id=?', $id));
$this->erase();
}
}
any ideas or pointers will help me to go a long way.
Thanks in advance
I don't know if understood what your issue is, but if you want to make the categories available for all methods in a Controller, you could use the beforeRoute() method:
class TestController extends MainController {
// runs before routing
// if another controller extends TestController and also has a
// beforeRoute, this will be overriden
function beforeRoute() {
// Load Categories
$categories = new Categories($this->db);
// Assiging ->all() to variable makes the method return an array of all results
$categoriesArray = $categories->all();
// Set an array in the hive for template use
$this->f3->set('categories', $categoriesArray );
// Clear the instance
$categories->reset();
}
function renderPage() {
// the 'categories' hive variable is available because beforeRoute has been run
// Set the page title from the dictionary file
$this->f3->set('pageTitle', $this->f3->get('DICT_'.'page_whatever') );
// Render the View
$this->f3->set('view','page.whatever.htm');
$template=\Template::instance();
echo $template->render('layout.sidebar.htm');
}
// End of Controller
}
And of course, in the template:
<repeat group="#categories" value="#category">
<li>
{{ #category.label }}
</li>
</repeat>
ps: You might be using $f3 instead of $this->f3 (and the same for $db)

Codeigniter HMVC pass data from module to controller

I'm building an application in Codigniter with HMVC. In it I call a module inside another controller. The problem I've run into is trying to pass/retrieve the data loaded into module. Specifically, I'm loading some javascript files that I would then like to pass to the calling controller.
Here is a simplified code:
public function module()
{
...
$this->data['js'] = $this->js_assets;
...
return $this->load->view('module_view', $this->data, true);
}
public function controller()
{
...
$this->load->module('module/module');
$this->data['module'] = $this->module->module();
...
}
I know that I can retrieve data['js'] in module_view as $js, but I wonder if I can just pass the data directly to the controller.
What I'd like to do is get data from the module to the calling controller
Yes, you can do that with HMVC.
In the calling module you can organise code like this (pseudocode):
class Someclass extends CI_Controller {
...
public function show_page()
{
$this->load->module("module");
$js_files = $this->module->get_js_files();
$this->load->view("header", array("js_files" => $js_files));
}
...
}
In the callee module you would write something along these lines:
class Module extends CI_Controller {
...
public function get_js_files()
{
$scripts = $this->frontend_model->get_scripts();
return $scripts;
}
....
}
(Although in this fantasy case it would be wiser to get data from model in the first place)
Another technique is, like #wolfgang1983 correctly mentioned, to call another module like this:
Modules::run('modulename/controller/function', $data);
and in order to get data from that controller you just have to assign returned data to a variable:
$data_received = Modules::run('modulename/controller/function', $data);
But in this case you can't get variables, only buffered output (like loaded and processed view files or echoed statements).

One Method Controller, Multiple View, Codeigniter

So, lets say I have a global view and specific view.
In global view, the url may look like this (http://example.com/index.php/controller/method/)
Where when it come to the specific page view, the url will turn like this:
(http://example.com/index.php/controller/method/1989-03-25)
The difference between the global view and the specific page view is, if in the global view it shows the information in general, but in the specific page view it shows based on the detail or the date.
Of course, not only have different view, but also they will have different function of models.
The point is, I just want to make the url keep in order (which it mean there is no change in the name of the controller method).
How to do this. Thanks in advance.
You create just one param into your function. And set the param value is null. like this
class YOUR_CLASS extends CI_controller {
public function method($params=null) //null is Empty value
{
if($params)
{
//load your modal and view with your param
}
else
{
//load your modal and view
}
}
}
This method supports the following type of URL's without any issue.
http://example.com/index.php/YOUR_CLASS/method/
http://example.com/index.php/YOUR_CLASS/method/1989-03-25
Hope this will help you....
This class used to wrap CI_controller, my_base_controller will override CI_controller methods for depends to your project needs
class my_base_controller extends CI_controller {
}
You can load models by known states and define it in my_base_controller class.
class my_controller extends my_base_controller{
public function method($params=null) //null is option value
{
}
}
Good luck!
You can add additional parameter in your method like:
class Your_controller extends CI_controller {
public function method($params = null)
{
// Your Code Here with the condition for processing of the $params variable
}
}
in which that $params can be something in your URL like:
http://example.com/controller/method/your-dynamic-params
So if the $params is null you will call the model the views the general and if the $params has a specific value you can call other model by using if or switch conditional statements. Hope this helps...
Update with Example
you can use the $params variable like this:
if ($params == "1991") {
$this->load->view('general.html', $params);
} elseif ($params == "1992") {
$this->load->view('year_1992.html', $params);
} else {
$this->load->view('other_years.html', $params)
}
in this way you can use the $params as a conditional variable to load different views.
or using switch:
switch($params) {
case '1991':
$this->load->view('general.html', $params);
break;
case '1992':
$this->load->view('year_1992.html', $params);
break;
default:
$this->load->view('other_years.html', $params)
}
Note: Use a helper method so you can avoid fat controllers because it will be hard to test your methods if you have a several lines of codes in a function.

Zend Framework1, Is there a way to override Zend_View_Helper_* functions per Controller?

I have a View Helper like below and it is working perfectly fine:
class Zend_View_Helper_List extends Zend_View_Helper_Abstract
{
public function list()
{
return '<ul><li>Something Really Good</li></ul>'; // generated html
}
}
I have this in my global layout myapp.phtml:
<div><?php echo $this->list(); ?></div>
My question is how can I override list() in different controller or even more granular, each controller action?
I have tried to set a View variable in each controller e.g. $this->view->type and then pass it to the list function <div><?php echo $this->list($this->type); ?></div>, but it looks dirty and not right!
You can put your helpers in a view/helpers folder of specific Controller so it will be visible for this controller only.
You can also add new path for Helpers $this->view->setHelperPath('MyScripts/View/Helper/','MyScripts_View_Helper'); if you need to change it per Action.
If you only want to have a single view helper you can effectively use variables.
You can try something like this:
In your foo action:
$this->view->type = 'action_foo';
In your View Helper:
public function list()
{
if (isset($this->view->type)){
if ('action_foo' == $this->view->type)
return '<ul><li>Something Really Good for Foo Action</li></ul>'; // generated html
else
return '<ul><li>' . $this->view->type . '</li></ul>'; // generated html
}
else
return '<ul><li>Something Really Good</li></ul>'; // generated html
}

CodeIgniter - every controller need 3 variables passed to views

I'm in the middle of cleaning up a web app written in CodeIgniter. Each main view has a sidebar that displays some common database driven data - recent updates, news, etc. I have the queries cached to help speed things up a bit but I'm also looking for a better way to make this happen.
Right now each controller loads the model, loads up the $data array and passes it to the view.
I've extended the main CI controller class and am not loading up the data in the constructor which has simplified the code a bit but I'm still left feeling like there should be a better way. Any suggestions?
not sure if that's what you're asking , but you can have a extra parent/base controller to load repetitive data like news , updates , etc...
class baseController extends CI_Controller {
public function __construct(){
parent::__construct();
}
function viwe($view = 'defualt' , $data = array()){
$data['updates'] = $this->db->get('updates');
$data['news'] = $this->db->get('news');
$this->load->view($view , $data );
}
}
another controller :
include "baseController.php";
class another extends baseController {
public function __construct(){
parent::__construct();
}
function index()
{
$data['thisData'] = $this->db->get('thisdata');
$this->view('thisView' , $data );
}
}

Categories