Zend_Form: addElementPrefixPath vs addPrefixPath

3.1k views Asked by At

I'm currently building my own set of Form elements with Zend_Form and want all my elements in my custom form to be able to use my custom Decorators

So i've created a custom form like this:

<?php
class Nuke_Form extends Zend_Form
{

    public function __construct($options = null)
    {
        $this->addElementPrefixPath('Nuke_Form_Decorator_TwitterBootstrap', 'Nuke/Form/Decorator/TwitterBootstrap/', 'decorator');

        parent::__construct($options);

    }

}

And for completeness sake, this is my Decorator

<?php
class Nuke_Form_Decorator_TwitterBootstrap_ControlGroup extends Zend_Form_Decorator_Abstract
{

    public function render($content)
    {
        $class = $this->getOption('class') ?: "control-group";

        $element = $this->getElement();
        $errors = $element->getMessages();
        if (!empty($errors)) {
            $class .= " error";
        }

        $output = '<div class="' . $class . '">' . $content . '</div>';

        return $output;
    }

}

and the concrete form implementation I'm creating in my Controllers

class App_Form_Index_Test extends Nuke_Form
{
    public function init()
    {
        parent::init();

        $this->addAttribs(array('class' => 'form-horizontal'));

        $username = new Nuke_Form_Element_Text('username');
        $username->setLabel("Gebruikersnaam")
                 ->setDescription("This is a description")
                 ->setRequired(true)
                 ->addValidator('NotEmpty', true)
                 ->addValidator('int');

        $submit = new Zend_Form_Element_Submit('submit');

        $this->addElements(array($username, $submit));
    }
}

But for some reason the Elements fail to find the Decorators and output the following exception:

Message: Plugin by name 'ControlGroup' was not found in the registry; used paths: Zend_Form_Decorator_: Zend/Form/Decorator/

Analyzing the exception I see my plugin path isn't added, even though I've added it explicitly by using addElementPrefixPath() in the Nuke_Form class.


The strange thing is, when I'm adding the PluginPath to each of my Custom Elements individually, it works flawlessly, like below.

<?php
class Nuke_Form_Element_Text extends Zend_Form_Element_Text
{
    public function init()
    {
        $this->addPrefixPath('Nuke_Form_Decorator_TwitterBootstrap', 'Nuke/Form/Decorator/TwitterBootstrap/', 'decorator');

        $this->addDecorators(array(
            array('ViewHelper', array(
                'helper' => 'formText'
            )),
            array('Errors'),
            array('Description', array(
                'placement' => Zend_Form_Decorator_Abstract::APPEND,
                'class' => 'help-block'
            )),
            array(array('controls' => 'HtmlTag'), array(
                'tag'   => 'div',
                'class' => 'controls',
            )),
            array('Label', array(
                'class' => 'control-label',
                'requiredSuffix' => ' *',
                'placement' => Zend_Form_Decorator_Abstract::PREPEND
            )),
            array('ControlGroup')
        ));
    }
}

I'm using the latest version of Zend Framework (v1.11.11).

After some research i've noticed that addElementPrefixPath() will add the path to all added form elements when invoked, I think this is why it won't work when calling it in the Nuke_Form constructor since at that time no elements have been added yet. But how are we supposed to use this method then? I've found several examples online that do call it in the constructor with success. I'm puzzled or overlooking something.

2

There are 2 answers

4
RockyFord On BEST ANSWER

try passing it in the constructor:
$options = array('elementPrefixPath' => 'Nuke/Form/Decorator/TwitterBootstrap/');

or maybe put it in init():

public function __construct($options = null)
{
    parent::__construct($options);

}
public function init() {
    $this->addElementPrefixPath('Nuke_Form_Decorator_TwitterBootstrap_', 'Nuke/Form/Decorator/TwitterBootstrap/', 'decorator');
}

}

I think calling it in init() will work best. And don't bother to call parent::init(); here, it's just an empty function in Zend_Form, nothing to override.

THIS DOES WORK

I used all of your code except the custom element, I set the elementPrefixPath in inti() and called ControlGroup Decorator using addDecorator() against a normal Zend_Form_Element, this is the output:

<form enctype="application/x-www-form-urlencoded" class="form-horizontal" method="post" action=""><dl class="zend_form">
<div class="control-group"><dt id="username-label"><label for="username" class="required">Gebruikersnaam</label></dt>
<dd id="username-element">
<input type="text" name="username" id="username" value="" />
<p class="description">This is a description</p></dd></div>
<dt id="submit-label">&#160;</dt><dd id="submit-element">
<input type="submit" name="submit" id="submit" value="submit" /></dd></dl></form>

the only change I made was to add a trailing _ to the prefix.

<?php
class Application_Form_NukeForm extends Zend_Form
{

    public function __construct($options = null)
    {

        parent::__construct($options);

    }
    public function init(){
        $this->addElementPrefixPath('Jgs_Decorator_', '/Jgs/Decorator/', 'decorator');
    }

}

of course I used my own path...

<?php
class Application_Form_Test extends Application_Form_NukeForm
{
    public function init()
    {
        parent::init();

        $this->addAttribs(array('class' => 'form-horizontal'));

        $username = new Zend_Form_Element_Text('username');
        $username->setLabel("Gebruikersnaam")
                 ->setDescription("This is a description")
                 ->setRequired(true)
                 ->addValidator('NotEmpty', true)
                 ->addValidator('int')
                ->addDecorator('ControlGroup');

        $submit = new Zend_Form_Element_Submit('submit');

        $this->addElements(array($username, $submit));
    }
}

I only renamed the decorator to fit my path

<?php
class Jgs_Decorator_ControlGroup extends Zend_Form_Decorator_Abstract
{

    public function render($content)
    {
        $class = $this->getOption('class') ?: "control-group";

        $element = $this->getElement();
        $errors = $element->getMessages();
        if (!empty($errors)) {
            $class .= " error";
        }

        $output = '<div class="' . $class . '">' . $content . '</div>';

        return $output;
    }

}

I think your problem is somewhere else...
I also got the __constructor to work, I had to adjust the parameters to get the correct options:

<?php

class Application_Form_NukeForm extends Zend_Form {

    public function __construct($options = array('elementPrefixPath' => array(
            'prefix' => 'Jgs_',
            'path' => '/Jgs/',
            'type' => 'decorator'
    ))) {

        parent::__construct($options);
    }

I really hope this helps :)

0
RockyFord On

in class Nuke_Form_Element_Text you might be better off if you called setDecorators() instead of addDecorators(), as it appears as though you are replacing all of the default decorators.
You could also just add $this->loadDefaultDecoratorsIsDisabled();