Multiple forms on the same page in Zend Framework

Photo by Christa Dodoo on Unsplash.

Processing two forms on the same page is not the simplest task in the Zend Framework. About a year ago I encountered this problem and studied others' approaches. Eventually I hacked together a solution with multiple actions and lots of redundant code. But I was recently making updates to this page and found a more elegant method for handling multiple forms.

Most solutions to this problem involve submitting each form to a different action. And many times that is appropriate. Imagine a login page with a search box in the corner. The login form can self-post and the search form can submit to its own action. If the search form fails validation, it can reappear with errors on its own page and the user won't miss the login form.

But sometimes your forms are similar enough that they should be processed by the same action. It's hard to describe such a scenario, but frankly it doesn't matter. If you're reading this post, you probably have such a need in your project and my example doesn't matter. So we'll imagine a user profile page with two forms for the user to update different parts of their account.

I've written previously about how I use forms across the MVC layers. If something in the code below doesn't make sense, please check my "MVC Form Layer" post.

First let's look at the view for the user profile edit page.

$this->editForm1->setMethod('post')
                ->setAction($this->url());

$submitForm1 = new Zend_Form_Element_Submit('submitForm1');$submitForm1->setLabel('Save Form 1');
$this->editForm1->addElement($submitForm1);

echo $this->editForm1;

$this->editForm2->setMethod('post')
                ->setAction($this->url());

$submitForm2 = new Zend_Form_Element_Submit('submitForm2');$submitForm2->setLabel('Save Form 2');
$this->editForm2->addElement($submitForm2);

echo $this->editForm2;

Notice that each form has a separate submit button, and each submit button has a different name in the form.

And now the edit profile action in the User module's Index controller.

public function editProfileAction()
{
    // TODO: Get user's ID from their session.
    //$userId = ???

    $userMapper = new Application_Model_Mapper_Users();
    $user = $userMapper->getById($userId);
    if($user === NULL) {
        // TODO: Handle failed profile lookup.
        return;
    }

    $this->view->editForm1 = $user->getForm('form1Name');
    $this->view->editForm2 = $user->getForm('form2Name');
    $request = $this->getRequest();
    if($request->isPost()) {
        $postData = $request->getPost();

        if(isset($postData['submitForm1'])) {            // The user submitted editForm1.
            if(!$user->saveForm1($this->view->editForm1, $postData)) {
                // Redisplay editForm1 with validation errors.
                // Repopulate the editForm2 from saved data.
                $this->view->editForm2->populateFromUser($user);
            } else {
                // TODO: Handle successful editForm1 submission.
                return;
            }
        } else { // OR elseif(isset($postData['submitForm2']))            // The user submitted editForm2.
            if(!$user->saveForm2($this->view->editForm2, $postData)) {
                // Redisplay editForm2 with validation errors.
                // Repopulate editForm1 from saved data.
                $this->view->editForm1->populateFromUser($user);
            } else {
                // TODO: Handle successful editForm2 submission.
                return;
            }
        }
    } else {
        // Populate both forms from saved data.
        $this->view->editForm1->populateFromUser($user);
        $this->view->editForm2->populateFromUser($user);
    }
}

Notice that we're checking the POST data for the names of the submit button that we created in the view. I've replaced some sections with // TODO comments to focus on the multi-form handling.

This is a bare bones example, but once you add more logic the advantages of a single action will stand out. If you submit each form to a different action, each action and view will share much of the same code. The approach above is much DRY-er.

Drew

Drew

Hi! I'm Drew, the Wimpy Programmer. I'm a software developer and formerly a Windows server administrator. I use this blog to share my mistakes and ideas.