The end of the HtmlHelper
For the past two years, I have gotten pretty close with the HtmlHelper. It has been there for me, making my life easier than before. But that time has come to an end, so sorry HtmlHelper, you are just too much of a burden now a days. The HtmlHelper has been an amazing convenience by automatically building my anchor links, by creating images with the correct path, or linking my CSS from within separate views, and much much more. But why do all this when you can simply write the HTML yourself?
I am not sure why I didn't notice this sooner; I was probably just stoked on developing with CakePHP so I wanted to do everything the CakePHP way. Lately however, I have noticed that the HtmlHelper really isn't needed that much. I can only think of a few cases where it is needed: linking stylesheets/javascript dynamically, building breadcrumbs and creating routes. Everything else is just consuming PHP logic and processing time to render HTML, which you can simply write yourself in the first place and bypass the PHP interpreter.
Linking Routes
The primary use of the helper, but why not just use url() instead? By doing that you don't have to deal with the hassle of nesting your array of attributes, or escaping variables into the method call. You also don't have to worry about Cake being over zealous and escaping all your data. Take these examples, they deliver the same result.
// With the helper
<?php echo $this->Html->link('Anchor Link', array('controller' => 'news', 'action' => 'index'), array('title' => 'Anchor Title')); ?>
// Without the helper
<a href="<?php echo $this->Html->url(array('controller' => 'news', 'action' => 'index')); ?>" title="Anchor Title">Anchor Link</a>
// Or pure HTML if your routes never change
<a href="/news" title="Anchor Title">Anchor Link</a>
Doctypes and meta tags
Another example of using PHP to render HTML, when you can just write HTML. This gets even easier with HTML5.
<?php echo $html->docType('xhtml-trans');
echo $html->meta('keywords', 'miles johnson php mysql design code developement developer web production creation coding functions tutorials methods scripts packages open source cakephp cake bake controller component model behavior view helper'); ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<meta name="keywords" content="miles johnson php mysql design code developement developer web production creation coding functions tutorials methods scripts packages open source cakephp cake bake controller component model behavior view helper" />
Images
This one I am still 50/50 on. It helps by determining the base URL, detecting asset timestamps and appending the alt attribute. But this of course can still be written normally as well. I will let you decide.
<?php echo $this->Html->image('logo.png'); ?>
<img src="/img/logo.png" alt="" />
Divs, paragraphs, tables, lists, script blocks
All of these should never be used in the view or layout. Using PHP to render basic HTML tags like this is absurd in my opinion.
So when should the helper be used?
Like I stated above, there are only a few times when the helper should be used. The first is asset linking (stylesheets and javascript). This allows you to include an asset from within any view, which is then output within $scripts_for_layout. Why is this so awesome? Simple, you can have a specific stylesheet for a specific page, without having to include it on all pages.
Secondly is building breadcrumbs. From within your view you can define the "top level" or "trailing" crumb, and within your layout you can define the base crumbs. This allows you to add multiple levels of crumbs within different layers of views. A quick example, which would give you the trail of: Blog -> Archives -> Blog Title.
// In the view
$this->Html->addCrumb('Archive', array('controller' => 'blog', 'action' => 'archive');
$this->Html->addCrumb($blog['Blog']['title'], array('controller' => 'blog', 'action' => 'read', $blog['Blog']['id']);
// In the layout
$this->Html->addCrumb('Blog', array('controller' => 'blog', 'action' => 'index');
Lastly, the primary reason that the HtmlHelper has all these convenience methods, is so that you can use them within other helpers. Since it's impossible to render HTML within PHP without string concatenation, the HtmlHelper gives other helpers the ability to render HTML easily without all the hardship. That is the primary reason of this helper.
Now all of this is a personal opinion of course, but since I didn't realize most of this for a while, I thought some of you might not have either. This is neither belittling the CakePHP dev team as they have done an awesome job so far, so thank you! So take it how you wish and code how you like. Enjoy!
11 Comments
Right now I have an app that has it's development version mirrored in a subdirectory named "/beta/", (due to ioncube loader domain restrictions) so all the urls need to be generated with the Router, not using absolute urls as you have done.
One thing I need to note for @Mauro and anyone else looking to user Router::url() to replace their $Html->url() is that you can not over ride the behavior of Router::url() but you can that of $Html->url(). What do I mean? I override the $Html->url() in my main AppHelper to add a trailing slash to all my URLs. I do this to avoid duplicate pages and other SEO concerns within CakePHP.
Thanks for the article Miles.
As your site grows, you almost have to use a helper for resources. Static resources should come from a CDN. If you have a helper to prepend the host then this becomes trivial. If you use multiple CDN subdomains you pretty much have to use a helper to rotate through the domains. I talked a little about this here: http://www.hermanradtke.com/blog/using-absolute-urls-in-the-view/
Consider other requirements you may have in the future. Things like multiple langauges, subdomains for affiliates and analytics are all good reasons to continue to use helpers in the view.
$tweets = $this->Articles->find('first');
echo $this->Html->image($article['image'], array('alt' => $article['title']))
Your safe!
<img src="/img/<?php echo $article['image'] ?>" alt="<?php echo $article['title'] ?>" />
First day: Yuck! That looks ugly
Second day: Someone hacked my site!
because we are still in need of it in one way or another.
the main advantage of it is the portability of your code URLS especially from development to production.
It is not obligatory like the FormHelper that provides security added values.
thank you for opening this subject and point to the "where we should" and "where we could"
@Nick - Was just stating how redundant it is to use a PHP class to render basic HTML markup. Like Jose stated, most overhead is within the route parsing which can be cached.
@everyone - The array routing really depends on the project, I am not stating it should be ditched all together. Personally, I have never had a project within a base folder, nor ever use many custom routes. So doing absolute paths is easier in some situations. It all boils down to personal preference or project requirements.
And yes, you can also use Router::url().
Another great benefit is controller and action are implied using array based routing, if you're redirecting to the same controller you can omit from the array.
The argument that why not use url() is kinda silly IMHO. the HtmlHelper is a lot cleaner than the url() alternative and you're not gaining any benefit as it's still going through the PHP interpretor anyway before being outputed.
In fact, I'm not convinced you're gaining any speed as the page has to be parsed through the PHP interpretor anyway. If you are gaining speed its negligible at best unless you rid of the HtmlHelper all together. And even then it's not a heavy class.
it's certainly personal taste. However, I ask, if you're to the point where you need to squeeze every last drop of performance out of your application and being rid of the HtmlHelper looks like a great idea to get that boost you seek, you probably would benefit from a few design changes instead. Just an observation.
Nick
But even though the overhead is high, I'll always use the helper, because it is an object-oriented way of creating my templates. What happens if you want to add a language parameter to all routes? Just hack AppHelper::url() and that's it.
That's a justification for always use url(), what about attribute parsing? They make you feel and know you're safe. Even adding attributes to html from user submitted data, you know your application won't suffer XSS attacks, not in the link title, not in the link attributes.
Helper url generation can be sped up by just adding a url cache at AppHelper level, check my github for a plugin that will assis you cache links in views.
Cheers.
Most routes never change eh? That really depends on your boss. In fact, I have spent months fine tuning urls for certain projects. At the very least if you aren't going to use HtmlHelper, do this:
Also you way is not flexible in regards to different document base paths. For example if I have a different version of my app in a subdirectory, "/news" will ignore my subdirectory.
Right now I have an app that has it's development version mirrored in a subdirectory named "/beta/", (due to ioncube loader domain restrictions) so all the urls need to be generated with the Router, not using absolute urls as you have done.
The performance hit of HtmlHelper is also minimal if you are using caching correctly and judiciously.
One more tip: you can replace $html->url() by Router::url(), that way you won’t need to include the helper at all and you can use reverse routing in links like this