views:

2307

answers:

3

I'm trying to build a form using the Zend_Form component, but the number of elements varies. The information for each Zend_Form element is stored in a database (name, options, validators, filters, etc.).

The application I'm working on consists of building surveys which contain a varying number of questions. Each question is associated with different arrays of answers. Ultimately my goal is to build arrays of radio/checkbox buttons, dynamically, server-side.

I'm looking for a pretty way to generate my form, but I'm not sure of the best way to load the model within the form. Should the model be loaded in the controller then passed (somehow, via a parameter?) directly to the form, or is it better to load the model within the Form init() method? Where's the best place to put the logic, should it be within the form class, or within the controller, or within the model?

My idea is to fetch form element properties (name, rules, filters, etc.) in the database, then iterate and finally render the form. What do you think of this approach? Ultimately, elements will be dynamically added (client-side), this time, using AJAX and a JavaScript library (such as jQuery).

Here are a couple useful links I found via Google, but I think they all answer a slightly different question than mine:

On building dynamic forms, server side:

On building dynamic forms, client side, with AJAX processing:

+1  A: 

You could extend Zend_Form.

Zend form is not good place for logic, only form representation.

So, Load all needed elements using model in controller and pass them to the form in contructor as parameters.

waney
A: 

Are you sure you're not trying to overuse Zend_Form? I'm also building survey applications with Zend Framework and I use Zend_Form for some basic forms here and there but for my questionnaire I use my own dynamic form factory because of exactly the problem you describe. I don't see an advantage in using Zend_Form for displaying varying arrays of radio buttons, etc. If you find a good solution with Zend_Form I would be interested.

tharkun
It might not be possible to do it with Zend_Form... I could as well just use Zend_Validate and render my own form. I wonder if it is possible to execute code within Zend_Form just before Zend_Form::init() is called : a single loop before this method would do it. I'm a beginner programmer, though !
Sorw
A: 

I think I found a possible solution, it involves passing an array of Zend Form elements to the Zend Form::__construct() method. The constructor takes an array of options, one of them is called "elements". Have a look at the source code within the Zend Framework library.

I coded a new private method within the controller, called buildSurveyForm(). Note : the object, passed as a parameter, is built from a huge SQL query with half a dozen JOIN statements, fetching data from a few tables (surveys, questions, answers, etc.) within the database. One of the public attributes for this class consists of an array of questions, stored as objects (with public methods/attributes as well, etc.). Same for answers. The code for building these classes is pretty trivial and out of topic here.

Here's the code within the survey controller. I copy/pasted and edited/dropped a few lines to make it a lot clearer :

private function buildSurveyForm(MyApp_Object_Survey $survey)
{
    foreach ($survey->questions as $question)
    {
        $element = new Zend_Form_Element_MultiCheckbox($question->order);
        $element->addMultiOptions($question->getAnswersLabels());
        $element->setName($question->order);
        $element->setLabel($question->title);

        $elements[] = $element;
    }

    // Here's the trick :
    $formOptions = array('elements' => $elements);

    $surveyForm = new MyApp_Survey_Form($formOptions);

    $urlHelper = $this->_helper->getHelper('url');
    $surveyForm->setAction($urlHelper->url(array(
            'controller' => 'survey',
            'action' => 'vote'),
        'default'
    ));
    $surveyForm->setMethod('post');

    $this->_forms['survey'] = $surveyForm;

    return $this->_forms['survey'];
}

The MyApp Survey Form class only contains a Submit button within the init() method. The dynamically generated elements with the code above are added BEFORE this submit button (which is unexpected, but useful). This class simply extends Zend_Form.

Then, within survey controller / view action :

public function viewAction()
{
    $surveyModel =    $this->_model['survey'];
    $survey =        $surveyModel->getFullSurvey($this->_getParam('id'));
    $survey =        new MyApp_Object_Survey($survey);
    // Calls above private method :
    $surveyForm =    $this->buildSurveyForm($survey);

    $this->view->assign(array(
         'surveyForm' => $surveyForm,
    ));
}

Adding filters, validators and decorators to form elements is now trivial. My proposal is a bit dirty, but I think it gets the job done. I will add a new proposal if I find something more elegant. Feel free to post different answers/solutions.

Sorw