1 <?php
2 /**
3 * FormInput.php
4 *
5 * @author Elvyrra SAS
6 * @license http://rem.mit-license.org/ MIT
7 */
8
9 namespace Hawk;
10
11 /**
12 * This class describes the general behavior of inputs in forms. This class is associated to Form class
13 *
14 * @package Form\Input
15 */
16 class FormInput{
17 use Utils;
18
19 /**
20 * Attributes properties that can be called at input initialization, and there type
21 *
22 * @static array $attr
23 */
24 public static $attr = array(
25 'checked' => 'bool',
26 'class' => 'text',
27 'cols' => 'int',
28 'disabled' => 'bool',
29 'id' => 'text',
30 'maxlength' => 'int',
31 'multiple' => 'bool',
32 'name' => 'text',
33 'placeholder' => 'html',
34 'readonly' => 'bool',
35 'rows' => 'int',
36 'size' => 'int',
37 'style' => 'text',
38 'title' => 'html',
39 'type' => 'text',
40 'value' => 'html',
41 'autocomplete' => 'text',
42 );
43
44 /**
45 * HTML input attributes, used to apply non listed attributes, as aria or data attributes
46 *
47 * @var array
48 */
49 public $attributes = array();
50
51 /**
52 * HTML class attribute
53 *
54 * @var string
55 */
56 public $class = '';
57
58 /**
59 * HTML title attribute
60 *
61 * @var string
62 */
63 public $title = '';
64
65 /**
66 * HTML style attribute
67 *
68 * @var string
69 */
70 public $style = '';
71
72 /**
73 * HTMl name attribute (required)
74 *
75 * @var string
76 */
77 public $name;
78
79 /**
80 * HTML id attribute. If not given, it will be generated form a uniqid and the name of the input
81 *
82 * @var string
83 */
84 public $id;
85
86 /**
87 * The default input value
88 *
89 * @var string
90 */
91
92 /**
93 * Field value
94 *
95 * @var string
96 */
97 public $value = '';
98
99 /**
100 * HTML placeholder attribute
101 *
102 * @var string
103 */
104 public $placeholder = '';
105
106 /**
107 * HTML maxlength attribute
108 *
109 * @var int
110 */
111 public $maxlength = 0;
112
113 /**
114 * HTML disabled attribute
115 *
116 * @var boolean
117 */
118 public $disabled = false;
119
120 /**
121 * HTML readonly attribute
122 *
123 * @var boolean
124 */
125 public $readonly = false;
126
127 /**
128 * Defines if the field is required or not
129 *
130 * @var boolean
131 */
132 public $required = false;
133
134 /**
135 * Define this property to display the errors on another input, giving the input name
136 * Can be used for example for hidden fields that value is dynamically filled client-side
137 *
138 * @var string
139 */
140 public $errorAt = '';
141
142 /**
143 * Defines if the field has to be displayed on a new line
144 *
145 * @var boolean
146 */
147 public $nl = true;
148
149 /**
150 * If set to true, the input won't be displayed. Can be usefull to hide a field in a specific usecase
151 *
152 * @var boolean
153 */
154 public $notDisplayed = false;
155
156 /**
157 * If set to true, the input will be displayed but with a display none
158 *
159 * @var boolean
160 */
161 public $hidden = false;
162
163 /**
164 * String to display before the input
165 *
166 * @var string
167 */
168 public $before = '';
169
170 /**
171 * String to display after the input
172 *
173 * @var string
174 */
175 public $after = '';
176
177 /**
178 * The label to display with the input
179 *
180 * @var string
181 */
182 public $label = '';
183
184 /**
185 * If set to true, the input will be displayed before the label (defaultly, it is displayed after)
186 *
187 * @var boolean
188 */
189 public $beforeLabel = false;
190
191 /**
192 * The style attribute to apply to the label
193 *
194 * @var string
195 */
196 public $labelStyle = '';
197
198 /**
199 * The regular expression the input value must valid
200 *
201 * @var string
202 */
203 public $pattern = '';
204
205 /**
206 * The input validators. Other parameters defaultly fills this property, but can be completed at the instanciation
207 *
208 * @var array
209 */
210 public $validators = array();
211
212 /**
213 * Mask to apply on the HTML input
214 *
215 * @var string
216 */
217 public $mask = '';
218
219 /**
220 * Defines if the value of this field has to be unique in the database
221 *
222 * @var boolean
223 */
224 public $unique = false;
225
226 /**
227 * The type of data in the database
228 *
229 * @var string
230 */
231 public $dataType = '';
232
233 /**
234 * The database field attached to the input
235 *
236 * @var string
237 */
238 public $field;
239
240
241 /**
242 * Define the value defined as "empty", default ""
243 *
244 * @var mixed
245 */
246 public $emptyValue = '';
247
248 /**
249 * Define the class on the label
250 *
251 * @var string
252 */
253 public $labelClass = '';
254
255 /**
256 * If set to true, then this field won't be searched and updating in the database
257 *
258 * @var boolean
259 */
260 public $independant = false;
261
262 /**
263 * Define if the input data has to be inserted in database when the form is submitted
264 *
265 * @var boolean
266 */
267 public $insert = true;
268
269 /**
270 * The input view filename
271 *
272 * @var string
273 */
274 public $tpl = '';
275
276 /**
277 * This constant is used to force the property $independant for all instances of a input class that extends this class
278 */
279 const INDEPENDANT = false;
280
281 /**
282 * Constructor
283 *
284 * @param array $param The input parameters. This arguments is an associative array where each key is the name of a property of this class
285 */
286 public function __construct($param) {
287
288 $this->setParam($param);
289
290 if(!isset($this->name)) {
291 $this->name = $this->field;
292 }
293
294 if(!isset($this->id)) {
295 $this->id = uniqid().'-'. str_replace(array('.', '#', '[', ']','>'), '-', $this->name);
296 }
297
298 $this->type = static::TYPE;
299
300 if(empty($this->tpl)) {
301 $theme = Theme::getSelected();
302
303 $file = Theme::getSelected()->getView(Form::VIEWS_DIR . 'form-input-' . static::TYPE . '.tpl');
304 $this->tpl = is_file($file) ? $file : Theme::getSelected()->getView(Form::VIEWS_DIR . 'form-input.tpl');
305 }
306 }
307
308
309 /**
310 * Set the input parameters
311 *
312 * @param string|array $param If set as a string, the name of the parameter to set. If an array, a set of parameters
313 * @param mixed $value The value to set, in case the parameter $param is a string
314 */
315 public function setParam($param, $value = null){
316 if(is_array($param)) {
317 $this->map($param);
318 }
319 else{
320 $this->$param = $value;
321 }
322 }
323
324
325 /**
326 * Create a input instance with it parameters
327 *
328 * @param array $param The input parameters.
329 * This arguments is an associative array where each key is the name of a property of this class
330 *
331 * @return FormInput The created instance
332 */
333 public static function create($param){
334 $class = get_called_class();
335
336 return new $class($param);
337 }
338
339
340 /**
341 * Display the input
342 *
343 * @return string the HTML result of the input displaying
344 */
345 public function __toString(){
346 return $this->display();
347 }
348
349 /**
350 * Display the input (alias method)
351 *
352 * @return string the HTML result of the input displaying
353 */
354 public function display(){
355 try{
356 $theme = Theme::getSelected();
357
358 if($this->name == $this->errorAt) {
359 unset($this->errorAt);
360 }
361
362 $inputLabel = $this->label ? View::make(
363 Theme::getSelected()->getView(Form::VIEWS_DIR . 'form-input-label.tpl'), array(
364 'input' => $this
365 )
366 ) : '';
367
368 $inputDisplay = View::make(
369 $this->tpl, array(
370 'input' => $this
371 )
372 );
373
374 return View::make(
375 Theme::getSelected()->getView(Form::VIEWS_DIR . 'form-input-block.tpl'), array(
376 'input' => $this,
377 'inputLabel' => $inputLabel,
378 'inputDisplay' => $inputDisplay
379 )
380 );
381 }
382 catch(\Exception $e){
383 App::errorHandler()->exception($e);
384 }
385 }
386
387 /**
388 * Check the submitted value of the input
389 *
390 * @param Form $form The form the input is associated with
391 *
392 * @return bool True if the submitted value is valid, else false
393 */
394 public function check(&$form = null){
395 if(empty($this->errorAt)) {
396 $this->errorAt = $this->name;
397 }
398
399 // Check, if the field is required, that a value was submitted
400 if(!empty($this->required) && ((string)$this->value == '' || $this->emptyValue && $this->value === $this->emptyValue)) {
401 // The field is required but not filled
402 $form && $form->error($this->errorAt, Lang::get('form.required-field'));
403 return false;
404 }
405
406 // Check the format of the field is correct
407 if(!empty($this->value) && !empty($this->pattern)) {
408 // test the format of the field
409 if(!preg_match($this->pattern, $this->value)) {
410 // the format of the field value is not correct
411 if ($form) {
412 $message = '';
413 if(isset($this->errorMessage)) {
414 $message = $this->errorMessage;
415 }
416 elseif(Lang::exists('form.'.static::TYPE."-format")) {
417 $message = Lang::get('form.'.static::TYPE."-format");
418 }
419 else {
420 $message = Lang::get('form.field-format');
421 }
422
423 $form->error($this->errorAt, $message);
424 }
425 return false;
426 }
427 }
428
429 // If the value of this field must be unique in the database, check this unicity is not compromised
430 if(!empty($this->value) && $this->unique && $form) {
431 $example = new DBExample(
432 array(
433 '$not' => $form->reference,
434 array($this->name => $this->dbvalue())
435 )
436 );
437
438 $model = $form->model;
439 if($model::getByExample($example)) {
440 // The field must be unique but is not
441 $form->error($this->errorAt, Lang::get('form.unique-field'));
442 return false;
443 }
444 }
445
446 // Check custom validators
447 if(!empty($this->validators)) {
448 foreach($this->validators as $validator){
449 $error = '';
450 if(is_callable($validator) && !$validator($this, $error)) {
451 $form->error($this->errorAt, $error);
452 return false;
453 }
454 }
455 }
456
457 // The field is correctly filled (for the common checks)
458 return true;
459 }
460
461 /**
462 * Return the value, formatted for the SQL database
463 *
464 * @return mixed The value, formatted for the database
465 */
466 public function dbvalue(){
467 switch(strtolower($this->dataType)){
468 case "boolean" :
469 case "bool" :
470 return (bool) $this->value;
471 break;
472
473 case "integer" :
474 return (int) $this->value;
475 break;
476
477 case "numeric" :
478 case "float" :
479 return (float) $this->value;
480 break;
481
482 default :
483 return $this->value;
484 break;
485 }
486 }
487
488
489 /**
490 * Set the value to the input
491 *
492 * @param mixed $value The value to set
493 */
494 public function setValue($value){
495 $this->value = $value;
496 }
497
498
499 /**
500 * Create an input from an array, automaticall finding it type
501 *
502 * @param array $parameters The input parameters, formatted as the constructor parameters, and including a 'type' data
503 *
504 * @return FormInput The input instance
505 */
506 public static function getInstance($parameters){
507 // Detect the class name to instance
508 if(!isset($parameters['type'])) {
509 $parameters['type'] = 'text';
510 }
511
512 $classname = ucwords($parameters['type']).'Input';
513
514 // Remove the 'type' data, only used to find out the classname
515 unset($parameters['type']);
516
517 // Create the instance
518 return new $classname($parameters);
519 }
520 }
521