Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Zend_Form->createElement()

I have a base User form that I am subclassing for each use case, i.e. Register, Edit, etc.

Some form elements are common to all use cases and for these I am using the form as an element factory, e.g. $this->addElement('text', 'first_name', array(...)). That works fine.

For elements which are required in only some use cases, I am creating them in the base form class, but not adding them, e.g. $this->createElement('text', 'id', array(...)). When I get to the subclass itself, that's when I actually add these optional elements.

Now, I thought that in the subclass I would be able to simply add the element using either:

$this->addElement($this->getElement('id'));

Or

$this->addElement($this->id);

But that's not the case. I get an exception saying I am trying to addElement(NULL).

The only way I can get the desired result is to specifically assign the create element to a member variable, then later use that variable name.

e.g. In the base form:

$this->id = $this->createElement('text', 'id', array(...));

Then in the sub class:

$this->addElement($this->id);

It seems to me that this should produce a variable name clash. If createElement is not naming my element 'id', what is it naming it?

EDIT

I am using the init() methods in both parent and child classes, and the child's init() calls parent init() as its first task.

like image 938
DatsunBing Avatar asked Jul 05 '11 00:07

DatsunBing


2 Answers

Everything is working as expected. There is no name clash because when you use $this->createElement(), the element you create is not actually kept anywhere. With this approach, you must explicitly keep the element in some variable (like a member variable), and then add it to the form element set using $this->addElement().

If you take a look at Zend_Form source code you will notice that:

1- When calling createElement(), the element is created and immediately returned; in other words, the element is not internally kept anywhere, so you must keep it yourself and add it to the form latter:

public function createElement($type, $name, $options = null)
{
    ...
    $element = new $class($name, $options);
    return $element;
}

2- When you call addElement(), the element is then added to the form, and internally kept in a protected member array called _elements. This is the same thing as doing:

$this->id = $this->createElement('text', 'id', array(...));

which magically calls addElement() (as pointed by user user594791 in his comment). You could also directly put the element in the _elements array, but I advise against this, because addElement() does some further processing. There are no name clashes because you are doing the same action twice (as pointed by Marcin in the other answer), and in the second time you are overwriting the element with itself.

Finally, I also advise against instantiating elements which you will not use (waste of resources and not very good programming practice). It is preferable to keep an associative array of configurations for possibly necessary elements in your parent class; then, in your sub-classes, you only instantiate the elements you really require, using the respective configuration arrays which were pre-stored in the parent class. An example:

class ParentForm extends Zend_Form {
    ...
    protected $_elementConfig;
    ...
    public function init() {
        ...
        // Add element configuration, but don't instantiate element right now
        $this->_elementConfig = array();
        // Element 1, a text input
        $this->elementConfig['myTextInput'] = array(
            'Text',
            array(
                'label' => 'Theme',
                'description' => 'Main application theme',
                'maxLength' => 128,
                'validators' => array(
                    array('StringLength', false, array('max' => 128))
                ),
                'filters' => array(
                    array('HtmlEntities')
                )
            )
        );
        // Element 2, a submit button
        $this->elementConfig['mySubmitButton'] = array(
           'Submit', 
           array(
               'label' => 'Save'
           )
        );
        // Element 3, something else
        ...
     }
     ...
}

class ChildForm extends ParentForm {
    public function init() {
        parent::__construct(); // Parent init() is called by the parent constructor
        ...
        // Will use the submit button
        $this->addElement(
            $this->_elementConfig['mySubmitButton'][0], // Type of element
            'mySubmitButton', // Name of element
            $this->_elementConfig['mySubmitButton'][1]  // Config of element
        );
    }
}

If you have many elements, and do not wish to pollute the parent init() method very much, I suggest you place the complete element definitions in an external file, which can be either a PHP file which returns a PHP array with the full configuration, a XML file, INI file, or any other format supported by Zend_Config (you can define the elements without a piece of actual PHP code - here -at the bottom of the page- is an example of defining an element with an INI file).

like image 131
faken Avatar answered Nov 07 '22 07:11

faken


createElement does not add an element to the form, it just creates it. Unless you add it to your form, the form will not know about it. That's why $this->id and
$this->getElement('id') don't work in your first example.

In your second example, you first add the newly created element to the form (i.e. $this->id = $this->createElement('text', 'id', array(...));) and then it seems you are adding it again (i.e. $this->addElement($this->id);). I believe that there won't be any name clash, as Zend_Form will just reassign it. Thus I think $this->addElement($this->id); is in fact not needed.

Hope this helps.

like image 36
Marcin Avatar answered Nov 07 '22 08:11

Marcin