How to use Zend_Form->createElement()
Asked Answered
P

3

6

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.

Predation answered 5/7, 2011 at 0:42 Comment(0)
B
3

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.

Botulin answered 5/7, 2011 at 1:26 Comment(1)
Thanks Marcin, you were right - the second assignment is not required. When I went back and looked at my subforms from earlier in the day, they had the optional elements too.Predation
C
4

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).

Crites answered 5/7, 2011 at 1:31 Comment(3)
I think assignment to $this->id will execute __set method which in turn will execute addElement method to add element.Biotechnology
You are absolutely correct, I will correct my answer. I sometimes forget these devilish PHP wizardries ;)Crites
Hi Faken, thanks for an excellent answer. I awarded the points to Marcin, because he was more accurate, however your answer has some great info - and yes, I would appreciate further details on your method of element configuration only in the parent form...Predation
B
3

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.

Botulin answered 5/7, 2011 at 1:26 Comment(1)
Thanks Marcin, you were right - the second assignment is not required. When I went back and looked at my subforms from earlier in the day, they had the optional elements too.Predation
C
0

Assuming you are using Zend_Form::init() and not calling parent::init() in the sub class, you might be overriding the method of the parent class. Meaning that none of your base elements are added to the subclass form. If you add parent::init() into the init() of the subclass, it will inherit the parent elements to the sub class form. Then, you can add and remove elements as needed for the subclass.

public function init()
{
    parent::init();
    /** Additional code here **/ 
}
Clam answered 5/7, 2011 at 1:4 Comment(2)
Thanks user828841. I am using the init() as recommended. Should have pointed this out in my original question. Have now updated the question...Predation
If your calling the parent::init() you do not need to use $this->addElement($this->getElement('id')); $this->getElement('id) implies that the element is already added to the form thus you don't need the redundancyClam

© 2022 - 2024 — McMap. All rights reserved.