CakePHP3: When to use Elements, Helpers or View Cells?

This has been asked on Stackoverflow and I decided to turn my long answer in a Blog post as well. This article will explain when to use the different parts of views the best. This might be opinionated but it is based on writing clean and re-usable code.

Elements

Use it when you need to repeat presentation related stuff, usually HTML, a lot. For example I have a project in which three tables use records of an addresses table. The form part of all of these three that contains the address data is an element. There is no logic at all in this and I think if it is just a very simply if that wraps an element call or something similar you don’t need to come up with a helper for that, it’s just to basic. Elements are basically “dumb” snippets that repeat through the site.

Helpers

Use it to encapsulate view logik, don’t put HTML in it if possible or other presentation related things. For example let it do something and depending on the result you can use an element of that result type to render the data: `return $this->_view->render(‘items/’ . $type . ‘_item’);`

You might say now “But the Form- and HtmlHelper contain HTML…” well, they do but look at their code how they deal with it. If you look a the HtmlHelper for example you’ll see a property $_defaultConfig[1]:

These are the template strings that are used to generate the HTML output. This separtes the markup pretty nice from the actual code that generates the final output. Take a look at the FormHelper as well, it’s using widgets to render more complex output. See this section “Adding Custom Widgets“[3] of the official documentation.

So this works fine with element like pieces of markup. By a rule of thumb I would say if your markup is longer than what you see there make it an element and call it from within the helper or make it a widget.

View Cells

Think of view cells as “Mini MVC” stacks that have a view and can load multiple models. They’re IMHO similar to AngularJS directives if you’re familiar with them. See this article for an example [2]. I really suggest you to read it, it explains them and their use cases in detail.

I haven’t done much with them yet but they can be used to replace requestAction() calls for example. You won’t “pollute” your controller with methods that are not intended to be access by a request. Taken from the linked article above:

One of the most ill-used features of CakePHP is View::requestAction(). Developers frequently use this all over their applications, causing convoluted cases where you need to figure out if you are within a web request or an internal action request, cluttering controllers. You also need to invoke a new CakePHP request, which can add some unneeded overhead.

Disclaimer

The above reflects my personal view on these things, there is no ultimate and final rule how you have to use these three things. The goal is always clean and re-useable code and proper separation of concerns. How you archive that is up to you, you’ve got the tools.

[1]: http://api.cakephp.org/3.0/source-class-Cake.View.Helper.HtmlHelper.html#54
[2]: http://josediazgonzalez.com/2014/03/20/view-cells/
[3]: http://book.cakephp.org/3.0/en/views/helpers/form.html#adding-custom-widgets

Best practice for dealing with additional data in beforeSave()

On stackoverflow.com somebody asked how to securely set a value inside a form. Besides the obvious fact that you don’t do that if the value is not needed for some purpose in the view, one of the answers given to that question contained so much not so good code, that it inspired me to give a detailed answer as well and to write this article about it. Here is the code in question:

Short summary of what is wrong here:

  • $this->alias is not used
  • The method signature is wrong and will cause a php 5.4+ strict warning
  • Tight coupling with the Router class is introduced
  • The direct assignment of an integer value here is not good
  • The if check is not using strict === comparison

This code is introducing tight coupling with a static call and the model should not need to be aware of the Router. Also you’re missing the $options argument in the Model::beforeSave() which causes strict errors on php 5.4+ and up to date CakePHP. See this pull request for reference.

Further this couples the model code with a specific controller action and don’t even check for the controller as well. If the parameters are needed they should be passed to the model instead of accessed through a static call of the Router. In fact you can pass additional options through the second argument of the Model::save() method. The documentation doesn’t explain this very well, but if you look at the code you can pass any additional options besides the validate and callbacks options. These options are passed to the Model::beforeSave() method, which is in fact listening to the Model.beforeSave event. So in fact you could do this:

The best way to do it would be in the model because this will make sure the default group id gets always set, no matter from where the save is called and you can’t forget to pass the group id:

An alternative way is to  simply add the data in the controller method to $this->request->data but the model is the far better place because you can test the whole thing, use it in a shell and have everything in one place and the right place because all of this belongs into the model layer.