Define your own “for_layout” variables

Published on and tagged with cakephp  helper  layout  view

CakePHP comes with three “for_layout” variables: $title_for_layout, $content_for_layout, and $scripts_for_layout (since 1.2). As the names imply, those variables are used within the layout as “placeholders” for some data.

The simplest way to use your own “for_layout” variable is to set a variable in your controller or view:

$this->set('var_for_layout', 'value');

You can then use this variable in your layout with:

if (isset($var_for_layout)) {
     echo $var_for_layout;
}

The “problem” of this solution is that you need an isset() check to avoid an “undefined variable” error when the variable is not set. This check is not necessary for the built-in “for_layout” variables, and so it doesn’t look very consistent if you mix those variables with your own “for_layout” variables.

To avoid this problem I wrote a simple helper, which automatically sets an empty string if the variable is not set.

class DemoHelper extends AppHelper {
    var $value = '';

    function afterRender() {
        $view = ClassRegistry::getObject('view');
        $view->set('var_for_layout', $this->value);
    }

    function setValue($value) {
        $this->value = $value;
    }
}

This helper is then included in the $helpers array of my AppController (app/app_controller.php):

var $helpers = array('Demo');

With that I can access the helper in all my views. To set the value for the $var_for_layout variable I have to use the helper instead of $this->set():

$demo->setValue('value');

In my layout I can now simply output my “for_layout” variable:

echo $var_for_layout;

17 comments baked

  • MiB

    Hello,

    wouldn`t be easier, to set necessary variables in beforeFilter() function, and then overwrite them in actions?

    // appController
    class appController extends Controller {
    function beforeFilter() {
    $this->set(‘my_var_for_layout’,”);
    }
    }
    // controller
    class anyController extends appController {
    function first_action() {
    $this->set(‘my_var_for_layout’,’hello’);
    }
    function other_action() {
    }
    }
    // view
    e($var_for_layout)

    No matter which action will you trigger, you never get PHP Notice when calling $var_for_layout.

    Regards

  • cakebaker

    @MiB: Yes, your solution is in certain situations easier. But if you need some additional logic, then I think the approach shown in the article is more flexible.

    For example, in the helper from which I derived the code shown above I do some string concatenations in the afterRender function when the data was set like: $view->set(‘var_for_layout’, ‘This is value1:’.$this->value1.’, and this is value2:’.$this->value2);

  • Dieter@be

    Hey guys.
    Let me start this comment by saying something useful: trying to use a variable that is not set yields a notice, not an error. (I think this is an important difference)

    Now, I have some questions, I hope they aren’t too stupid :p (especially 1 and 2)
    1) Is it just me or does your helper only 1 work for one variable? Probably you’re just trying to explain the principle and we could take it from there, but I think it would make more sense to change setValue($value) to something like setValue($key,$value). Combine this with a member variable which is an array of key-value pairs and you can set as many as you want. Probably you thought about this too but just kept it simple for us, or am I missing something? :-)
    2) Are you using a helper inside the controller? That’s a bit non-cake-ish isn’t it?
    3) Your “more advanced usage” (your reply to MiB) seems useful in some specific scenario’s, but to me this has nothing to do with the way these variables are set in your view/layout. Imho, it makes perfect sense to use a separate helper that does (nothing more then) “advanced logic” with variables in your view/layout. You could set the variables with MiB’s method or yours.
    4) I want to propose yet another alternative :-p. I don’t really like that with Daniels method you have to use the helper in the controller (correct me if i’m wrong!) and although MiB’s method is nicer in that area, I don’t really like the fact that this logic is mixed with other logic in AppControllers beforeFilter. So my proposal it using MiB’s method, but putting it separately in a component. (Which basically is the same as using Daniels method, but using a component instead of a helper and so the need for using another callback name)

  • pdaether

    I think an easy way is to define a helper function like this:

    saveEcho($name){
    echo (isset($$name))?$$name:”;
    }

    In the view you can then output the var:

    $myHelper->saveEcho(‘myVar’);

  • cakebaker

    @Dieter: Yes, you are right, it is a notice and not an error. I use “error” often in a more general way, meaning that something unexpected happens ;-)

    1) Yes, it is only for one variable. setValue() is not meant to be generic. In the helper from which I derived this example I pass three values to the “setValue()” function I use there.

    2) No, the helper is used in the views.

    3) Hm, to me it seems like overkill to use a separate helper for the “advanced logic”. Every helper handles one variable, and so I think the logic for the variable should be in the respective helper. But if you use a more generic approach as you described in 1), it probably makes sense to use separate helpers for the advanced logic.

    4) It depends on the data whether you should use a helper or a component. In my case the data are class definitions for the body tag and so I used a helper.

    Hope it is now more clear :)

    @pdaether: Hm, I don’t see how your solution will allow me to use something like echo $var_for_layout in the layout (my original goal)

  • Dieter@be

    Daniel, so you are calling $demo->setValue(‘value’); from out of your view? If you do I can understand the choice for a helper. But usually we want to set values from out of the controller (hence the choice for a component)

  • Felix Geisendörfer

    Am I missing something? Or are all of you folks ashamed of using PHP *g*?

    Should do the trick.

    — Felix

  • Felix Geisendörfer

    Swalled my tags : (:

    echo @$var_for_layout;

  • Joel Stein

    @felix: thanks for the tip.

    In case anyone else was like me and didn’t know what Felix is talking about, check out the documentation on PHP’s site:

    http://www.php.net/manual/en/language.operators.errorcontrol.php

  • cakebaker

    @Dieter: Yes, I am calling $demo->setValue(‘value’); from my view.

    @Felix: Yes, that works. But personally I think it is a bad idea to suppress error messages for errors you can simply avoid.

  • othman ouahbi

    By using @ you’re only supressing the notice on the variable you’re *expecting* so it’s ok.

  • moeffju

    I would just set the generic for_layout var in the AppController’s beforeFilter (to ”), and overwrite them in the specific controllers. Is there any reason that would be a bad idea?

  • cakebaker

    @moeffju: No, it is a fine solution for certain scenarios, see also the other comments.

  • Webspin

    Is there a way to set a custom layout variable from within the model directly? Or do you have to pass values from the model to the controller, and then have the controller set them to the layout?

  • cakebaker

    @Webspin: Your model shouldn’t know anything about a view resp. a layout. That means your controller has to get the data from the model and pass those data to the layout.

  • Eppo

    well i tried this, but nothing was showed in the $var_for_layout

    When i do a pr($this->value) in afterRender.. its just empty..

    When i do a pr($this->value) in valueSet($value) I get the value thats set in the view..

    How to do this?

  • cakebaker

    @Eppo: Hm, I just tested it here with the latest CakePHP version, and it works as described in the article. Can you post the relevant code somewhere, for example on http://bin.cakephp.org?

Bake a comment




(for code please use <code>...</code> [no escaping necessary])

© daniel hofstetter. Licensed under a Creative Commons License