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 DateTime as PhpDateTime;
13use Traversable;
14use Zend\Form\Element;
15use Zend\Form\ElementPrepareAwareInterface;
16use Zend\Form\FormInterface;
17use Zend\InputFilter\InputProviderInterface;
18use Zend\Stdlib\ArrayUtils;
19use Zend\Validator\Regex as RegexValidator;
20use Zend\Validator\ValidatorInterface;
21
22class MonthSelect extends Element implements InputProviderInterface, ElementPrepareAwareInterface
23{
24    /**
25     * Select form element that contains values for month
26     *
27     * @var Select
28     */
29    protected $monthElement;
30
31    /**
32     * Select form element that contains values for year
33     *
34     * @var Select
35     */
36    protected $yearElement;
37
38    /**
39     * Min year to use for the select (default: current year - 100)
40     *
41     * @var int
42     */
43    protected $minYear;
44
45    /**
46     * Max year to use for the select (default: current year)
47     *
48     * @var int
49     */
50    protected $maxYear;
51
52    /**
53     * If set to true, it will generate an empty option for every select (this is mainly needed by most JavaScript
54     * libraries to allow to have a placeholder)
55     *
56     * @var bool
57     */
58    protected $createEmptyOption = false;
59
60    /**
61     * If set to true, view helpers will render delimiters between <select> elements, according to the
62     * specified locale
63     *
64     * @var bool
65     */
66    protected $renderDelimiters = true;
67
68    /**
69     * @var ValidatorInterface
70     */
71    protected $validator;
72
73    /**
74     * Constructor. Add two selects elements
75     *
76     * @param  null|int|string  $name    Optional name for the element
77     * @param  array            $options Optional options for the element
78     */
79    public function __construct($name = null, $options = array())
80    {
81        $this->minYear = date('Y') - 100;
82        $this->maxYear = date('Y');
83
84        $this->monthElement = new Select('month');
85        $this->yearElement = new Select('year');
86
87        parent::__construct($name, $options);
88    }
89
90    /**
91     * Set element options.
92     *
93     * Accepted options for MonthSelect:
94     *
95     * - month_attributes: HTML attributes to be rendered with the month element
96     * - year_attributes: HTML attributes to be rendered with the month element
97     * - min_year: min year to use in the year select
98     * - max_year: max year to use in the year select
99     *
100     * @param array|Traversable $options
101     * @return self
102     */
103    public function setOptions($options)
104    {
105        parent::setOptions($options);
106
107        if ($options instanceof Traversable) {
108            $options = ArrayUtils::iteratorToArray($options);
109        }
110
111        if (isset($options['month_attributes'])) {
112            $this->setMonthAttributes($options['month_attributes']);
113        }
114
115        if (isset($options['year_attributes'])) {
116            $this->setYearAttributes($options['year_attributes']);
117        }
118
119        if (isset($options['min_year'])) {
120            $this->setMinYear($options['min_year']);
121        }
122
123        if (isset($options['max_year'])) {
124            $this->setMaxYear($options['max_year']);
125        }
126
127        if (isset($options['create_empty_option'])) {
128            $this->setShouldCreateEmptyOption($options['create_empty_option']);
129        }
130
131        if (isset($options['render_delimiters'])) {
132            $this->setShouldRenderDelimiters($options['render_delimiters']);
133        }
134
135        return $this;
136    }
137
138    /**
139     * @return Select
140     */
141    public function getMonthElement()
142    {
143        return $this->monthElement;
144    }
145
146    /**
147     * @return Select
148     */
149    public function getYearElement()
150    {
151        return $this->yearElement;
152    }
153
154    /**
155     * Get both the year and month elements
156     *
157     * @return array
158     */
159    public function getElements()
160    {
161        return array($this->monthElement, $this->yearElement);
162    }
163
164    /**
165     * Set the month attributes
166     *
167     * @param  array $monthAttributes
168     * @return self
169     */
170    public function setMonthAttributes(array $monthAttributes)
171    {
172        $this->monthElement->setAttributes($monthAttributes);
173        return $this;
174    }
175
176    /**
177     * Get the month attributes
178     *
179     * @return array
180     */
181    public function getMonthAttributes()
182    {
183        return $this->monthElement->getAttributes();
184    }
185
186    /**
187     * Set the year attributes
188     *
189     * @param  array $yearAttributes
190     * @return self
191     */
192    public function setYearAttributes(array $yearAttributes)
193    {
194        $this->yearElement->setAttributes($yearAttributes);
195        return $this;
196    }
197
198    /**
199     * Get the year attributes
200     *
201     * @return array
202     */
203    public function getYearAttributes()
204    {
205        return $this->yearElement->getAttributes();
206    }
207
208    /**
209     * @param  int $minYear
210     * @return self
211     */
212    public function setMinYear($minYear)
213    {
214        $this->minYear = $minYear;
215        return $this;
216    }
217
218    /**
219     * @return int
220     */
221    public function getMinYear()
222    {
223        return $this->minYear;
224    }
225
226    /**
227     * @param  int $maxYear
228     * @return self
229     */
230    public function setMaxYear($maxYear)
231    {
232        $this->maxYear = $maxYear;
233        return $this;
234    }
235
236    /**
237     * @return int
238     */
239    public function getMaxYear()
240    {
241        return $this->maxYear;
242    }
243
244    /**
245     * @param  bool $createEmptyOption
246     * @return self
247     */
248    public function setShouldCreateEmptyOption($createEmptyOption)
249    {
250        $this->createEmptyOption = (bool) $createEmptyOption;
251        return $this;
252    }
253
254    /**
255     * @return bool
256     */
257    public function shouldCreateEmptyOption()
258    {
259        return $this->createEmptyOption;
260    }
261
262    /**
263     * @param  bool $renderDelimiters
264     * @return self
265     */
266    public function setShouldRenderDelimiters($renderDelimiters)
267    {
268        $this->renderDelimiters = (bool) $renderDelimiters;
269        return $this;
270    }
271
272    /**
273     * @return bool
274     */
275    public function shouldRenderDelimiters()
276    {
277        return $this->renderDelimiters;
278    }
279
280    /**
281     * @param mixed $value
282     * @return self
283     */
284    public function setValue($value)
285    {
286        if ($value instanceof PhpDateTime) {
287            $value = array(
288                'year'  => $value->format('Y'),
289                'month' => $value->format('m')
290            );
291        }
292
293        $this->yearElement->setValue($value['year']);
294        $this->monthElement->setValue($value['month']);
295        return $this;
296    }
297
298    /**
299     * @return string
300     */
301    public function getValue()
302    {
303        return sprintf(
304            '%s-%s',
305            $this->getYearElement()->getValue(),
306            $this->getMonthElement()->getValue()
307        );
308    }
309
310    /**
311     * Prepare the form element (mostly used for rendering purposes)
312     *
313     * @param  FormInterface $form
314     * @return void
315     */
316    public function prepareElement(FormInterface $form)
317    {
318        $name = $this->getName();
319        $this->monthElement->setName($name . '[month]');
320        $this->yearElement->setName($name . '[year]');
321    }
322
323    /**
324     * Get validator
325     *
326     * @return ValidatorInterface
327     */
328    protected function getValidator()
329    {
330        return new RegexValidator('/^[0-9]{4}\-(0?[1-9]|1[012])$/');
331    }
332
333    /**
334     * Should return an array specification compatible with
335     * {@link Zend\InputFilter\Factory::createInput()}.
336     *
337     * @return array
338     */
339    public function getInputSpecification()
340    {
341        return array(
342            'name' => $this->getName(),
343            'required' => false,
344            'filters' => array(
345                array('name' => 'MonthSelect'),
346            ),
347            'validators' => array(
348                $this->getValidator(),
349            ),
350        );
351    }
352
353    /**
354     * Clone the element (this is needed by Collection element, as it needs different copies of the elements)
355     */
356    public function __clone()
357    {
358        $this->monthElement = clone $this->monthElement;
359        $this->yearElement  = clone $this->yearElement;
360    }
361}
362