开发者

Codeigniter: Best way to structure partial views

How would you structure the below page in Codeigniter?

Codeigniter: Best way to structure partial views

I thought开发者_开发百科 about creating seperate controllers for each section

  1. Left nav
  2. Content nav
  3. Login name
  4. Leaderboard

Excluding the content section (as this changes depending on the link on the left nav and content nav used as a kinda sub-menu). All the other sections remain roughly the same

I thought about doing:

Class User_Profile extends Controller
{

    function index()
    {
        $this->load_controller('Left_Nav');
        $this->load_controller('Content_Nav');
        $this->load_controller('Login_Name');
        $this->load_controller('Leaderboard', 'Board');

        $this->Left_Nav->index(array('highlight_selected_page' => 'blah'));

        $this->load('User');

        $content_data = $this->User->get_profile_details();

        $this->view->load('content', $content_data);

        $this->Login_Name->index();
        $this->Board->index();
    }

}

Obviously this load_controller does not exist but this functionaility would be useful. The controller for each section gets the data required from the model and then loads the page through $this->view->load()

It could be a headache to have this code in all the left nav links like News, Users, About Us, etc.. But then again not every nav link has all those sections so I need that flexability of having the sections as a "partial view"

Can anyone suggest a better way of doing this?


@Reinis answer probably hit the spot correctly for older versions of CI less than 2.0 however alot has changed since then, so I thought I'd answer this question with an up to date method of what I've done.

Most of it is similar to @Reinis method and also described here:http://codeigniter.com/wiki/MY_Controller_-_how_to_extend_the_CI_Controller

However here are the updates ive done:

Step 1: Create a MY_Controller.php file and store it in /application/core

Step 2: In your MY_Controller.php file put in the following contents:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class MY_Controller extends CI_Controller {

    function __construct()
    {
        parent::__construct();
    }

    function _output($content)
    {
        // Load the base template with output content available as $content
        $data['content'] = &$content;
        echo($this->load->view('base', $data, true));
    }

}

Step 3: Create a sample controller to base off of MY_Controller.php, in this case I will create a welcome.php controller inside of application/controllers/ with the following content:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Welcome extends MY_Controller {

    function __construct()
    {
        parent::__construct();
    }

    public function index()
    {
        $this->load->view('welcome_message');
    }

}

Once you have these controllers set, do the following:

Step 4: Create a base view inside of /application/views and name the file base.php, the content of the file should be similar to this:

<!DOCTYPE html>
<!--[if IE 7 ]><html lang="en" class="ie7"><![endif]-->
<!--[if IE 8 ]><html lang="en" class="ie8"><![endif]-->
<!--[if gt IE 8]><!--><html lang="en"><!--<![endif]-->
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <title></title> 
        <link rel="stylesheet" href="<?php echo base_url(); ?>stylesheets/reset.css" media="screen" />
    </head>
    <body>
        <div id="section_main">
            <div id="content">
                <?php echo $content; ?>
            </div>
        </div>
        <?php $this->load->view('shared/scripts.php'); ?>
        </div>
    </body>
</html>

Step 5: Create another view in /application/views and name this view welcome_message.php, the content of this file will be:

<h1>Welcome</h1>

Once, all this is complete, you should see the following output:

<!DOCTYPE html>
<!--[if IE 7 ]><html lang="en" class="ie7"><![endif]-->
<!--[if IE 8 ]><html lang="en" class="ie8"><![endif]-->
<!--[if gt IE 8]><!--><html lang="en"><!--<![endif]-->
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <title></title> 
        <link rel="stylesheet" href="http://somedomain.local/stylesheets/reset.css" media="screen" />
    </head>
    <body>
        <!-- BEGIN: section_main -->
        <div id="section_main">
            <div id="content">
                <h1>Welcome</h1>
            </div>
        </div>
        <!-- END: section_main -->
        <script src="/path/to/js.js"></script>
        </div>
    </body>
</html>

As you can see <h1>Welcome</h1> was put into the base template.

Resources:

  • Obviously @Reinis initial response
  • http://codeigniter.com/wiki/Extending_Controller_with_masterpage_template_funtionality

Hope this helps anyone else coming across this technique.


I can't vouch that this is the best approach, but I create a base controller like this:

class MY_Controller extends CI_Controller {

    public $title = '';
    // The template will use this to include default.css by default
    public $styles = array('default');

    function _output($content)
    {
        // Load the base template with output content available as $content
        $data['content'] = &$content;
        $this->load->view('base', $data);
    }

}

The view called 'base' is a template (a view that includes other views):

<?php echo doctype(); ?>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <?php $this->load->view('meta'); ?>
    </head>
    <body>
        <div id="wrapper">
            <?php $this->load->view('header'); ?>

            <div id="content">
                <?php echo $content; ?>
            </div>

            <?php $this->load->view('footer'); ?>
        </div>
    </body>
</html>

What this achieves is that every controller wraps its output in the base template, and that views have valid HTML instead of opening tags in one view and closing in another. If I'd like a specific controller to use a different or no template, I could just override the magic _output() method.

An actual controller would look like this:

class Home extends MY_Controller {

    // Override the title
    public $title = 'Home';

    function __construct()
    {
        // Append a stylesheet (home.css) to the defaults
        $this->styles[] = 'home';
    }

    function index()
    {
        // The output of this view will be wrapped in the base template
        $this->load->view('home');
    }
}

Then I could use its properties in my views like this (this is the 'meta' view that populates the <head> element):

echo "<title>{$this->title}</title>";
foreach ($this->styles as $url)
    echo link_tag("styles/$url.css");

I like my approach because it respects the DRY principle and the header, footer and other elements get included just once in the code.


My Template library can handle all of this. You create a single (or multiple) layout file(s) that contain the partials and a tag for where the main body content will go.

Syntax as simple as:

// Set the layout: defaults to "layout" in application/views/layout.php
$this->template->set_layout('whatever') 

// Load application/views/partials/viewname as a partial
$this->template->set_partial('partialname', 'partials/viewname');

// Call the main view: application/views/bodyviewname
$this->template->build('bodyviewname', $data); 

Simples right?

Put some of that into MY_Controller and its even easier.


have you considered templates? There are many decent ones available with a little searching - check the CI wiki.

templates do more or less exactly what you are after. You define one master template and "sections", and these are loaded for you each time

don't want to plug too much so this might get you started - template libraries in CI


I would make a MY_Controller to take care of all this. You can top it with a layout(template)/navigation library to generate all the layouts, navigation, showing/highlighting selected menu item, loading views etc.

I'd say it's not the right way to do it, if you're using a controller for each page section. You can to make use of views and nested views for that.


I like what Phil Sturgeon mentioned. Although it's considered as very complicated i really liked template structure that magento have.

Inspired with that way of structuring i made my logic, (which is not great at all but it's simple as it can be,. and perhaps maybe i could override ->view loader and make it accept some kind of object as a template name and than load the structure as required)

first: This approach must be used very responsibly (you mus prepare data in controller/method that your templates require!

second: Template needs to be prepared and structured properly.

This is what i do:

  • in every controller i have attribute of Array type, something like this:

    class Main extends CI_Controller {
    
    public $view = Array(
            'theend' => 'frontend',
            'layout' => '1column',
            'mainbar' => array('content','next template file loaded under'),
            'sidebar' => array('generic','next template file loaded under'),
            'content' => '',
    );
    
  • In every method for which i want to use previous structure, and if i want to change it a bit i write it like this:

    public function index()
    {
    $data['view'] = $this->view;  // i take/load global class's attribute
    $data['view']['mainbar'] = Array('archive','related_posts'); // i change mainbar part of it
    // i add/load data that i need in all those templates that are needed $data['view'] also my using same Array  $data['my_required_data_that_i_use_in_template_files'] = 1;
    $this->load->view('main',$data); //
    }
    

third In /application/view folder i have structure like

/view/main.php <-- which basically just determines which side's wrapper of web to load (frontend or backend or some other)

/view/frontend/wrapper.php

/view/backend/wrapper.php

/view/mobile/wrapper.php   <-- this wrappers are again another level of structuring for ex:

/view/backend/layouts/   <-- inside i have templates different layouts like 1column.php 2columns-left (have left side is narrow one),2columns-right,3columns... etc...

/view/backend/mainbar/   <-- inside i have templates for mainbar in pages

/view/backend/mainbar/.../ <-- in the same way it's possible to add folders for easily grouping templates for example for posts so you add for example

    /view/backend/mainbar/posts/  <-- all templates for creating, editing etc posts... 

    /view/backend/sidebar/   <-- inside i have templates for sidebar in pages

    /view/backend/...other special cases.... like dashboard.php

forth file in /app/view/main.php looks something like:

if ($view['theend'] == "frontend")
{
$this->load->view('/frontend/wrapper');
} elseif ($view['theend'] == "backend")
{
$this->load->view('/backend/wrapper');
}

fifth wrapper is simple some php in structured HTML where you have head (loading html headers, title etc...) header/headers (loading in headers if there are any in passed $data['view']['headers'] variable/array) layout (loads in layout file that simply have new html structured file with next level of loading files) footer/footers (loading in footers if there are any in passed $data['view']['footers'] variable) scripts (loading inscripts like analytics/facebook scripts just before tag)

sixth So in the same way, layout would be also loading in mainbar/sidebar content that is specified in public $view = Array(....)

If i need in some method, i simply override part of public $view = Array(...) attribute, and i override just part that is different.

it's done something like this:

public function index()
{
    $data['view'] = $this->view;  // i take/load global class's attribute
    $data['view']['mainbar'] = Array('archive','related_posts'); // i change mainbar part of it
// i add/load data that i need in all those templates that are needed $data['view'] also my using same Array  $data['my_required_data_that_i_use_in_template_files'] = 1;
    $this->load->view('main',$data); //
}

FINALLY Load goes like this:

  1. $this->load->view('main',$data); <-- Loads /app/view/main.php and passes $data $data has node 'view' ($data['view']) and in it it has sub nodes which determines other important things like: what is the end, what layout, what headers, what footers etc...

  2. Using defined data in $data['view']['theend'] it loads proper wrapper

  3. Again using data in $data['view']['layout'] further in wrapper it loads other, deeper structures like layout...
  4. layout, uses same $data['view']['mainbar'], $data['view']['sidebar'] and catch other important parts to load like mainbar templates, sidebar templates ...

That is about it...

p.s. I'm so sorry for not using numbers, but stackoverflow system is so strange that instead of showing 3. it shows me 1.. as you see i had some nested lists...


What I've done (in Kohana 2) is have the 1 template with all the sub sections (like left menu, top header), and a single controller that populates the variables that will be replaced in the template.

Then, the variables for every sub-section can be generated by functions called in the controller itself. You can also have those functions be in a separate controller class's constructor, with every controller of yours extending that one so they're automatically ran and set as class variables for easy access.

For slightly nicer templates you can have the subsections in separate files, and the big template include them:

<?php include 'leftMenu.php'; ?>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜