views:

782

answers:

4

Sorry about the question title, but I couldn't find a more appropriate way to phrase this.

I am currently building a CakePHP powered website and I'm not quite sure how to approach the following issue. The website looks something like the follwing mockup:

Website Mockup .

The greyed out areas are part of the layout, because their content does not change between views. In the sidebar, I have a collection of ads who are linked to several models. I need controller logic to determine the picture associated with an ad. Also, the ad list needs to be dynamic. Where should I put the logic for building the sidebar?

I've thought about:

  • putting the logic into the AppController (beforeFilter / afterFilter) - the problem is I can't use the controller logic I need (the other controllers inherit from AppController, I'm not sure how to use them there).
  • making a component - is it okay to build components that rely on controllers?
  • replicating the sidebar code in all controllers that render views - this seems kind of stupid to me.

What is the Cake way for this?


Update

After some reading and experimenting, I've gotten to refactoring most of it.

I obtained the best performance by moving the logic for building my ads in the model (eliminating the component that retrieved the pictures) and not using requestAction. It's almost three times faster and the code looks much better.

+3  A: 

I guess the answer is requestAction in case the results are cachable:

http://book.cakephp.org/view/434/requestAction

harpax
Thanks for the idea! I've currently implemented it this way, but the results are not cacheable and there seems to be a lot of overhead involved when calling requestAction.If you have any other ideas please share :).
Alex Ciminian
+2  A: 

I've done something similar for data-driven navigation. I put my logic in AppController::beforeRender and haven't had any problems. I'm not sure I understand your concern related to controller inheritance. I retrieve my menus via:

$menus = $this->NavMenuItem->groupByMenu();
$this->set( compact( 'menus' ) );

I then created an element that renders the menu. It's executed by the layout via:

<?php echo $this->element( 'navigation', array( 'id' => 'secondary', 'menu' => $menus['SECONDARY'] ) ) ?>

If that doesn't help, maybe you can further explain your issue with controller inheritance in a comment.

Rob Wilkerson
Thank you for your answer! I've read your blog post on the subject (found it using Google :), but I couldn't get it to work.The problem is that when I call `$this->Sidebar->build()` from the AppController::beforeRender method, I get an SQL error. It interprets the method name as a query (syntax error near 'build').I've added the sidebar controller to the uses array, as you did with your menu, but still I can't seem to access the controller methods - I guess I'm working with the model object. I assumed that this is an inheritance issue - can't call a child's methods from the parent class.
Alex Ciminian
Is your `AppController` using your Sidebar model? e.g. `public $uses = array( 'Sidebar' );` The syntax error doesn't sound like a SQL thing since "build" isn't part of the SQL, but rather a method call.
Rob Wilkerson
Yep, it is in the $uses arraym I mentioned it in the previous comment. I've attached a screenshot of the code http://i49.tinypic.com/210c41t.jpg and of the error http://i47.tinypic.com/111pdv4.jpg.
Alex Ciminian
So I glossed over your use of a `build()` method. I'm familiar with that method in Rails, but I'm not aware of it existing in CakePHP. Or is it a custom method on your `Sidebarhits` model (or `AppModel`)?
Rob Wilkerson
It's a custom method I'm using in my Sidebarhits _controller_, to get the ads I need.
Alex Ciminian
Would you update your question with the code for that method (including the signature)?
Rob Wilkerson
Sorry for having such a big lag between messages, but I'm currently swamped in things to do, and this is a side-project. Thank you very much for your time. I really want to learn to do this right.Here is the link to the code behing my controller: http://pastebin.com/m59dde486.
Alex Ciminian
I'm confused. In your calling code, the `build()` method appears to be in your model. In the code you've linked to, the method appears to be in the controller. Does that method exist in your `Sidebarhits` model? If so, would you paste that code?
Rob Wilkerson
I've mentioned before that the method is in my *controller*, not in my model. I know that it would have worked if it were in the model, but currently it uses a component (the Picture component), which I need in order to build my output. My question was if there was any way to get it to work _this way_; my assumption was that the answer is no, because SidebarhitsController inherits from AppController and it would be weird to refer to the child class from the parent class.
Alex Ciminian
+1  A: 

It can be done in this way:

  1. Create an element that will help in layout of the Ad Block
  2. Create one or more controller that will generate the data required for rendering of the block
  3. Use requestAction for getting the data out of the models and into the element.

Check the cake book, there is an example of an element where data from Post Model is used to display top/latest 5 posts. Your requirement, I feel, is very similar to it.

Mayank
i agree.. it is best to create an element and then call request action from within the element. this way in the long run you can cache the whole element if need arises(to reduce page load time).
Yash
A: 

Alex,

you're getting a SQL error because the build() function has to be in the Sidebar model, not controller. Also, you don't necessarily need to use $user = array('Sidebar'); you could calling Sidebar in all of your models with this:

$Sidebar = ClassRegistry::init('Sidebar'); and then $Sidebar->find();, $Sidebar->build(); etc.

Or, if you only need to call the build() function from the Sidebar model, you could do this:

  $sidebar = ClassRegistry::init('Sidebar')->build();
  $this->set('sidebar', $sidebar);

Cheers.

Christopher Vrooman