I'm using CodeIgniter and have a menu on the site that needs to read a list of cities from the database. This is simple to do if it's just on one or two pages - I load the model and call a function from the controller, and pass the data into the view.
But if I want it on every page, that means I have to keep copying the same code to every single controller function and pass the data into the view. (Note, I'm using a separate "header" view that contains the menu.)
What's the best way to automatically load some data on every page load and have it available to my view?
Create a new root controller class like MY_Controller.
You can read how here: https://www.codeigniter.com/user_guide/general/core_classes.html
Then make all your controllers extend that class.
Add a function in MY_Controller like this:
function show_view_with_menu($view_name, $data) {
$menu_data = $this->menu_model->get_menu(); // load your menu data from the db
$this->load->view('header', $menu_data); // display your header by giving it the menu
$this->load->view($view_name, $data); // the actual view you wanna load
$this->load->view('footer'); // footer, if you have one
}
Whenever you normally do load a view, instead do this:
$this->show_view_with_menu('view_for_this_controller', $data);
You define your own Application_Controller, which extends CI_Controller. All of your own controllers then extend your Application_Controller rather than the CI_Controller.
In the __construct() of your Application_Controller you'll introduce the code you've been copying and pasting everywhere previously.
My solution was just to create a display class that handles these things. A simplified version:
class Display
{
public function load_pages($name, $data = array()) {
$CI =& get_instance();
// Top and header templates
$CI->load->view('header.php', $data);
// Default to loading the one template file
$CI->load->view($name, $data);
// Footer template
$CI->load->view('footer.php');
}
}
I have it doing fancier stuff, such as setting default values (page title, meta tags) and loading js/css, etc. It works just like a shortcut to having to copy/paste the regular templates that I load but also allows me to define a custom template setup if I need to, unlike if you have it do so automatically be extending your controller class.
I haven't had the need to but you can also specify different functions within this class to load different sections of the site, such as load_admin_pages() or some such. In my case I handle that just by setting a prefix parameter that gets prepended to the file paths and that's gotten what I need for my current project.
Related
I'm currently working on a e-commerce using codeigniter. I'm done with almost all the views and I am starting the coding (controllers and models) now.
For the login, my ideia is set its form on the "top view".
The problem is that I have to call the "top view" in every controller and configure the form_validation everytime I call it.
There is a proper way to dealing with it?
There are a lot of different ways to solve the problem of how to handle commonly recurring html snippets that load on many different pages.
One solution is to add a helper function to each controller like the render_page() function shown below. [NOTE: Any dynamic data that is needed in any of the views to be loaded should be passed via the $data argument.]
protected function render_page($main_view, $data=null){
$this->load->view('top_view', $data);
$this->load->view('menu_view');
if(isset($main_view)){
$this->load->view($main_view);
}
$this->load->view('footer_view');
}
Elsewhere in the same controller
public function index(){
//do stuff including setting $data as needed
$this->render_page('some_view', $data);
}
The problem with this approach is that you need to define render_page() in every controller. To get around this, use the render_page() idea in a custom library (class) that works in a similar fashion. You then autoload that library and use its render_page() method in any controller you want.
If you made such a library and named it Viewmaster then you would use it in a controller as follows (assumes it was autoloaded)
public function index(){
//do stuff including setting $data as needed
$this->viewmaster->render_page("some_view", $data);
}
I'll leave it to you to figure out the implementation details of the Viewmaster class.
In my current implementation of the MVC design pattern (demonstrated using PHP and CodeIgniter):
Let's say I have a "page" located at "www.mysite.com/blue/page" that is ultimately generated by the following (greatly simplified) files:
/libraries
session_lib.php
/controllers
/red
/white
/blue
page.php
/models
/red
/white
/blue
funky_class.php
page_model.php
/views
/red
/white
/blue
page.php
And here's an easy to understand controller:
// FILE: /controllers/blue/page.php
// Get some paramaters from URL
$params = $this->input->get();
// Use a library to do some stuff with these params with a session
$this->load->library('session_lib');
$this->session_lib->store_in_session($params);
// Use a very page specific class to do some funky stuff with the params
$this->load->model('blue/funky_class');
$funkified_params = $this->funky_class->funkify($params);
// Pass params to a model
$this->load->model('blue/page_model');
$data['output_from_db'] = $this->page_model->get_some_output_from_db($funkified_params);
// Send to view
$this->load->view('blue/page', $data);
And now the question...
What is the best procedure for these "funky" little page specific classes that don't interact with the database? In this example I store the little class with the models, and in some cases might just add additional methods inside the model class to do the funky stuff. Is this good practice? Or should the library and funky class both go inside the model so the controller is even skinnier (however the model might be used in other places without the need for sessions and funkiness)?
I would use a helper.
When a helper is loaded, that function will be available in the global scope so you can call it anywhere.
They're great for small, stand-alone functions that do only one thing that is not necessarily related to code in a model or library
Helpers especially excel for me when converting things between one format or another (slugify, convert_timezone, change_query_string) or doing little tasks that you don't want to think about (force_ssl, is_image, uri_string)
funkify() seems like an appropriate helper function that may prevent a lot of repeated code.
Here's the codeigniter documentation page on them: http://ellislab.com/codeigniter/user-guide/general/helpers.html
If you're in a situation where the helper function is so specific it will be only used in one place, you can still use a helper. Name the helper after your controller, page_helper.php for example.
page_helper.php
function funkify($params) {
// do funky stuff
return $funky_params;
}
then in your controller:
class Page extends CI_Controller {
public function __construct() {
parent::__construct();
$this->load->helper('page');
}
public function page() {
// do stuff
$funky_params = funkify($params);
// do more stuff
$this->load->view('blue/page', $data);
}
I have no excuse for it, but sometimes if I am in a situation where I need a razor specific function that will only be used on one location (say, a controller) ever, I will put it right in the controller's file. You can paste a function outside of the class definition and it will act like a helper and be available globally (as long as that controller is loaded). You can also define functions inside of a view. Yucky, but possible. I don't like to do it often because it's unusual and not expected (by myself or other developers)
Trying to determine the best way to handle views in codeigniter. Right now anything I consider seems too messy.
Currently I have 3 relevent views:
1) Header
2) Content
3) footer
Every single controller has something like this. Some controllers even have this several times (different functions in the same controller):
$this->load->view('head', $data);
$this->load->view('volunteers/add_profile.php',$content_data);
$this->load->view('foot');
It seems pretty silly to have to load header and footer on EVERY single page. However, each page will have slightly different data in the header (meta tags, style sheets, loaded scripts, etc).
Is there a cleaner way for me to do this?
Thanks!
I like to create a parent controller with a method like renderPage('content_view', $data). That method can include the header, menu, footer, ... That way, all the view loading stuff is kept in the controller and I don't have to bother with header, menu or footer on every action or view. It's also flexible as your child controllers can redefine the renderPage() method to fit their purposes.
If you need to load multiple content views, you could create a renderPage() method that takes in an array of string instead of a string.
Yes - have a template view. In your controller:
$data['header'] = xxx;
$data['content'] = xxx;
$this->load->view('my_template', $data);
Then in your my_template.php view file:
$this->load->view('head', $header);
$this->load->view('volunteers/add_profile.php',$content);
$this->load->view('foot');
Either what #TheShiftExchange suggested, or, if your application allows it, you can call header and footer views from each content view (which is the only view called from the controller then).
I've created my own controller where I create MY_Controller extends CI_Controller class then in MY_Controller I use access modifier $data and $loadviewArray.
public $data = array();
public $loadviewArray = array();
after this I create function in MY_Controller
public function loadview() {
foreach ($this->loadviewArray as $key => $val) {
$this->load->view($val, $this->data);
}
}
then I create controller Admin and extends MY_Controller like this Admin extends MY_Controller in Admin controller create function index.
public function index() {
$this->data["page_title"] = "Login";
$this->data["records"] = $data; // You can pass data
$this->loadviewArray = array("admin/header", "admin/login", "admin/footer");
$this->loadview();
}
In $data access modifier array I pass data at views and in $loadviewArray load views then call a function for loading views you can do like this also its very helpful for me now. And create header and footer views seperate...
please check https://github.com/alzalabany/codeigniter-base-controller/tree/master
So you can always use a templating lib. yet i dont like them for some reason !
codeigniter allow you to extend its core; if you go to link mentioned above u can see a small example
in this example every controller that will extend MY_controller will start with these defauls
protected $body='base/body',
$title='Codeigniter Z master',//txt
$js=array(),//filename
$inline_js='',//script
$css=array(),
$inline_css='',//style
$breadcrumb=FALSE,//<li><a>
$content=array(),//html
$noEcho = FALSE;
so if u chose to change them in MY_controller its effect will be default, otherwise use $this->title = 'Codeigniter - welcome page'; in ur controller constructor for example;
loading assist is a very easy job just call $this->_assets() and location of asset (edit MY_controller.php default location to you assets folder); if its an inline_js/css just call
$this->_assets('alert("hi");','js');
if you want to load a view into a page section use $this->outv(view_path,view_data,section_name);
and if you want to just load html into a variable u can use
$this->out('Footer','footer');
at end just call ->_flush();
some other options i use like
$noEcho ; if set it will clear all buffer to remove any echo's before it send your view content to browser;
you can also set functions like log-out or log-in inside MY_controller and it will be accessible by any of ur controllers http://localhost/ci/welcome/logout
any way :) i hope that answers your question !
I need to call some library functions in order to correctly display some data in my header view - what is the best/correct way to approach this?
I take it I'm supposed to call this in all my controllers but it seems a bit redundant. Should I take this route? What sort of non 'hacky' method do you suggest?
Edit:
I have a view which outputs the header portion of the page. In this view I have a menu which varies depending whether you're logged in, have any favorites etc. To determine what to display in the menu, I must refer to some libraries, for example, an authentication and favorites library.
I tried calling the library from the view, but it gives me an error.
I suppose I could load the controller and pass the data to the view in every controller but I would have a lot of repetitive code. Is this the way to go?
Yes, manually assigning the same data to a partial view in every controller or method is redundant. You want to avoid this.
Any decent template library should be able to handle this for you. You can google up a solution or write your own.
Here, in mockup code, is what it may resemble:
class Template {
public $data = array();
function area($area_name)
{
$CI =& get_instance();
$data = isset($this->data[$area_name]) : $this->data[$area_name] ? NULL;
$CI->load->view('templates/'.$area_name, $data);
}
function set_data($area_name, $data)
{
$this->data[$area_name] = $data;
}
}
Then in the controller, something like this:
$this->template->set_data('header', $my_data);
Then in the view, something like this:
<header><?php echo $this->template->area('header'); ?></header>
<div><?php echo $this->template->area('content'); ?></div>
<footer><?php echo $this->template->area('footer'); ?></footer>
This is over-simplified, and there are a million ways to handle it, but I definitely suggest writing some kind of class to handle your templates rather than just using $this->load->view() for everything, even if it is just a wrapper for loading views.
To avoid manually setting that same data in every controller, use a MY_Controller, set it in the template class __construct(), or call it directly from the view file from whatever the source is (model, library, etc.). Father MVC may shed a tear, but sometimes this is easiest or even makes the most sense.
Quick example of how to use a controller extension:
// File: application/core/MY_Controller.php
class MY_Controller extends CI_Controller {
public function __construct()
{
parent::__construct();
$data['var_name'] = $this->some_lib->get_data();
$data['var_name2'] = $this->some_model->get_more_data();
$this->template->set_data('header', $data);
}
}
Then, all your controllers would extend MY_Controller rather than CI_Controller and the header data would already be loaded. This is just a simplified glimpse into another topic altogether, much much more is possible with this method.
I tried calling the library from the view, but it gives me an error.
This shouldn't happen if the library is loaded and you are calling it correctly. The syntax is $this->library_name->method().
In any case, I definitely recommend writing or borrowing some kind of Template class.
You don't have to follow the MVC model, but if you want to, the "correct" way is to call the functions in the controller, then pass the data to the view where it's printed out.
You should be able to just autoload the library or else load it in the controller before the view. Then you'll have access to the library methods in your views and can access them the same way you would in your controllers.
I've written a small MVC in PHP5 and desire a pagination module to be added to some files in my views section/folder..
I was wondering.. would the Pagination class be included in the Controller or Models section/folder?
Currently i've included it in the Models folder and called the function when needed..
The way I see it, pagination is a control, allowing user to tell your database (model), which portion of data he or she wants to see.
So I would go with the Controllers module.
Well, I think a better approach would be to make a helpers folder and then load them into your application like this :
function use_helper()
{
static $helpers = array();
foreach (func_get_args() as $helper)
{
if (in_array($helper, $helpers)) continue;
$helper_file = HELPER_PATH.DIRECTORY_SEPARATOR.$helper.'.php';
if (!file_exists($helper_file))
throw new Exception("Helper file '{$helper}' not found!");
include $helper_file;
$helpers[] = $helper;
}
}
Then all you have to do is build a pagination.php file with your Pagination class.
When you need it, you call the function
use_helper('pagination');
From here of course it depends on you Pagination class.
Hope this helps.
i guess the best approach is to call the pagination from the view, referring to this MVC
A view queries the model in order to generate an appropriate user interface
(for example the view lists the shopping cart's contents).
The view gets its own data from the model.
In some implementations, the controller may issue a general instruction to the view to render itself.
In others, the view is automatically notified by the model of changes in state (Observer) that require a screen update.
and because you will be using this class almost in every view, you should make a helper and include this class inside that helper so that all the views can share its methods