1<?php
2/**
3 * Zend Framework (http://framework.zend.com/)
4 *
5 * @link      http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license   http://framework.zend.com/license/new-bsd New BSD License
8 */
9
10namespace Zend\Form\Element;
11
12use Zend\Form\ElementInterface;
13use Zend\Form\Exception\InvalidArgumentException;
14use Zend\Validator\Explode as ExplodeValidator;
15use Zend\Validator\InArray as InArrayValidator;
16use Zend\Validator\ValidatorInterface;
17
18class MultiCheckbox extends Checkbox
19{
20    /**
21     * Seed attributes
22     *
23     * @var array
24     */
25    protected $attributes = array(
26        'type' => 'multi_checkbox',
27    );
28
29    /**
30     * @var bool
31     */
32    protected $disableInArrayValidator = false;
33
34    /**
35     * @var bool
36     */
37    protected $useHiddenElement = false;
38
39    /**
40     * @var string
41     */
42    protected $uncheckedValue = '';
43
44    /**
45     * @var array
46     */
47    protected $valueOptions = array();
48
49    /**
50     * @return array
51     */
52    public function getValueOptions()
53    {
54        return $this->valueOptions;
55    }
56
57    /**
58     * @param  array $options
59     * @return MultiCheckbox
60     */
61    public function setValueOptions(array $options)
62    {
63        $this->valueOptions = $options;
64
65        // Update Explode validator haystack
66        if ($this->validator instanceof ExplodeValidator) {
67            $validator = $this->validator->getValidator();
68            $validator->setHaystack($this->getValueOptionsValues());
69        }
70
71        return $this;
72    }
73
74    /**
75     * @param string $key
76     * @return self
77     */
78    public function unsetValueOption($key)
79    {
80        if (isset($this->valueOptions[$key])) {
81            unset($this->valueOptions[$key]);
82        }
83
84        return $this;
85    }
86
87    /**
88     * Set options for an element. Accepted options are:
89     * - label: label to associate with the element
90     * - label_attributes: attributes to use when the label is rendered
91     * - value_options: list of values and labels for the select options
92     *
93     * @param  array|\Traversable $options
94     * @return MultiCheckbox|ElementInterface
95     * @throws InvalidArgumentException
96     */
97    public function setOptions($options)
98    {
99        parent::setOptions($options);
100
101        if (isset($this->options['value_options'])) {
102            $this->setValueOptions($this->options['value_options']);
103        }
104        // Alias for 'value_options'
105        if (isset($this->options['options'])) {
106            $this->setValueOptions($this->options['options']);
107        }
108        if (isset($this->options['disable_inarray_validator'])) {
109            $this->setDisableInArrayValidator($this->options['disable_inarray_validator']);
110        }
111
112        return $this;
113    }
114
115    /**
116     * Set a single element attribute
117     *
118     * @param  string $key
119     * @param  mixed  $value
120     * @return MultiCheckbox|ElementInterface
121     */
122    public function setAttribute($key, $value)
123    {
124        // Do not include the options in the list of attributes
125        // TODO: Deprecate this
126        if ($key === 'options') {
127            $this->setValueOptions($value);
128            return $this;
129        }
130        return parent::setAttribute($key, $value);
131    }
132
133    /**
134     * Set the flag to allow for disabling the automatic addition of an InArray validator.
135     *
136     * @param bool $disableOption
137     * @return Select
138     */
139    public function setDisableInArrayValidator($disableOption)
140    {
141        $this->disableInArrayValidator = (bool) $disableOption;
142        return $this;
143    }
144
145    /**
146     * Get the disable in array validator flag.
147     *
148     * @return bool
149     */
150    public function disableInArrayValidator()
151    {
152        return $this->disableInArrayValidator;
153    }
154
155    /**
156     * Get validator
157     *
158     * @return ValidatorInterface
159     */
160    protected function getValidator()
161    {
162        if (null === $this->validator && !$this->disableInArrayValidator()) {
163            $inArrayValidator = new InArrayValidator(array(
164                'haystack'  => $this->getValueOptionsValues(),
165                'strict'    => false,
166            ));
167            $this->validator = new ExplodeValidator(array(
168                'validator'      => $inArrayValidator,
169                'valueDelimiter' => null, // skip explode if only one value
170            ));
171        }
172        return $this->validator;
173    }
174
175    /**
176     * Get only the values from the options attribute
177     *
178     * @return array
179     */
180    protected function getValueOptionsValues()
181    {
182        $values = array();
183        $options = $this->getValueOptions();
184        foreach ($options as $key => $optionSpec) {
185            $value = (is_array($optionSpec)) ? $optionSpec['value'] : $key;
186            $values[] = $value;
187        }
188        if ($this->useHiddenElement()) {
189            $values[] = $this->getUncheckedValue();
190        }
191        return $values;
192    }
193
194    /**
195     * Sets the value that should be selected.
196     *
197     * @param mixed $value The value to set.
198     * @return MultiCheckbox
199     */
200    public function setValue($value)
201    {
202        $this->value = $value;
203        return $this;
204    }
205}
206