1
2/*
3 +------------------------------------------------------------------------+
4 | Phalcon Framework                                                      |
5 +------------------------------------------------------------------------+
6 | Copyright (c) 2011-2017 Phalcon Team (https://phalconphp.com)          |
7 +------------------------------------------------------------------------+
8 | This source file is subject to the New BSD License that is bundled     |
9 | with this package in the file LICENSE.txt.                             |
10 |                                                                        |
11 | If you did not receive a copy of the license and are unable to         |
12 | obtain it through the world-wide-web, please send an email             |
13 | to license@phalconphp.com so we can send you a copy immediately.       |
14 +------------------------------------------------------------------------+
15 | Authors: Andres Gutierrez <andres@phalconphp.com>                      |
16 |          Eduar Carvajal <eduar@phalconphp.com>                         |
17 +------------------------------------------------------------------------+
18 */
19
20namespace Phalcon\Tag;
21
22use Phalcon\Tag\Exception;
23use Phalcon\Tag as BaseTag;
24use Phalcon\EscaperInterface;
25
26/**
27 * Phalcon\Tag\Select
28 *
29 * Generates a SELECT html tag using a static array of values or a Phalcon\Mvc\Model resultset
30 */
31abstract class Select
32{
33
34	/**
35	 * Generates a SELECT tag
36	 *
37	 * @param array parameters
38	 * @param array data
39	 */
40	public static function selectField(parameters, data = null)
41	{
42		var params, name, id, value, useEmpty, code, emptyValue, emptyText,
43			options, using;
44
45		if typeof parameters != "array" {
46			let params = [parameters, data];
47		} else {
48			let params = parameters;
49		}
50
51		if !fetch id, params[0] {
52			let params[0] = params["id"];
53		}
54
55		/**
56		 * Automatically assign the id if the name is not an array
57		 */
58		if !memstr(id, "[") {
59			if !isset params["id"] {
60				let params["id"] = id;
61			}
62		}
63
64		if !fetch name, params["name"] {
65			let params["name"] = id;
66		} else {
67			if !name {
68				let params["name"] = id;
69			}
70		}
71
72		if !fetch value, params["value"] {
73			let value = BaseTag::getValue(id, params);
74		} else {
75			unset params["value"];
76		}
77
78		if fetch useEmpty, params["useEmpty"] {
79
80			if !fetch emptyValue, params["emptyValue"] {
81				let emptyValue = "";
82			} else {
83				unset params["emptyValue"];
84			}
85
86			if !fetch emptyText, params["emptyText"] {
87				let emptyText = "Choose...";
88			} else {
89				unset params["emptyText"];
90			}
91
92			unset params["useEmpty"];
93		}
94
95		if !fetch options, params[1] {
96			let options = data;
97		}
98
99		if typeof options == "object" {
100
101			/**
102			 * The options is a resultset
103			 */
104			if !fetch using, params["using"] {
105				throw new Exception("The 'using' parameter is required");
106			} else {
107				if typeof using != "array" && typeof using != "object" {
108					throw new Exception("The 'using' parameter should be an array");
109				}
110			}
111		}
112
113		unset params["using"];
114
115		let code = BaseTag::renderAttributes("<select", params) . ">" . PHP_EOL;
116
117		if useEmpty {
118			/**
119			 * Create an empty value
120			 */
121			let code .= "\t<option value=\"" . emptyValue . "\">" . emptyText . "</option>" . PHP_EOL;
122		}
123
124		if typeof options == "object" {
125
126			/**
127			 * Create the SELECT's option from a resultset
128			 */
129			let code .= self::_optionsFromResultset(options, using, value, "</option>" . PHP_EOL);
130
131		} else {
132			if typeof options == "array" {
133
134				/**
135				 * Create the SELECT's option from an array
136				 */
137				let code .= self::_optionsFromArray(options, value, "</option>" . PHP_EOL);
138			} else {
139				throw new Exception("Invalid data provided to SELECT helper");
140			}
141		}
142
143		let code .= "</select>";
144
145		return code;
146	}
147
148	/**
149	 * Generate the OPTION tags based on a resultset
150	 *
151	 * @param \Phalcon\Mvc\Model\Resultset resultset
152	 * @param array using
153	 * @param mixed value
154	 * @param string closeOption
155	 */
156	private static function _optionsFromResultset(resultset, using, value, closeOption)
157	{
158		var code, params, option, usingZero, usingOne, escaper,
159			optionValue, optionText, strValue, strOptionValue;
160
161		let code = "";
162		let params = null;
163
164		if typeof using == "array" {
165			if count(using) != 2 {
166				throw new Exception("Parameter 'using' requires two values");
167			}
168			let usingZero = using[0], usingOne = using[1];
169		}
170
171		let escaper = <EscaperInterface> BaseTag::getEscaperService();
172
173		for option in iterator(resultset) {
174
175			if typeof using == "array" {
176
177				if typeof option == "object" {
178					if method_exists(option, "readAttribute") {
179						let optionValue = option->readAttribute(usingZero);
180						let optionText = option->readAttribute(usingOne);
181					} else {
182						let optionValue = option->usingZero;
183						let optionText = option->usingOne;
184					}
185				} else {
186					if typeof option == "array" {
187						let optionValue = option[usingZero];
188						let optionText = option[usingOne];
189					} else {
190						throw new Exception("Resultset returned an invalid value");
191					}
192				}
193
194				let optionValue = escaper->escapeHtmlAttr(optionValue);
195				let optionText = escaper->escapeHtml(optionText);
196
197				/**
198				 * If the value is equal to the option's value we mark it as selected
199				 */
200				if typeof value == "array" {
201					if in_array(optionValue, value) {
202						let code .= "\t<option selected=\"selected\" value=\"" . optionValue . "\">" . optionText . closeOption;
203					} else {
204						let code .= "\t<option value=\"" . optionValue . "\">" . optionText . closeOption;
205					}
206				} else {
207					let strOptionValue = (string) optionValue,
208						strValue = (string) value;
209					if strOptionValue === strValue {
210						let code .= "\t<option selected=\"selected\" value=\"" . strOptionValue . "\">" . optionText . closeOption;
211					} else {
212						let code .= "\t<option value=\"" . strOptionValue . "\">" . optionText . closeOption;
213					}
214				}
215			} else {
216
217				/**
218				 * Check if using is a closure
219				 */
220				if typeof using == "object" {
221					if params === null {
222						let params = [];
223					}
224					let params[0] = option;
225					let code .= call_user_func_array(using, params);
226				}
227			}
228		}
229
230		return code;
231	}
232
233	/**
234	 * Generate the OPTION tags based on an array
235	 *
236	 * @param array data
237	 * @param mixed value
238	 * @param string closeOption
239	 */
240	private static function _optionsFromArray(var data, var value, var closeOption)
241	{
242		var strValue, strOptionValue, code, optionValue, optionText, escaped;
243
244		let code = "";
245
246		for optionValue, optionText in data {
247
248			let escaped = htmlspecialchars(optionValue);
249
250			if typeof optionText == "array" {
251				let code .= "\t<optgroup label=\"" . escaped . "\">" . PHP_EOL . self::_optionsFromArray(optionText, value, closeOption) . "\t</optgroup>" . PHP_EOL;
252				continue;
253			}
254
255			if typeof value == "array" {
256				if in_array(optionValue, value) {
257					let code .= "\t<option selected=\"selected\" value=\"" . escaped . "\">" . optionText . closeOption;
258				} else {
259					let code .= "\t<option value=\"" . escaped . "\">" . optionText . closeOption;
260				}
261			} else {
262
263				let strOptionValue = (string) optionValue,
264					strValue = (string) value;
265
266				if strOptionValue === strValue {
267					let code .= "\t<option selected=\"selected\" value=\"" . escaped . "\">" . optionText . closeOption;
268				} else {
269					let code .= "\t<option value=\"" . escaped . "\">" . optionText . closeOption;
270				}
271			}
272		}
273
274		return code;
275	}
276}
277