Pragmatism in the real world

A form in your layout

I recently received an email asking for my advice about how to handle a form that appears on every page.

I want to add a newsletter sign up box to layout.phtml so it will appear on every page. The layout->content() comes from several different action controllers… So how do I handle the newsletter sign up?

I thought that the answer is long-winded enough to be worth writing a blog post about.

One way to do this is to use a action helper, so let’s build a simple application to show this solution.

Start by setting up a ZF application:

$ zf create project layoutform
$ cd layoutform
$ zf enable layout

Copy the Zend Framework’s library/Zend folder into layoutform/library check that you get the ZF Welcome screen when you navigate to the project in your browser. Empty application/views/scripts/index/index.phtml and replace with something simple like this:

application/views/scripts/index/index.phtml:
<p>This is the home page</p>

Now we have a clean, simple place to start from.

The form

We need a form, so we’ll create one:

$ zf create form signup

Now, let’s create the fields we need:

application/forms/Signup.php:
class Application_Form_Signup extends Zend_Form
{
public $processed = false;

public function init()
{
$this->addElement('text', 'name', array(
'label' => 'Name',
'required' => true,
'validators' => array(
array('StringLength', false, array('max'=>75)),
),
));
$this->addElement('text', 'email', array(
'label' => 'Email',
'required' => true,
'validators' => array(
array('StringLength', false, array('max'=>150)),
'EmailAddress',
),
));
$this->addElement('submit', 'go', array(
'label' => 'Sign up',
));
}
}

now we have a form, we need to instantiate it and then display it.

The action helper

We use an action helper to instantiate the form and then later process it.

Firstly, setup the action helper. add a line to the [production] section of the config file:

application/configs/application.ini:
resources.frontController.actionhelperpaths.Application_Controller_Helper = APPLICATION_PATH "/controllers/helpers"

Now the system knows were we are storing our action helpers, we can register a helper called Signup:

application/Bootstrap.php:
<?php

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initMyActionHelpers()
{
$this->bootstrap('frontController');
$signup = Zend_Controller_Action_HelperBroker::getStaticHelper('Signup');
Zend_Controller_Action_HelperBroker::addHelper($signup);
}
}

The action helper looks like this:

application/controllers/helpers/Signup.php:
<?php

class Application_Controller_Helper_Signup extends Zend_Controller_Action_Helper_Abstract
{
public function preDispatch()
{
$view = $this->getActionController()->view;
$form = new Application_Form_Signup();

$request = $this->getActionController()->getRequest();
if($request->isPost() && $request->getPost('submitsignup')) {
if($form->isValid($request->getPost())) {
$data = $form->getValues();
// process data

$form->processed = true;
}
}

$view->signupForm = $form;
}
}

This is pretty standard code for form handling. The only unusual thing we do is that set the processed property of the form to true so that we know that we have done something. We then use this to display a thank you message.

The view helper

To display the form, we use a view helper which is called from the layout view script.

The view helper looks like this:
application/views/helpers/SignupForm.php:
<?php

class Zend_View_Helper_SignupForm extends Zend_View_Helper_Abstract
{
public function signupForm(Application_Form_Signup $form)
{
$html = '<h2>Sign up for our newsletter</h2>';
if($form->processed) {
$html .= '<p>Thank you for signing up</p>';
} else {
$html .= $form->render();
}
return $html;
}
}

And to round it off, we render this in layout.phtml, whilst also taking the opportunity to create a minimal layout script:

application/layouts/scripts/layout.phtml:
<?php
$this->headMeta()->prependHttpEquiv('Content-Type', 'text/html; charset=UTF-8');
$this->headTitle('Layout form test');
echo $this->doctype(); ?>
<html>
<head>
<?php echo $this->headMeta()->setIndent(4); ?>
<?php echo $this->headTitle()->setIndent(4); ?>
</head>
<body>
<div id="maincontent">
<?php echo $this->layout()->content; ?>
</div>
<div id="secondary">
<?php echo $this->signupForm($this->signupForm); ?>
</div>
</body>
</html>

All done

And that’s it. The form is displayed on every page and if filled in, it is processed and a thank you message is displayed. Obviously validation also works.

Completely un-styled, it looks like this:
Sign up form in a layout

This is the project code that I used: zf-tutorial-layoutform.zip

if you found this useful, then you should have a read of Matthew’s article on using action helpers to implement re-usable widgets also.

15 thoughts on “A form in your layout

  1. Rob,

    Really? You "recently received an email asking for your advice about how to handle a form that appears on every page"?

    Not that this question was asked just the day before on Stackoverflow?

    http://stackoverflow.com/questions/3898956/zend-form-should-i-use-one-right-in-the-layout

    even the further reading link you provide at the bottom is identical.

    Shame. If you don't want to help with answers on Stackoverflow and just want to use it to find material, you should have at least helped the Stackoverflow community by providing a link to this tutorial there.

    (oh yeah, and what's with the I received an email. Give credit to Stackoverflow please since it's clear that's where you got the idea from)

  2. Grecko,

    The question is a fairly common one on the web, not just stackoverflow. It also comes up on #zftalk. Lots of people have this problem.

    I receive a few emails a week from people asking me questions directly. Usually, I just reply directly but sometimes I create an article here, if I think that it's a general enough topic.

    In this case, I received the email on 25th September and wrote up the code and draft of this article on 3rd October. Then Matthew published his article a day later and stole my thunder :)

    You are, of course, welcome to believe whatever you want.

    Regards,

    Rob..

  3. Greko,

    Rob is a well-known, active and respected member of the ZF community and a regular speaker at conferences about ZF topics.

    You can be sure that he don't need Stackoverflow to get topics to blog about. He gets a lot of questions because of his outstanding position in the ZF community.

    Regarding the further reading link: The post is from the lead developer of ZF, so no wonder its linked often.

    Jan

    BTW: Thank you Rob for work around ZF, much appreciated!

  4. Greko ,
    As far as I know when we start #zf we all starts from here . Especially newbies.
    As a blogger he can write what he thinks and may it may be for the future reference also . So I don't want to remember each and every-time the big link of any other websites.
    I do write something on my blog for my references and if some one loves it , they can also take from it.
    We love the community and the spirit . Its really a shame on you rather than him . I feel you are someone related to stackoverflow than a zend framework lover. I am not saying stackoverflow is bad , but the spirit you have shown is bad.
    You may get lots of responses soon from the #zf community ;) . We love you Rob :).

  5. Hello Rob,
    i tried your implementation on a module based application but i have the problem that my "Plugin by name 'SignupForm' was not found in the registry;" (this is the original error message).
    The script searches at "Users_View_Helper_" (this is the module "Users") and at "Zend_View_Helper_".

    Where should I tell him that it should use the default module?

    regards
    tobias

  6. Additional hint:
    In default Module everything works fine! ;-)
    But if I call another module i got this error message described above.

    regards

  7. Hi Rob,

    Thanks for the article.

    One suggestion: maybe this was also a good opportunity to highlight why you did the
    $request->getPost('submitsignup')
    check (and maybe also add it to the creation of the form).
    Or, even better, extend Zend_Form for you application.

    Your example sign-up form will surely lead to the use of multiple forms on one page. This will cause problems for people which may not expect the default $form->isValid($_POST) (taken from the manual) will also validate your form if another form on the same page is submitted.

    Sam

  8. Hi Rob,
    Excellent article – I am new to Zend Framework, but I'm used to using CI and found this article brilliant for making the transistion.

  9. Hi.
    I am using a different method to render the form in the views.I got one question, what is the value of form action param?
    thanx.

  10. Hi,

    Thank you for this post, valuable.

    Small mistake in your application/forms/Signup.php:

    Last element:

    $this->addElement('submit', 'go', array(
    'label' => 'Sign up',
    ));

    Should be:

    $this->addElement('submit', 'submit-signup', array(
    'label' => 'Sign up',
    ));

Comments are closed.