In my current project I need to deal with the Layout more than adding some header, static menu and footer in it. I need to have dynamic menu loaded from the database plus additional blocks which will contain some data related to the main content. For example YouTube.com /and many other sites/ has some blocks which appear all over the site like related movies block from the right depending from the tags related to the main movie. There are a lot examples.
Well, searching in the CakePHP Manual, CakePHP Google Group or Bakery doesn’t satisfy my understanding how to solve the case. Apart from $anything_for_layout: Making HTML from the View available to the layout article I didnt find anything on this topic. And this one sounds like a hack rather than a complex solution.
Then I decided to investigate my self the situation and here is my solution:
Let’s go through complete example. Let’s imagine that we have to load a dynamic menu from the database in all pages. Menu is in the database.
Before starting with the example I want to resume the process:
1. Component fetch the data from the database
2. in the beforeRenter() function in the AppController the component provide the fetched data to the $this->data variable
3. In the helper the specified node in the $this->data variable is manipulated in order to create a valid html block /or whatever it’s required/
4. Including the helper in the default layout or other one will provide the desired functionality on the site
That’s it. It’s nothing more by wrapping the data with helper so it would be easy to be changed.
Ok, let’s continue with first step – to create a menu component which will fetch the data:
//Get the menu from the database
class MenuComponent extends Object {
function fetchData(&$controller){
//Dynamically Loading the model of the menu, because it's not necessary to have it on every controller.
App::import('Model', 'Menu');
$this->Menu = new Menu();
return $this->Menu->find('all');
}
}
?>
The component just fetch all data from the Menu table and return it as array. It’s possible to skip this step and to get the data directly with a function in a Controller, but I think this way is more clear.
Once we have this component we need to include it in our application. If we want to be loaded for all controllers the best place is app_controller.php
So, what we add in the AppController class? The callback beforeRender() is executed always after all Controller’s actions. This small trick allow us to put extra node in the $this->data variable. For our example this node is called “Menu”, but you should set name which wont be in a conflict with other variables /as example I could mention if there is interface for editing the Menu/, so I would suggest array node with name something like ‘MenuBlock’ or similar, but for this example it’s not needed. Let’s keep it simple.
We have the data from the database and it is associated properly to $this->data array. Let’s make the Helper:
Basically what this helper do is to extract the name and create a simple unsorted list.
Now whats left is to add this in the layout. Layouts are in app/views/layouts directory. The default one is called default.ctp. If you don’t have default one in that directory, you can get a copy from cake/libs/view/templates/layouts/default.ctp, put it in the app/views/layouts and modify it by adding your code and functionality. Once you have it then just add the following code in it in the proper position.
<head></head>
<body>
...
<?php
//Check if the Helper is loaded in the memory /if it's not, then loading it/
App::import('Helper', 'Menu');
//Create instance of the Helper class
$Menu = new MenuHelper();
echo $Menu->display($this->data);?>
...
<?php echo $content_for_layout;?>
</body>
</html>
That’s it, now this menu list will be populated in all pages.
Pingback: links for 2007-10-11 « Richard@Home
I’m coming from Rails and was really missing the content_for method, I still do, but this is a great workaround, thanks for sharing.
Thanks! It really works.
There’s just one issue in the helper: the $ret variable is not $ret .= ”… but $ret = ”…
I think nice tutorial but unfortunalty it not working for its shows undefined variable ; i can’t figureout the exact problem anybody help me why this shows undefined varialbe how we can rectify this ,thanks in adavnce
@All – thanks for your comments!
DannyC – you are completely right, with .= it will take wrong string and it will become mess. I will fix in the article right now.
@vipin – I just want to warn you that the example is writen for CakePHP 1.2, and as DannyC said – it’s working, but there is no guarantee that it would work in v1.1
loadModel is deprecated. Please use App::import instead.
Another question, can you share abit on what does your database look like to use this code? Is it just menu table with id, name, and pages?
@conankun – true, loadModel() is deprecated, but the article was written before the deprecation, so I will leave as it is.
About db model this example is made with one table with 2 fields in it – id and name. This is because I simplify the example as much as possible.
Hi,
I keep getting a
Fatal error: Class ‘Menu’ not found in /var/www/cake/app/controllers/components/calendar.php on line 8
What is wrong, Version 1.2.0.6311 beta
@Stacey Kingwill – I think you need to have create a model named Menu. because as far I can see look like there is no such class. This article is just prove of concept and is not tend to be full working demo.
i’m having trouble implementing this. i think i’ve got everything set except i cannot get the line of code in the layout to work. i can’t figure out how i can have access to the helper within the layout. any help would be appreciated.
Apologize to all guys commented here. I miss the part which explain how to get instance of the helper in layout.
Just small clarification: Properly because of sequence of the class inheritance, but it’s not possible to load helper in the usual way in AppController class. The $helpers variable get overwritten from the default helpers which are Html and Form.
there are 2 approaches to sort this out:
1. You need to add desired helper in every controller /but this is time consuming and not convenient/
2. The approach which I just added in the post.
So, hopefully this issue finally get sorted.
To help those that find this article, but can’t quite get it working. It think that they deprecated the ‘App::…’ part. Replace these with loadModel(‘NameofMenu’). And things will be good.
Valerie, you are totally wrong π it’s upside down – loadModel() is the deprecated one and the App::import is the new way to load different classes in the memory. In fact it was written with loadModel in the beginning, but I change this to meet new convention in cake.
Why not just use “elements”?
Feed them a few variables and away you go.
-Ben
Hi…
great job, thanks for sharing.
[]’s
—
Luiz
Pingback: Signets remarquables du 29/09/2008 au 02/10/2008 | Cherry on the...
Hi,
thank you for your job. You have a small mistake in your script in the MenuComponent:
return $this->Menu->find(‘all’);
You have to write all in lower-case charakter.
Regards Thomas
Although it’s a concept, rather than complete example, you are absolutely right.
Thanks Thomas
Pingback: pshhqvmi
Pingback: xldztoek
I couldn’t express my thank to you with my English.
It’s a great work you’ve done here. It took me out of the endless sleepless-night.
Thank you very much.
Hi!
It seems i can’t get this working, i’m beginner with cake and think it is only something i have done wrong.
When i add the
function beforeRender()
{
$this->data = am($this->data, array(‘Menu’=>$this->Menu->fetchData(&$this)));
}
to my app/app_controller.php i only get a blank webpage
my db name is menus and i have the id and name field in it.
Hope someone has any help for me with this.
Would be very happy for all help i can get
Regards
// Mify
try to replace the row:
with:
This way you skipping the component part.
How would you add / retreive other fields to this (nameley in the Set::extract method?) – such as a link field?
Peter, I didn’t understood what you asking π could you clarify?
my menu table has 2 columns – name and link.
How would you extract both of these using your method above?
@Nik Chankov
Nik,
This causes problems on mysetup when passing data to and from edit methods of controllers, e.g. if you were editing a menu using your example above this data will be overwritten when passed through to the edit method?
Peter, it should not if you using $this->data[‘show_menu’] or some unique string which doesn’t match Menu controller.
In fact if you have Menu form and you are using my method – yes it will be a problem. But my example is proov of concept. I would use PublicMenu and AdminMenu as $this->data nodes instead of Menu only.
Right – I get you π
With regards to my previous comment I worked it out – it was the old notation that threw me somewhat as I am used to XPath.
//This extracting only the name of the menu from the multidimentional array.
//$contents = Set::extract('/User/username', $data['User']);
$avatars = Set::extract('/UserProfile/avatar', $layoutData['UserProfile']);
$usernames = Set::extract('/User/username', $layoutData['UserProfile']);
$ret = "";
for($i; $i<=count($usernames); $i++) {
$ret.="".$usernames[$i]."";
}
$ret .= "";
return $ret;
}
Yoy are right, now extract has different syntax. I will change it in order to fit with the new one. Thanks
ok cool – how do you post code on your blog? that looks rubbish above lol
just noticed the date on this blog entry! good work π
Pingback: Widgets et contenu modulaire avec CakePHP - Pierre MARTIN
had problems uploading png’s too. you can edit line 302 from:
imagepng($newImage, $dstimg, $quality);
to:
imagepng($newImage, $dstimg, 9);
I think using components with requestAction() is much more better π
true MVC style FTW π
Pingback: How to deal with Layouts in CakePHP | Vietnam Online Marketing Addict | Nganhtuan.com
what is the database structure ???