14
votes

I want know how to add custom attribute for option in a select field of Zend form.

PHP:

$option_values = array("multiOptions" => array(
    "US" => "United States",
    "CA" => "Canada",
));

$type=array('big','small');
$option= new Zend_Form_Element_Select('option',  $option_values);

HTML:

<select>
    <option value='US' type='big'>United States</option>
    <option value='CA' type='small'>Canada</option>
</select>

How to add this type attribute in the option?

6
type is not standard attributes that option can have, see w3schools.com/tags/tag_option.aspHaim Evgi
@Haim It could be data-type which is perfectly valid in HTML5. The actual attribute name is arbitrary in relation to this questionPhil

6 Answers

10
votes

It is not possible using ZF's implementation of Zend_Form_Element_Select. You need to create your own element. I have done something similar, here's the relevant code:

<?php
require_once 'Zend/Form/Element/Select.php';

/**
 * Select, but with the possibility to add attributes to <option>s
 * @author Dominik Marczuk
 */
class Zend_Form_Element_SelectAttribs extends Zend_Form_Element {

    public $options = array();

    public $helper = 'selectAttribs';

    /**
     * Adds a new <option>
     * @param string $value value (key) used internally
     * @param string $label label that is shown to the user
     * @param array $attribs additional attributes
     */
    public function addOption ($value,$label = '',$attribs = array()) {
        $value = (string) $value;
        if (!empty($label)) $label = (string) $label;
        else $label = $value;
        $this->options[$value] = array(
            'value' => $value,
            'label' => $label
        ) + $attribs;
        return $this;
    }
}

Put this into /library/Zend/Form/Element/SelectAttribs.php. You also need a helper to render the element. Put it into your view helpers directory, name it SelectAttribs.php as well. Here's the contents of my file:

<?php
require_once 'Zend/View/Helper/FormElement.php';

class Zend_View_Helper_SelectAttribs extends Zend_View_Helper_FormElement {
    public function selectAttribs($name, $value = null, $attribs = null, $options = null, $listsep = "<br />\n") {
        $info = $this->_getInfo($name, $value, $attribs, $options, $listsep);
        extract($info); // name, id, value, attribs, options, listsep, disable

        // force $value to array so we can compare multiple values to multiple
        // options; also ensure it's a string for comparison purposes.
        $value = array_map('strval', (array) $value);

        // now start building the XHTML.
        $disabled = '';
        if (true === $disable) {
            $disabled = ' disabled="disabled"';
        }

        // Build the surrounding select element first.
        $xhtml = '<select'
                . ' name="' . $this->view->escape($name) . '"'
                . ' id="' . $this->view->escape($id) . '"'
                . $disabled
                . $this->_htmlAttribs($attribs)
                . ">\n  ";

        // build the list of options
        $list = array();
        $translator = $this->getTranslator();
        foreach ($options as $opt_value => $option) {
            $opt_disable = '';
            if (is_array($disable) && in_array($opt_value, $disable)) {
                $opt_disable = ' disabled="disabled"';
            }
            $list[] = $this->_build($option, $disabled);
        }

        // add the options to the xhtml and close the select
        $xhtml .= implode("\n   ", $list) . "\n</select>";

        return $xhtml;
    }

    protected function _build($option, $disabled) {
        $html = '<option';
        foreach ($option as $attrib => $value) {
            $html .= " $attrib=\"$value\"";
        }
        return $html.$disabled.">".$option['label']."</option>";
    }
}

With this, you should be ready to go:

$elt = new Zend_Form_Element_SelectAttribs('whatever');
$elt->addOption($value,$label,array('attribname' => 'attribvalue'));
4
votes

Using addMultiOption($value,$label) I just set the value parameter to something like:

$value = $id . '" ref="' . $ref;

and when it renders you get:

<option value="<idValue>" ref="<refValue"><labelValue></option>

Hope this helps....

Okay, value gets escaped but optionClasses does not so inside the loop that adds the addMultiOptions(val,lable) I do something like this:

$optionClasses[<val>] = 'ref_' . <val> . '" ref="' . <ref>;

and then after the loop just do a setAttrib('optionClasses',$optionClasses)

And that actually works...

I answered this for another question but could not find a way to add a comment here to reference it; It was Zend Framework addMultiOption adding custom parameters like "rel" for options

2
votes

I didn't find @mingos's answer complete and had some issues with implementing setting the value. His answer helped a lot with what needed to be extended and changed however. Once I had that start the rest was pretty easy. I just extended ZF1's Select and overrode where I needed:

/**
 * Select, now with abbility to specify attributes on <Option>, addMultiOption has new syntax
 * @author Seth Miller
 */
class MyNamespace_Form_Element_SelectAttribs extends Zend_Form_Element_Select {

    public $options = array();
    public $helper = 'selectAttribs';

    /**
     * Add an option
     *
     * @param  string $option
     * @param  string $value
     * @return Zend_Form_Element_Multi
     */
    public function addMultiOption($value, $label = '', $attribs = array()) {
        $value = (string) $value;
        if (!empty($label)) {
            $label = (string) $label;
        }
        else {
            $label = $value;
        }

        $this->_getMultiOptions();
        if (!$this->_translateOption($value, $label)) {
            $this->options[$value] = array(
                'value'  => $value,
                'label'  => $label
                    ) + $attribs;
        }
        return $this;
    }

    /**
     * Add many options at once
     *
     * @param  array $options
     * @return Zend_Form_Element_Multi
     */
    public function addMultiOptions(array $options) {
        foreach ($options as $optionKey => $optionProperties) {
            if (is_array($optionProperties)
                    && array_key_exists('key', $optionProperties)
                    && array_key_exists('value', $optionProperties)
            ) {
                if(array_key_exists('key', $optionProperties)) $optionKey = $optionProperties['key'];
                $this->addMultiOption($optionKey, $optionProperties['value'], $optionProperties['attribs']);
            }
            else {
                $this->addMultiOption($optionKey, $optionProperties);
            }
        }
        return $this;
    }

    public function isValid($value, $context = null)
    {
        if ($this->registerInArrayValidator()) {
            if (!$this->getValidator('InArray')) {
                $multiOptions = $this->getMultiOptions();
                $options      = array();

                foreach ($multiOptions as $optionKey => $optionData) {
                    // optgroup instead of option label
                    if (is_array($optionData['options'])) {
                        $options = array_merge($options, array_keys($optionData['options']));
                    }
                    else {
                        $options[] = $optionKey;
                    }
                }

                $this->addValidator(
                    'InArray',
                    true,
                    array($options)
                );
            }
        }
        return parent::isValid($value, $context);
    }
}

And the view helper:

class MyNamespace_View_Helper_SelectAttribs extends Zend_View_Helper_FormElement {

public function selectAttribs($name, $value = null, $attribs = null, $options = null, $listsep = "<br />\n") {
    $info = $this->_getInfo($name, $value, $attribs, $options, $listsep);
    extract($info); // name, id, value, attribs, options, listsep, disable
    // force $value to array so we can compare multiple values to multiple
    // options; also ensure it's a string for comparison purposes.
    $value = array_map('strval', (array) $value);

    // check if element may have multiple values
    $multiple = '';

    if (substr($name, -2) == '[]') {
        // multiple implied by the name
        $multiple = ' multiple="multiple"';
    }

    if (isset($attribs['multiple'])) {
        // Attribute set
        if ($attribs['multiple']) {
            // True attribute; set multiple attribute
            $multiple = ' multiple="multiple"';

            // Make sure name indicates multiple values are allowed
            if (!empty($multiple) && (substr($name, -2) != '[]')) {
                $name .= '[]';
            }
        }
        else {
            // False attribute; ensure attribute not set
            $multiple = '';
        }
        unset($attribs['multiple']);
    }

    // now start building the XHTML.
    $disabled = '';
    if (true === $disable) {
        $disabled = ' disabled="disabled"';
    }

    // Build the surrounding select element first.
    $xhtml = '<select'
            .' name="'.$this->view->escape($name).'"'
            .' id="'.$this->view->escape($id).'"'
            .$multiple
            .$disabled
            .$this->_htmlAttribs($attribs)
            .">\n    ";

    // build the list of options
    $list = array();
    $translator = $this->getTranslator();
    foreach ((array) $options as $optionKey => $optionData) {
        if (isset($optionData['options'])) {
            $optDisable = '';
            if (is_array($disable) && in_array($optionData['value'], $disable)) {
                $optDisable = ' disabled="disabled"';
            }
            if (null !== $translator) {
                $optValue    = $translator->translate($optionData['value']);
            }
            $optId       = ' id="'.$this->view->escape($id).'-optgroup-'
                    .$this->view->escape($optionData['value']).'"';
            $list[]      = '<optgroup'
                    .$optDisable
                    .$optId
                    .' label="'.$this->view->escape($optionData['value']).'">';
            foreach ($optionData['options'] as $optionKey2 => $optionData2) {
                $list[]  = $this->_build($optionKey2, $optionData2, $value, $disable);
            }
            $list[]  = '</optgroup>';
        }
        else {
            $list[] = $this->_build($optionKey, $optionData, $value, $disable);
        }
    }

    // add the options to the xhtml and close the select
    $xhtml .= implode("\n    ", $list)."\n</select>";

    return $xhtml;
}

/**
 * Builds the actual <option> tag
 *
 * @param string $value Options Value
 * @param string $label Options Label
 * @param array  $selected The option value(s) to mark as 'selected'
 * @param array|bool $disable Whether the select is disabled, or individual options are
 * @return string Option Tag XHTML
 */
protected function _build($optionKey, $optionData, $selected, $disable)
{
    if (is_bool($disable)) {
        $disable = array();
    }

    $opt = '<option';

    foreach ($optionData as $attrib => $attribValue) {
        $opt .= ' '.$this->view->escape($attrib).'="'.$this->view->escape($attribValue).'"';
    }

    // selected?
    if (in_array((string) $optionData['value'], $selected)) {
        $opt .= ' selected="selected"';
    }

    // disabled?
    if (in_array($optionData['value'], $disable)) {
        $opt .= ' disabled="disabled"';
    }

    $opt .= '>' . $this->view->escape($optionData['label']) . "</option>";

    return $opt;
}

}

And implementation in a form would be something like:

$selectElement = new MyNamespace_Form_Element_SelectAttribs('selectElementName');
$selectElement->addMultiOption($value, $label, array('data-custom' => 'custom data embedded in option tag.');

I hope that helps someone. Thanks.

1
votes

mingos, you forgot about setvalue method of multiselect.

you shoul add something like:

...
foreach ($options as $opt_value => $option) {
            $opt_disable = '';
            $opt_selected = '';
            if (is_array($disable) && in_array($opt_value, $disable)) {
                $opt_disable = ' disabled="disabled"';
            }
            if (in_array($opt_value,$value)) {
                $opt_selected = ' selected="selected"';
            }
            $list[] = $this->_build($option, $disabled, $opt_selected);
        }
...
and

protected function _build($option, $disabled, $opt_selected) {
        $html = '<option';
        foreach ($option as $attrib => $value) {
            $html .= " $attrib=\"$value\"";
        }
        return $html . $disabled . $opt_selected . " foo>" . $option['label'] . "</option>";
    }
0
votes

You could extend / overwrite the Zend_View_Helper_FormSelect helper but the real problem is going to be getting the extra data into each option.

By default, Zend_Form_Element_Select (via Zend_Form_Element_Multi) expects two strings for each option, one for the value attribute and an optional one for the text content. You may need to create your own element to handle the extra data.

0
votes

No need for custom form element at all, what u can do is: $element->setAttrib('disable', array(1, 2, 5));

As explained at http://pietervogelaar.nl/set-attribute-on-select-option-with-zend_form/