Phase 1: static pages
On the previous system, I was partly using a blog package (WordPress) to maintain these, with the content stored in a database rather than HTML files. For those like me who are happy using a text editor, it's debatable whether this is worth the bother, so I'll go back to maintaining simple HTML templates. Several of the separate sections used ordinary html files, so these are easy to convert to HTML templates as well.
As the site already exists and the same style is to be maintained, converting this static content is straightforward:
- assets (i.e. images, css, js): the default setup created by init-project can be used by simply copying the existing assets: stylesheet into web/css/main.css; images into web/images/; js into web/js/
- layout: the default template apps/main/templates/layout.php can be adapted by inserting the basic layout and sidebar menu from the old site, changing the links to use the link_to() helper and the asset links to use the asset helper. The breadcrumbs at the top are adapted from Richard Steven's snippet on the Symfony site (making it compatible with xhmtl amongst other things)
Modules
symfony init-module main general/it/walking
Each page will have its own template containing the content but, as there is no logic involved in generating them, the only action needed is to show it. So we'll change actions.class.php for each module, replacing the default executeIndex() with:
public function executeShow()
{
return $this->getRequestParameter('page');
}
So, instead of returning 'success' or whatever, it returns the name of the page.Meta html
metas:
title: Peter Robins, his website
description: Peter Robins, his website
author: Peter Robins
However, this doesn't describe the individual pages, so we'll also define separate texts for each page by setting up a view.yml for each module. For example, apps/main/modules/walking/config/view.yml might contain:
showindex:
metas:
title: Walking Intro
description: General description of the walking part of the website
keywords: walking, hiking
showblisters:
metas:
title: Tips on blisters
description: Tips on blisters
keywords: walking, hiking, blisters
URL routing
homepage:
url: /
param: { module: general, action: show, page: index }
page_show:
url: /:module/:page
param: { action: show }
In addition, the default for each module should be changed to show the home page:
default_show:
url: /:module/
param: { action: show, page: index }
Note that /:module/ is not the same as /:module - under the normal Unix directory structure, /xxx means show me a page called xxx; /xxx/ means show me the contents of the directory xxx. In this case, we don't want a page called :module, but the default action of the module, which in our case is 'show', and the default page, 'index'. suffix: .html
to the 'all/routing' settings in apps/main/config/factories.yml (according to the Reference Manual, this setting is "deprecated and is not useful anymore", but AFAIC it's still very useful).This structure maintains backwards-compatibility with the normal url structure that was used previously - /directory/page.html - but it does mean that symfony's default /:module/:action won't work. When we come to set up the routing for those pages that have an action other than 'show', we have to remember to include them all in routing.yml. Of course, this increases the size of the file and the work that the routing process has to do for each request.
Templates
<?php echo link_to('static HTML','it/symfony-static') ?> and an example image tag <?php echo image_tag('walks', 'alt="My walks" size=694x819') ?>
The email in the menu uses the mail_to helper to create a different address depending on which section of the site the user is looking at: mail_to('web_'.$sf_params->get('module').'@peterrobins.co.uk', 'Email me', array('encode' => true))Javascript
Those javascript functions in the html head section can be put in separate js files in web/js/ and loaded by adding them to the javascript section for the page in the module's config/view.yml, such as:
javascripts: [positionfinder, http://maps.google.co.uk/maps?file=api&v=2&key=googlekey]
[Note: I no longer use this mechanism, but I'll leave it in here as an example of using a slot.] Any javascript in the body can be left there, but some pages, like those using Google Maps for example, need an onload() method on the body tag. Of course, you can also use window.onload and window.onunload, but for practice we'll set up the body tag. How to do this in Symfony? No doubt there are several ways, but I did it with a slot. This requires a change to:
- the page itself, to create the slot; conceptually this is logic, so belongs in the action, but I couldn't be bothered creating an action just for that so added it to the template:
<?php slot('body_onload') ?>onunload="GUnload()" onload="load();"<?php end_slot() ?> - apps/main/templates/layout.php, to put the slot in the body tag:
<body <?php if (has_slot('body_onload')) echo get_slot('body_onload'); ?>>
One problem I encountered was with older javascript written for non-strict html pages. These may not work in xhtml which expects css style, for example, to conform fully with the standard. Browsers do not always provide useful error messages for this. I have often found in IT projects that you sail through the main tasks but then get held up for hours on really trivial issues. This project was no exception: the most time by far was spent with the floating Pooh page which uses a javascript script called moveobjectslib. However, once I realised what the problem was, it was quickly fixed.
Error page
Something which is often forgotten about in websites is a default error page: programmers are so busy testing for possible errors in their programs that they forget that it's very easy for people to mistype urls, or click on an ancient link on another website to a long-dead page. A 404 error page is easy to set up in Symfony:
- uncomment the error404 lines in apps/main/config/settings.yml, changing them to, for example, general/error (you might as well change the default action while you're at it, for example, to general/index). Beware, though! This is not a url but a module/action, in other words, it expects there to be an error action in the general module
- so, add the function to apps/main/modules/general/actions/actions.class.php
public function executeError() { return '!'; } - and create the template apps/main/modules/general/templates/error!.php, with a text like "You have tried to access a page that doesn't exist. Please select an option from the menu." (I find the concept of a successful error rather illogical, so I return '!' to get the template error!.php, instead of returning the default 'Success')
Now, trying to get a non-existent module like web/grunt or web/ir should give you this error page rather than Symfony's default.
However, what if the user gets the module name right, but mistypes the page name, for example, 'general/avout', or simply tries a non-existent page like general/grant? Because we set up the routing to allow any name after 'general/', Symfony will not raise a 404 error; it will find a valid routing for the module 'general', but will then fail to find the template 'showavout.php' or 'showgrant.php'. This will raise a 500 internal server error 'template not found'. To catch these, we must set up an error.html.php file in config/error/ to override Symfony's default one. A simple way would be to have http forward to /error, a non-existent module: <meta http-equiv="refresh" content="0;URL=/error">. However, this is not a very good solution, as it assumes that all 500 errors are due to mistyped urls, and there should really be a proper 500 error page to display; create one afterwards.
A better solution is to revisit our actions and check whether the template file actually exists before passing control to the view; so change actions.class.php for all the modules:
public function executeShow()
{
if (is_readable(sfConfig::get('sf_app_module_dir').DIRECTORY_SEPARATOR.$this->getRequestParameter('module').DIRECTORY_SEPARATOR.'templates'.DIRECTORY_SEPARATOR.$this->getRequestParameter('action').$this->getRequestParameter('page').'.php'))
return $this->getRequestParameter('page');
else
$this->forward404();
}
Of course, this assumes that the filenames follow the conventions. If the template file is not readable, it's an error, so we can ship it off to the 'page not found' error we created above. An alternative way to handle these erroneous urls would be to go back to the beginning of this page, and put all the page texts in a database instead of in separate templates; then, if the action finds the text, it uses it, else it's an error.
Phew! What a lot of work just to get an error report! However, better to give the user some meaningful feedback rather than an uninformative default file.
Other assets
I mentioned above the publicly available files that Symfony puts by default in web/. Any other publicly available files you have can be put here; for example, I put the source code for the Mojavi tutorial in web/downloads/.

