1
votes

I'm wondering if there was a way to add a group of elements to a zend form as if they were one element, I guess much like a subform, but it seems the functionality of a subform may be too much...

Here's my use-case. I've created a class that handles multi-page forms. I want to be able to write logic to change the buttons at the bottom of the form based on the page of the form I'm on. Form Page 1Form Page 2Form page 3

I originally thought that Zend-Form-DisplayGroup would fix my problem, but you have to add the items to the form first and then add them to the display group and can't pass a display group through a function with attached elements. I would like to have a function that would be something like

public function setSubmitButtonGroup($submitButtonGroupElements)
{
     /*Code to set button group*/
}

The idea of using an array of elements just hit me right now as opposed to something else and add logic to add that array of elements to the form on render... but does anyone have any "better" ideas or done this before?

BTW, if anyone is wondering... I'm loosely basing my initial design off of this section: Zend Framework Advance Form Usage.

3

3 Answers

1
votes

Not sure I understand your problem correctly but this how I do some things.

In a Zend_Form object you can add elements as a group with `addElements($elements) in an array. For the Submit button etc. I have a class where I get the $elements array from and then I simply pop it in. I also add a displayGroup but separately and simply to control where the buttons are. Because a form is an object you can do simple things like the following but I always add a reference to show my intent.

update: shuffled the button manipulation

function addButtons(&$form,$nextName = null) {
    $buttons = $this->getButtons();  // this will be an array with your buttons
    // make sure you have the element names in your buttons arrays
    if ( is_string($nextName) ) {
        $buttons['nextButton']->setLabel($nextName);
    } elseif ( is_bool($nextName) && false === $nextName ) {
        unset($buttons['nextButton'];
    }
    // repeat for other buttons

    $form->addElements($buttons);
    $elementNames = array_keys($buttons);
    $form->addDisplayGroup($elementNames,'buttonGroup',array('legend'=>'Click some buttons'));

}

$this->addButtons($form,'Finish');
1
votes

You could make yourself a factory that receive three params, your form element, the current controller and the current action. Then in that factory, you could call a builder based on the controller/action combination and you pass your form.

In your builder you add 1, 2 or 3 buttons based on the corresponding controller/action requirement which are stored in diffrent components. Once it is done, you return your form to the factory and the factory return the form.

My_Form // your Zend_Form object

My_Form_Factory // Your new factory (see below)

My_Form_Factory_Builder_Controller_Action  // One of your builder (see below)

My_Form_Factory_Component // Extends the corresponding Zend_Form_Elements

// basic factory that can be called like My_Factory::factory($form, $controller, $action)
class My_Form_Factory {
    static public function factory($form, $controller, $action)
        $builderClass = "My_Form_Factory_Builder_" . $controller . '_' . $action;
        $builder = new $builderClass($form);
        return $builder->buildForm();
}

// Basic builder
class My_Form_Factory_Builder_Controller_Action
{
    protected $_form;
    protected $_previousComponent ;
    protected $_nextComponent ;
    protected $_cancelComponent ;

    public function __construct($form)
    {
        $this->_form = $form;
        $this->_previousComponent = new My_Form_Factory_Component_Previous();
        $this->_nextComponent = new My_Form_Factory_Component_Next();
        $this->_cancelComponent = new My_Form_Factory_Component_Cancel();
    }

    public function buildForm()
    {
        $this->_form->addElement($previousCompnent);
        $this->_form->addElement($nextComponent);
        $this->_form->addElement($cancelComponent);

        return $this->_form;
    }
}

If you want to automatize the instanciation you could initialize all the different compoments you might require in an abstract class and in the method buildForm() only add the elements you need for that current interface. (I would rather repeat the code in each builder than rely on this kind of "magic" but it a viable method to do it).

0
votes

So the complexity of my problem comes with knowing what page of the multipage form. Using an array and the above mentioned addElements() helped.

Simple Answer

The answer to my problem was an array that could be manipulated after the form was "built" so to speak but before it was rendered so that I could add to the form using addElements().

Long Answer

To get the whole picture, imagine each time you hit the next or previous button, you are traversing through an array of subforms. In this case one would need a function to handle the button rendering. I ended up using a case statment, though it's not the best implementation in the world (not reusable in the parent class Form_MultiPage), but it worked:

in my extention of my mulipage form class I have

public function setSubmitControls()
{
    $previous = new Zend_Form_Element_Submit('previous',array(
        'label'=>'previous',
        'required'=>false,
        'ignore'=>false,
        'order'=>9000
    ));
    $cancel = new Zend_Form_Element_Submit('cancel',array(
        'label'=>'Cancel',
        'required'=>false,
        'ignore'=>false,
        'order'=>9003
    ));
    $next = new Zend_Form_Element_Submit('next',array(
        'label'=>'Next',
        'required'=>false,
        'ignore'=>false,
        'order'=>9002
    ));
    $finished = new Zend_Form_Element_submit('finish',array(
        'label'=>'Finish',
        'required'=>false,
        'ignore'=>false,
        'order'=>9004
    ));
    $submitControls = array();
    echo var_dump($this->getCurrentSubForm()->getName());
    switch($this->getCurrentSubForm()->getName())
    {
        case 'billInfo':
            $submitControls = array(
                $next,
                $cancel
            );
        break;
        case 'payerInfo':
            $submitControls = array(
                $previous,
                $next,
                $cancel
            );
        break;
//So on for other subforms
    }
    $this->setSubmitButtonGroup($submitControls);
}

In my parent class, Form_Multipage, I have

public function setSubmitButtonGroup(array $elements)
{
    $this->_submitButton = $elements;   
}

And

public function addSubmitButtonGroupToSubForm(Zend_Form_SubForm $subForm)
{
    $subForm->addElements($this->_submitButton);
    return $subForm;
}

Which is called when I render the "page" of the form with this function

public function prepareSubForm($spec)
{
    if (is_string($spec)) {
        $subForm = $this->{$spec};
    } elseif ($spec instanceof Zend_Form_SubForm) {
        $subForm = $spec;
    } else {
        throw new Exception('Invalid argument passed to ' .
                            __FUNCTION__ . '()');
    }
    $subform = $this->setSubFormDecorators($subForm);
    $subform = $this->addSubmitButtonGroupToSubForm($subForm);
    $subform = $this->addSubFormActions($subForm);
    $subform->setMethod($this->getMethod());
    return $subForm;
}