Getting Started With CodeIgniter – 5

One Controller Per View in CodeIgniter

So how do you break a website into views and controllers exactly? If you read the CodeIgniter forums people have devised lots of different ways of tackling the controller problem.

For me, taking the project I am currently working on, I find it easy enough to think in terms of objects for the tours, the shopping cart, a customer and so on. But the static web pages – the views that end up being your about, contact me, terms and conditions and privacy pages – how do you break down the controllers for those?

This is an especially testing question because you are likely to hit it pretty quickly when you decide to learn CodeIgniter and the MVC approach at the same time. You’re likely after all to want to start with simple pages.

First I thought – I’ll do one controller per view no matter how straightforward the page content. Then as soon as I had decided that, doubts crept in. No way! That’s absurd. How can you have one controller for the about page, another for the company history page and yet another for the terms and conditions page. They’d be practically the same!

Changed My Mind – One Catchall Controller for All Static Views

So I set about creating a common controller that would control all static pages. By static, I mean pages that only contained text that would rarely change.

Hmm – what was I going to call it – maybe the content controller (content.php), or maybe the static controller (static.php)? I was having trouble finding a good name for this catchall controller. This seemed to me like a sign I was on the wrong track.

The next thought that came to mind was – well hang on, I might want to add something data flavoured to the About page one day – I don’t know – it might be a feed of the client’s Twitter tweets. What then, the about page would suddenly no longer be hopelessly static and if I’d had the foresight to create one controller per page at the start, I wouldn’t have to change the general content controller for all static pages, just because one needed more.

Back to One Controller Per View

I am now back to the one page per controller idea. This meant however that each controller for each static page, necessarily contained a lot of repeated code. I hunted through the CodeIgniter forums for the best way to address this, and the answer appeared to be to use a helper.

I say ‘appeared’ as the CI forums can get very technical and sometimes, I am not not sure what they are all talking about. Hence the need to write this stuff on my blog I suppose.

Here’s a list of forum posts that I found useful whilst getting my head around this.

Using A Helper for Common Controller Code

If you decide to use one controller per view, you can cut down on repeated code by putting the common controller set up code (data loads etc) into a specially written helper. I wrote one called commoncontrol_helper.php and put it in the application/helpers directory. My helper looks like this, and contains (currently) one function called setup_assets. This follows on from my previous posts in this series.

[php]
<?php

function setup_assets($theobject){
$theobject->load->model(‘systemdata’);
$theobject->load->database();
$theobject->data[‘css’] = $theobject->systemdata->get_css_filespec();
$theobject->data[‘pdf’] = $theobject->systemdata->get_pdf_filespec();
$theobject->data[‘cssIE6’] = $theobject->systemdata->get_cssie6_filespec();
$theobject->data[‘cssIE7’] = $theobject->systemdata->get_cssie7_filespec();
$theobject->data[‘scripts’] = $theobject->systemdata->get_scripts_filespec();
$theobject->data[‘xml’] = $theobject->systemdata->get_xml_filespec();
$theobject->data[‘flash’] = $theobject->systemdata->get_flash_filespec();

$theobject->data[‘base_url’] = $theobject->systemdata->get_base_url();
$theobject->data[‘site_images’] = $theobject->systemdata->get_site_images_filespec();
$theobject->data[‘images’] = $theobject->systemdata->get_images_filespec();
$theobject->data[‘public_status’] = $theobject->systemdata->get_public_status();
$theobject->data[‘member_status’] = $theobject->systemdata->get_member_status();
$theobject->data[‘page_name’] = $theobject->systemdata->get_page_name();
$theobject->data[‘currentyear’] = $theobject->systemdata->selectcurrentyear();
}
?>
[/php]

And consequently, a controller that uses this helper will look like this :

[php]
<?php
class Privacy
extends Controller {
var $data;

function Privacy(){
parent::Controller();
$this->load->helper(‘url’);
$this->load->helper(‘commoncontrol’);
setup_assets($this);
}

function index(){
$this->load->view(‘publicuser/viewprivacy’, $this->data);
}

}
?>
[/php]

Comments

  1. Liz Jamieson says:

    Hi Rebecca
    I wrote these posts for the purpose of encouraging (and hopefully not leading astray), other people like me who are learning CI and MVC simultaneously and on their own. In the next post, I’ve extended the controller as suggested by Noah.

  2. Rebecca Skinner says:

    Just stumbled across this blog post series while searching for answers to the exact same problem – just starting out with CodeIgniter, very little MVC knowledge, lots of static pages to build, a few dynamic ones, what to do about Controllers…

    I feel a lot more enlightened now, especially reading Noah’s comment about extending the base Controller, that seems like a great way to go 🙂

  3. Liz Jamieson says:

    Noah – I’ve made the changes as you suggested. I completely misunderstood what you meant by using the MY_Controller technique to *extend* the default controller class. I somehow mistook that MY_ process as how you *re-write* the controller class. Which of course it isn’t. You are quite right about the examples and docs. I will write up another post describing your solution, which now works for me later today if I can. Thanks again.

  4. Liz — I’m glad to chime in. I’ve been convinced that the folk wisdom archived on blogs is pretty powerful. The more we discuss in the open, the better off we all are.

    Something I think is lacking around all these MVC frameworks is novel strategies and patterns for structuring your app. The samples are usually trivial and don’t get to any number of the moderately tough problems likely to be the first you hit in an app. I think the model and controller per table approach in many examples is particularly brainwashing.

    Anyway… The danger is when you start changing a lot of behavior of a base component or replacing it entirely. In this case, the change is simple, so you would probably choose the path that feels the best — it’s not going to be hard to change gears later either way. If you do that asset setup the same on every page, you should really consider putting it in your overall base Controller, whether using MY_ or a standalone library (CI gurus, please chime in.)

    However you structure it, you have a set of tools for avoiding code duplication. Here, it seems like you have commonality across a large section, where it’s likely you’d want all of it to change together. When you “pull up” this functionality via inheritance so it’s automatic, you reduce your potential maintenance costs. If there was a lot of variability, or strong likelihood of change in that behavior between controllers, a different approach might be better — inheritance could end up giving you combinations of behavior that you would have to work against.

  5. Liz Jamieson says:

    Noah – thanks for your input. I’ve tried to see why this is better, but my lack of experience with MVC is a real hindrance to this. I like the idea of creating a new class that extends the controller class. I think I could do that? But I don’t want to mess with the core classes by using the MY_ extension because in the user guide it suggests you shouldn’t unless you know what your are doing. At this stage I don’t.

    Perhaps I could write a class that extends the controller class and place my common code in that. Would that be a step in the right direction away from like you say, scope-free helper functions? Is that what you are describing in your opening sentence?

    Your post is valuable as it also helps other beginners who may be able to take your ideas on board more quickly than I can! Thanks once again.

  6. Something you may consider is a base controller for all of your “static” pages. You can bake in some of your own convention and leave an easy break/migration path if a page needs to get complex.

    I prefer to use object-oriented models where I can. The CI helpers coming in as unscoped methods scares me just a bit. I’d try making a new (abstract) class in a library file (maybe StaticController, ContentController, BasicController — something…) and drop it in autoload.

    If that setup is truly global, I’d consider making a MY_Controller class in application/libraries that extends CI_Controller (see the user guide for extending core classes). You’d want to then change your existing controllers to extend MY_Controller, instead. This, presumably, would be eliminating other duplications, and so, would be valuable.

    In the basic/static/content controller, I’d include a render() method and an index() method. The default implementation of index() would just call $this->render(). The render() method would use get_class($this) to find the name of the controller (probably lowercased), and load a conventionally named view.

    To put this directly into code…


    //application/libraries/MY_Controller.php
    class MY_Controller extends Controller {
    function __construct() {
    parent::_construct();
    $this->setup_assets();
    }

    function setup_assets() { /* ... */ }
    }

    //application/libraries/ContentController.php
    class ContentController extends MY_Controller {
    function index() {
    $this->_render();
    }

    function _render() {
    if (get_class() !== get_class($this)) {
    $this->load->view(strtolower(get_class($this)));
    }
    }
    }

    //application/controllers/about.php
    class About extends ContentController {
    //nothing beyond existence needed here to load the 'about' view
    }

    //application/controllers/history.php
    class History extends ContentController {
    function index() {
    //check for some history-specific permission and redirect on failure
    $this->_render();
    }
    }

    This setup should give you a clean path to customize the needs lightly (at constructor time, with a new action, or just tuning index), while remaining very “automatic”. This automatic behavior is entirely opt-in even if you extend ContentController. (Spring folks may disagree with this a bit, since it’s not quite open-closed…)

    Another thought is that you could push this behavior up to MY_Controller as the base starting point for all of your controllers. Unless you do something else with index(), you’re going to load the view of the same name. I might actually like that a little better…

  7. Liz Jamieson says:

    Thank you Jenny. I needed to do something. By the way – I am still laughing about your first vlogg. I showed it to my husband and he showed it to his friends at work. They all think you are very funny. Any news from LA yet?

  8. Alright. I have no idea about this Code Igniter-thing, but I do think you have spruced your home here up a bit… yes?

    Very nice!