Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Zend Framework Nested Checkboxes

I would like to create a Zend_Form with nested checkboxes. I'm sure that it's possible, but I don't remember how to do it. This is what it would look like:

[x] Top level checkbox 1
    [ ] Sub level 1
    [x] Sub level 2

[ ] Top level 2
    [ ] Sub level 1
    [ ] Sub level 2

How would I achieve this result?

like image 725
Andrew Avatar asked Nov 15 '22 06:11

Andrew


1 Answers

I have one solution this issue. It is the not best, but it is enough for me now.

in first you need valid array which you will pass to form. if you select data from database, your array will have similar structure:

array(
0=>array(
    'id'=>1,
    'name'=>'Top level 1',
    'parent_id'=>0
    )
1=>array(
    'id'=>2,
    'name'=>'Top level 2',
    'parent_id'=>0
    )
2=>array(
    'id'=>3,
    'name'=>'Sub level 1',
    'parent_id'=>1
    )
3=>array(
    'id'=>4,
    'name'=>'Sub level 2',
    'parent_id'=>1
    )
4=>array(
    'id'=>5,
    'name'=>'Sub level 1',
    'parent_id'=>2
    )
5=>array(
    'id'=>6,
    'name'=>'Sub level 1',
    'parent_id'=>2
    )
)

You need to transform this array. I have used filter for it:

class My_Filter_Array_ParentRelation implements Zend_Filter_Interface {


public function __construct() {
}

public function filter($arr) {
    $data = array();
    foreach ( $arr as $value ) {
        $data[$value['parent_id']][$value['id']] = $value['name'];
    }
    $data = $this->create($data, 0,0);

    return $data;
}

public function create($data, $pid=0,$l) {
    $new_data = array();
    if ( $data[$pid] ) {
        foreach ($data[$pid] as $id => $value) {
            $new_data[$id] = str_repeat('---',$l).$value;
            if(isset($id)) {
                $temp_data = $this->create($data, $id,$l+1);
                $new_data = $new_data + $temp_data;
            }
        }
    }

    return $new_data;
}
}

What does this filter do? it returns a sorted one-dimensional array relative to the parent element. Nested level is marked as "---". Example:

array(
1=>'Top level 1',
3=>'---Sub level 1',
4=>'---Sub level 2',
2=>'Top level 2',
5=>'---Sub level 1',
6=>'---Sub level 2'
)

Then I create form. It has specific element category_id. I will set view helper for it.

class My_Form_Product extends Zend_Form {

protected $_categories = array();

public function init() {
    $this
        ->addElement('multiCheckbox', 'category_id', array('label' => 'Category:', 'required' => true, 'multiOptions' => $this->getCategories()));

    $this->category_id->helper = 'MultiCheckbox';
}

public function setCategories($categories = array()) {
    $this->_categories = $categories;
    return $this;
}

public function getCategories() {
    return $this->_categories;
}
}

Then you need view helper code:

class My_View_Helper_MultiCheckbox extends Zend_View_Helper_FormElement
{

protected $_inputType = 'checkbox';


protected $_isArray = false;

public function MultiCheckbox($name, $value = null, $attribs = null,
    $options = null, $listsep = "\n")
{

    $info = $this->_getInfo($name, $value, $attribs, $options, $listsep);
    extract($info); // name, value, attribs, options, listsep, disable
    $listsep = "\n";

    // retrieve attributes for labels (prefixed with 'label_' or 'label')
    $label_attribs = array();
    foreach ($attribs as $key => $val) {
        $tmp    = false;
        $keyLen = strlen($key);
        if ((6 < $keyLen) && (substr($key, 0, 6) == 'label_')) {
            $tmp = substr($key, 6);
        } elseif ((5 < $keyLen) && (substr($key, 0, 5) == 'label')) {
            $tmp = substr($key, 5);
        }

        if ($tmp) {
            // make sure first char is lowercase
            $tmp[0] = strtolower($tmp[0]);
            $label_attribs[$tmp] = $val;
            unset($attribs[$key]);
        }
    }

    $labelPlacement = 'append';
    foreach ($label_attribs as $key => $val) {
        switch (strtolower($key)) {
            case 'placement':
                unset($label_attribs[$key]);
                $val = strtolower($val);
                if (in_array($val, array('prepend', 'append'))) {
                    $labelPlacement = $val;
                }
                break;
        }
    }

    // the radio button values and labels
    $options = (array) $options;

    // build the element
    $xhtml = '';
    $list  = array();

    // should the name affect an array collection?
    $name = $this->view->escape($name);
    if ($this->_isArray && ('[]' != substr($name, -2))) {
        $name .= '[]';
    }

    // ensure value is an array to allow matching multiple times
    $value = (array) $value;

    // XHTML or HTML end tag?
    $endTag = ' />';
    if (($this->view instanceof Zend_View_Abstract) && !$this->view->doctype()->isXhtml()) {
        $endTag= '>';
    }

    // add radio buttons to the list.
    $filter = new Zend_Filter_Alnum();
    foreach ($options as $opt_value => $opt_label) {

        // Should the label be escaped?
        if ($escape) {
            $opt_label = $this->view->escape($opt_label);
        }

        // is it disabled?
        $disabled = '';
        if (true === $disable) {
            $disabled = ' disabled="disabled"';
        } elseif (is_array($disable) && in_array($opt_value, $disable)) {
            $disabled = ' disabled="disabled"';
        }

        // is it checked?
        $checked = '';
        if (in_array($opt_value, $value)) {
            $checked = ' checked="checked"';
        }

        // generate ID
        $optId = $id . '-' . $filter->filter($opt_value);

        // Wrap the radios in labels
        $wrapper_start = '';
        $wrapper_end = '</label><br >';
        $level = substr_count($opt_label,'---');
        $opt_label = str_replace('---','',$opt_label);
        $label = '<label '. $this->_htmlAttribs($label_attribs) . ' for="' . $optId . '">' .$opt_label;
        $radio = (('prepend' == $labelPlacement) ? $label : '')
                . str_repeat('&nbsp;&nbsp;&nbsp;',$level).'<input type="' . $this->_inputType . '"'
                . ' name="' . $name . '"'
                . ' id="' . $optId . '"'
                . ' value="' . $this->view->escape($opt_value) . '"'
                . $checked
                . $disabled
                . $this->_htmlAttribs($attribs)
                . $endTag .' '
                . (('append' == $labelPlacement) ? $label : '');
        $text = $wrapper_start . $radio . $wrapper_end;

        // add to the array of radio buttons
        $list[] = $text;
    }

    // done!
    $xhtml .= implode($listsep, $list);

    return $xhtml;
}
}

This helper will find the mark '---' and set three spacers (&nbsp;) before checkbox.

So stay just display controller action. You get categories list from db, filter it in new array, then create form and send to form the filtered array:

$categories = Something::getCategories();
$filterCategory = new My_Filter_Array_ParentRelation();

$form = new Admin_Form_Product(array(
    'name' => 'Form',
    'categories' => $filterCategory->filter($categories->toArray()),
));

$this->view->form = $form;
like image 58
Vasily Avatar answered Dec 15 '22 08:12

Vasily