Search code examples
phpzend-framework2multipage

zf2 - need help on multi page form in one page


I'm just learning zf2, and the basic tutorial from official document is great. Now, I would like to challenge myself to create multi page form in one page, something like that http://demo.stepblogging.com/multi-step-form/

So, currently I have two forms called "Contact Form" and "Album Form".

The idea is i want to split to 2 forms. The problem is when i finish all the fields on the first form, i'm not sure how to go to next form. I'm not sure i can do the logic at Controller, what i know most of the online tutorials are using javascript to handle the next and back button. Or maybe have better idea?

So this is my Controller page.

 public function multipleAction(){
    $formOne = new ContactForm();
    $formTwo = new AlbumForm();

    $formOne->get('next')->setValue('Next');
    $request = $this->getRequest();

    if($request->isPost()){ 
        $aa = new ContactFilter();
        $formOne->setInputFilter($aa); 
        $formOne->setData($request->getPost()); 

        if ($formOne->isValid()) {
           //save session 
           //maybe display second form or any other solution
        }
    }

my multiple.phtml page contains 2 forms

<ul id="signup-step">
<li id="contact" class="active">Contact</li>
<li id="album">Album</li>
</ul>

<?php
 $form_one = $this->form_one;

 //$form_one->setAttribute('action', $this->url('album', array('action' => 'multiple')));
 $form_one->prepare();

 //echo $_SESSION['name'];
 echo $this->form()->openTag($form_one); ?>

 <div id="contact-field">
<legend>Contact</legend>
<?php
 echo $this->formHidden($form_one->get('id'));

 echo $this->formLabel($form_one->get('name')).'<br>';
 echo $this->formInput($form_one->get('name'))."<br>";
 echo $this->formElementErrors($form_one->get('name'));

 echo $this->formLabel($form_one->get('artist')).'<br>';
 echo $this->formInput($form_one->get('artist'))."<br>";
 echo $this->formElementErrors($form_one->get('artist'));


 echo $this->formLabel($form_one->get('address')).'<br>';
 echo $this->formInput($form_one->get('address'))."<br><br>";
 echo $this->formElementErrors($form_one->get('address'));

 echo $this->formSubmit($form_one->get('next'));
 echo $this->form()->closeTag($form_one);
 ?>

 <?php
 $form_two = $this->form_two;
 $form_two->prepare();

 echo $this->form()->openTag($form_two); ?>

 <div id="album-field" >
<legend>Album</legend>
<?php 
 echo $this->formLabel($form_two->get('title')).'<br>';
 echo $this->formInput($form_two->get('title'))."<br>";
 echo $this->formElementErrors($form_two->get('title'));

 echo $this->formLabel($form_two->get('artist'))."<br>";
 echo $this->formInput($form_two->get('artist'))."<br>";
 echo $this->formElementErrors($form_two->get('artist'));
 echo $this->form()->closeTag($form_two);
 ?>


Solution

  • The code below is very crude and is only for example purposes (made this in 10 mins and looks like it)

    So basically all we are doing is the following -

    1. Creating a session
    2. Checking if there is any data in the session for that particular form, if there is one then use setData() to set the data on the form
    3. If the form posts data, then validate & filter it and then save that data to the session
    4. The "Submit" button value in the form is used to determine the navigation path i.e. previous or next page.
    5. The final page will save data from the session to the DB (or whatever you want to do with it) and of course destroy the session as well.

    Controller

    /** @var Zend\Session\Container Session Object */
    private $session;
    
    public function __construct()
    {
        // Put a meaningful name in the constructor
        $this->session = new Container();
    }
    
    public function page1Action()
    {
        // This should really be injected...
        $form = new Form\Form1();
    
        // Proper validation & filtering needs to be done for every form (this is example only)
        $form->setInputFilter(new InputFilter());
    
        // Populates form data from the session if it already exists
        if (isset($this->session->form1Inputs)) {
            $form->setData($this->session->form1Inputs);
        }
    
        $request = $this->getRequest();
    
        if ($request->isPost()) {
            $form->setData($request->getPost());
    
            if ($form->isValid()) {
                $formData = $form->getData();
    
                // Saves the new data to the session
                $this->session->form1Inputs = $formData;
    
                // Redirects to next page
                if ($formData['Next'] === 'Next') {
                    $this->redirect()->toRoute('application', ['controller' => 'Index', 'action' => 'page2']);
                }
            }
        }
    
        return new ViewModel(['form' => $form]);
    }
    
    public function page2Action()
    {
        // This should really be injected...
        $form = new Form\Form2();
    
        // Proper validation & filtering needs to be done for every form (this is example only)
        $form->setInputFilter(new InputFilter());
    
        // Populates form data from the session if it already exists
        if (isset($this->session->form2Inputs)) {
            $form->setData($this->session->form2Inputs);
        }
    
        $request = $this->getRequest();
    
        if ($request->isPost()) {
            $form->setData($request->getPost());
    
            if ($form->isValid()) {
                $formData = $form->getData();
    
                // Saves the new data to the session
                $this->session->form2Inputs = $formData;
    
                // Redirects to next or previous page
                if ($formData['Next'] === 'Next') {
                    $this->redirect()->toRoute('application', ['controller' => 'Index', 'action' => 'page3']);
                } elseif ($formData['Previous'] === 'Previous') {
                    $this->redirect()->toRoute('application', ['controller' => 'Index', 'action' => 'page1']);
                }
            }
        }
    
        return new ViewModel(['form' => $form]);
    }
    
    public function page3Action()
    {
        // This should really be injected...
        $form = new Form\Form3();
    
        // Proper validation & filtering needs to be done for every form (this is example only)
        $form->setInputFilter(new InputFilter());
    
        // Populates form data from the session if it already exists
        if (isset($this->session->form2Inputs)) {
            $form->setData($this->session->form2Inputs);
        }
    
        $request = $this->getRequest();
    
        if ($request->isPost()) {
            $form->setData($request->getPost());
    
            if ($form->isValid()) {
                $formData = $form->getData();
    
                // Saves the new data to the session
                $this->session->form3Inputs = $formData;
    
                // Finalise or redirect to previous page
                if ($formData['Finalise'] === 'Finalise') {
                    // Save all data to DB from session & destroy
                } elseif ($formData['Previous'] === 'Previous') {
                    $this->redirect()->toRoute('application', ['controller' => 'Index', 'action' => 'page2']);
                }
            }
        }
    
        return new ViewModel(['form' => $form]);
    }
    

    Form 1

    public function __construct($name = null)
    {
        parent::__construct($name);
    
        $this->add(['name' => 'Form1Text1', 'type' => 'Text', 'options' => ['label' => 'Form 1 Text 1 : ']]);
    
        $this->add(['name' => 'Form1Text2', 'type' => 'Text', 'options' => ['label' => 'Form 1 Text 2 : ']]);
    
        $this->add(['name' => 'Next', 'type' => 'Submit', 'attributes' => ['value' => 'Next']]);
    }
    

    Form 2

    public function __construct($name = null)
    {
        parent::__construct($name);
    
        $this->add(['name' => 'Form2Text1', 'type' => 'Text', 'options' => ['label' => 'Form 2 Text 1 : ']]);
    
        $this->add(['name' => 'Form2Text2', 'type' => 'Text', 'options' => ['label' => 'Form 2 Text 2 : ']]);
    
        $this->add(['name' => 'Next', 'type' => 'Submit', 'attributes' => ['value' => 'Next']]);
        $this->add(['name' => 'Previous', 'type' => 'Submit', 'attributes' => ['value' => 'Previous']]);
    }
    

    Form 3

    public function __construct($name = null)
    {
        parent::__construct($name);
    
        $this->add(['name' => 'Form3Text1', 'type' => 'Text', 'options' => ['label' => 'Form 3 Text 1 : ']]);
    
        $this->add(['name' => 'Form3Text2', 'type' => 'Text', 'options' => ['label' => 'Form 3 Text 2 : ']]);
    
        $this->add(['name' => 'Previous', 'type' => 'Submit', 'attributes' => ['value' => 'Previous']]);
        $this->add(['name' => 'Finalise', 'type' => 'Submit', 'attributes' => ['value' => 'Finalise']]);
    }