Welcome Guest, Not a member yet? Register   Sign In
Setting up a dynamic menu in CI
#1

[eluser]Ben Edmunds[/eluser]
First off, hello to everyone here. I am looking into CodeIgniter and I really like what I see.

There is something I am trying to figure out how to do with CodeIgniter and I need some help.

How would I setup a layout file with a header, footer, dynamic menu, and the normal view content?

For example:

Code:
<html>
<head>
<!--scripts go here-->
</head>
<body>
<div id='banner'></div>
<div id='menu'>&lt;?php $this->layout()->menu ?&gt;</div>
<div id='container'>&lt;?php $this->layout()->content ?&gt;</div>
<div id='ads'>&lt;?php $this->layout()->other ?&gt;
<div id='footer'>blah</div>
&lt;/body&gt;
&lt;/html&gt;

I do not want to break the MVC pattern though. So I want to have a controller for the menu that access a model and then a menu view to display the menu.

This is possible in Zend but the method is pretty bad IMHO and symfony can do this perfectly with components but I don't see anything comparable in CI? Symfony seems really bloated and slow, plus I don't like the ORMs so I don't want to use Symfony.

I saw the nested views in the FAQ but I still don't see how to accomplish it in an MVC pattern...

I think Symfony style components are the best way I've see of how to accomplish this but maybe there is something better?

How do you guys do this on your sites? Surely you don't put your menu into each controller, and do you keep the MVC pattern for your menu and other dynamic content that must be on every page?


Thank you in advance,
-Ben
#2

[eluser]Derek Allard[/eluser]
Just to restate, you want a way to have a common header, menu and footer across every page, but the "body" or content of the page to be different?

There are a few different ways to do this, but the easiest way is probably to call a view from your view
Code:
&lt;?php
$this->load->view('header');
$this->load->view('menu');
?&gt;

... content of each page goes here...

&lt;?php
$this->load->view('footer');
?&gt;
#3

[eluser]wiredesignz[/eluser]
@benedmunds, You might also look into using Modules or Widgets to achieve the separation you require. (See my signature)
#4

[eluser]Ben Edmunds[/eluser]
That is what I want to do but how do I have the menu dynamically pulled from the database with a model, view, and controller?

I also don't want to include the menu in each controller. I only want to do it once, DRY.


Thank you,
#5

[eluser]wowdezign[/eluser]
Hi Ben,

I normally use a layout - or template - file in my projects. The way I do it seems to work ok for me.

Basically, I use variables in the controller to hold view filenames and then pass the variables into my template view, I'll explain it using the content area as an example, but you could do it with a menu, table, or sidebar too.

1) I create a view of how the page is suppose to look and name it template.php
Code:
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;My HTML Template File&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        <div id="headerDiv">SomeSite.com</div>
        <div id="navDiv">
            <ul>
             <li>Nav Item</li>
             <li>Nav Item</li>
             <li>Nav Item</li>
             <li>Nav Item</li>
             <li>Nav Item</li>
            </ul>
        </div>
        <div id="contentDiv">

            
            // Use a variable to hold the name of the file you wish to load
            &lt;?php $this->load->view($contentView); ?&gt;


        </div>
        <div id="footerDiv">&copy; 2009 | All rights reserved.</div>
    &lt;/body&gt;
&lt;/html&gt;


Notice in the code above, instead of loading in a file name, I load in the contents of a variable called "$contentView".


2) In the controller, when I am setting up my data array, I set that variable based on what I want to load like so:

Code:
class SomeController extends Controller{
    
    function SomeController(){
        parent::Controller();
    }
    
    function index(){
        $this->load->model('Page'); // Or whatever model you use to retrieve content
        if($this->Page->getPageType() == 'products'){

            // Name of view file I want to display in the view
            $data['contentView'] = "product-list";

            $data['content'] = $this->Page->getProductsList();
        }else{

            // Name of view file I want to display in the view            
            $data['contentView'] = "standard"; // If it's a regular content page

            $data['content'] = $this->Page->getStandardPageText();
        }
        $this->load->view('template',$data); // Pass in the data array
    }
}

The above example is oversimplified to avoid having to write more lines of code. As you can see, though, when I set the "$data['contentView']" string variable in the controller, it is available in the view.

In the view, you just load it with &lt;?php $this->load->view($contentView); ?&gt;. Te view loads the file with the name that matches whatever string it read from the "contentView" variable.

I use this way of templating because it seems simple to me, I am sure there are other ways of doing it, but I haven't experimented with any others.

I hope this helps.
#6

[eluser]wowdezign[/eluser]
[quote author="benedmunds" date="1248627603"]That is what I want to do but how do I have the menu dynamically pulled from the database with a model, view, and controller?
[/quote]


There many ways to do it, but you could create a menu model:


Code:
class Menus extends Model{

    function Menus(){
        parent::Model();
    }

    function getMainMenu(){

        // Code to get menu data where menuType = main

    }


    function getBottomMenu(){

        // Code to get menu data where menuType = bottom

    }


    function getSubMenu(){

        // Code to get menu data where menuType = sub

    }

}


Then in your controller, you place the menu data you want in variables with something like:

Code:
$data['mainMenuArray'] = $this->Menu->getMainMenu();
$data['btmMenuArray'] = $this->Menu->getBottomMenu();

And pass them into your view like I mentioned previously.

Then in the view you can:

Code:
<ul id="mainMenuDiv">


    &lt;?php
        foreach($mainMenuArray as $item){
            echo"<li><a href='#'>$item->link_text</a></li>";
        }
    ?&gt;


</ul>
#7

[eluser]Ben Edmunds[/eluser]
Thanks for the help wowdesign. That really helped me understand how to do it. The only thing that really sucks is that then I have to pull the menus in every controller... I guess I don't have a choice though.

Is there something like a $this->load->controller() that I could use in the layout.php file?


Thank you again.
#8

[eluser]Dam1an[/eluser]
If there's something you need to happen in every controller, you can extend the Controller class with a MY_Controller and make your application controllers extend this, you can then put stuff you want to happen every request in MY_Controllers constructor, see here for more on extending core classes
#9

[eluser]qwertyqwerty[/eluser]
I wouldn't have thought to extend the Controller class and that is an interesting way of doing it and it would certainly work.

For the example of your main menu, I've created a helper function called 'main_menu()' and all it (basically) does is the following:

- Loads the Page model
- Calls the 'getMainMenu()' method from the page model which returns an associative array.
- Passes the returned associative array to Code Igniter's ul() helper function to generate the unordered list
- Returns the HTML (Important, it does not echo it)

Then in your template, you just put the following to spit out your main menu

Code:
&lt;?php echo main_menu(); ?&gt;


For all I know, this could be really bad practice in terms of correctly utilizing the MVC pattern, but it's the easiest and cleanest way I've found so far.

Note:
You'll need to specify how deep you want the traversal to go.
You'll need to indicate for each of your pages whether you want them to display or not as sometimes, you'll have pages at the root level (e.g. privacy) that you won't want showing up in the main menu.




Theme © iAndrew 2016 - Forum software by © MyBB