~ubuntu-branches/debian/experimental/php-nette/experimental

« back to all changes in this revision

Viewing changes to Nette-2.1.0RC/Nette/Forms/Controls/BaseControl.php

  • Committer: Package Import Robot
  • Author(s): David Prévot
  • Date: 2013-11-30 08:47:54 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20131130084754-4udf1xsu9085tnfc
Tags: 2.1.0~rc-1
* New upstream branch
* Update copyright

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
 
 
3
/**
 
4
 * This file is part of the Nette Framework (http://nette.org)
 
5
 *
 
6
 * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
 
7
 *
 
8
 * For the full copyright and license information, please view
 
9
 * the file license.txt that was distributed with this source code.
 
10
 */
 
11
 
 
12
namespace Nette\Forms\Controls;
 
13
 
 
14
use Nette,
 
15
        Nette\Forms\IControl,
 
16
        Nette\Utils\Html,
 
17
        Nette\Forms\Form,
 
18
        Nette\Forms\Rule;
 
19
 
 
20
 
 
21
/**
 
22
 * Base class that implements the basic functionality common to form controls.
 
23
 *
 
24
 * @author     David Grudl
 
25
 *
 
26
 * @property-read Nette\Forms\Form $form
 
27
 * @property-read string $htmlName
 
28
 * @property   string $htmlId
 
29
 * @property-read array $options
 
30
 * @property   Nette\Localization\ITranslator|NULL $translator
 
31
 * @property   mixed $value
 
32
 * @property-read bool $filled
 
33
 * @property-write $defaultValue
 
34
 * @property   bool $disabled
 
35
 * @property   bool $omitted
 
36
 * @property-read Nette\Utils\Html $control
 
37
 * @property-read Nette\Utils\Html $label
 
38
 * @property-read Nette\Utils\Html $controlPrototype
 
39
 * @property-read Nette\Utils\Html $labelPrototype
 
40
 * @property-read Nette\Forms\Rules $rules
 
41
 * @property   bool $required
 
42
 * @property-read array $errors
 
43
 */
 
44
abstract class BaseControl extends Nette\ComponentModel\Component implements IControl
 
45
{
 
46
        /** @var string */
 
47
        public static $idMask = 'frm-%s';
 
48
 
 
49
        /** @var string textual caption or label */
 
50
        public $caption;
 
51
 
 
52
        /** @var mixed current control value */
 
53
        protected $value;
 
54
 
 
55
        /** @var Nette\Utils\Html  control element template */
 
56
        protected $control;
 
57
 
 
58
        /** @var Nette\Utils\Html  label element template */
 
59
        protected $label;
 
60
 
 
61
        /** @var array */
 
62
        private $errors = array();
 
63
 
 
64
        /** @var bool */
 
65
        protected $disabled = FALSE;
 
66
 
 
67
        /** @var bool */
 
68
        private $omitted = FALSE;
 
69
 
 
70
        /** @var Nette\Forms\Rules */
 
71
        private $rules;
 
72
 
 
73
        /** @var Nette\Localization\ITranslator */
 
74
        private $translator = TRUE; // means autodetect
 
75
 
 
76
        /** @var array user options */
 
77
        private $options = array();
 
78
 
 
79
 
 
80
        /**
 
81
         * @param  string  caption
 
82
         */
 
83
        public function __construct($caption = NULL)
 
84
        {
 
85
                $this->monitor('Nette\Forms\Form');
 
86
                parent::__construct();
 
87
                $this->control = Html::el('input', array('type' => NULL, 'name' => NULL));
 
88
                $this->label = Html::el('label');
 
89
                $this->caption = $caption;
 
90
                $this->rules = new Nette\Forms\Rules($this);
 
91
                $this->setValue(NULL);
 
92
        }
 
93
 
 
94
 
 
95
        /**
 
96
         * This method will be called when the component becomes attached to Form.
 
97
         * @param  Nette\ComponentModel\IComponent
 
98
         * @return void
 
99
         */
 
100
        protected function attached($form)
 
101
        {
 
102
                if (!$this->isDisabled() && $form instanceof Form && $form->isAnchored() && $form->isSubmitted()) {
 
103
                        $this->loadHttpData();
 
104
                }
 
105
        }
 
106
 
 
107
 
 
108
        /**
 
109
         * Returns form.
 
110
         * @param  bool   throw exception if form doesn't exist?
 
111
         * @return Nette\Forms\Form
 
112
         */
 
113
        public function getForm($need = TRUE)
 
114
        {
 
115
                return $this->lookup('Nette\Forms\Form', $need);
 
116
        }
 
117
 
 
118
 
 
119
        /**
 
120
         * Loads HTTP data.
 
121
         * @return void
 
122
         */
 
123
        public function loadHttpData()
 
124
        {
 
125
                $this->setValue($this->getHttpData());
 
126
        }
 
127
 
 
128
 
 
129
        /**
 
130
         * Loads HTTP data.
 
131
         * @return mixed
 
132
         */
 
133
        public function getHttpData($type = Form::DATA_TEXT, $htmlTail = NULL)
 
134
        {
 
135
                return $this->getForm()->getHttpData($this->getHtmlName() . $htmlTail, $type);
 
136
        }
 
137
 
 
138
 
 
139
        /**
 
140
         * Returns HTML name of control.
 
141
         * @return string
 
142
         */
 
143
        public function getHtmlName()
 
144
        {
 
145
                return Nette\Forms\Helpers::generateHtmlName($this->lookupPath('Nette\Forms\Form'));
 
146
        }
 
147
 
 
148
 
 
149
        /********************* interface IFormControl ****************d*g**/
 
150
 
 
151
 
 
152
        /**
 
153
         * Sets control's value.
 
154
         * @return self
 
155
         */
 
156
        public function setValue($value)
 
157
        {
 
158
                $this->value = $value;
 
159
                return $this;
 
160
        }
 
161
 
 
162
 
 
163
        /**
 
164
         * Returns control's value.
 
165
         * @return mixed
 
166
         */
 
167
        public function getValue()
 
168
        {
 
169
                return $this->value;
 
170
        }
 
171
 
 
172
 
 
173
        /**
 
174
         * Is control filled?
 
175
         * @return bool
 
176
         */
 
177
        public function isFilled()
 
178
        {
 
179
                $value = $this->getValue();
 
180
                return $value !== NULL && $value !== array() && $value !== '';
 
181
        }
 
182
 
 
183
 
 
184
        /**
 
185
         * Sets control's default value.
 
186
         * @return self
 
187
         */
 
188
        public function setDefaultValue($value)
 
189
        {
 
190
                $form = $this->getForm(FALSE);
 
191
                if ($this->isDisabled() || !$form || !$form->isAnchored() || !$form->isSubmitted()) {
 
192
                        $this->setValue($value);
 
193
                }
 
194
                return $this;
 
195
        }
 
196
 
 
197
 
 
198
        /**
 
199
         * Disables or enables control.
 
200
         * @param  bool
 
201
         * @return self
 
202
         */
 
203
        public function setDisabled($value = TRUE)
 
204
        {
 
205
                if ($this->disabled = (bool) $value) {
 
206
                        $this->omitted = TRUE;
 
207
                        $this->setValue(NULL);
 
208
                }
 
209
                return $this;
 
210
        }
 
211
 
 
212
 
 
213
        /**
 
214
         * Is control disabled?
 
215
         * @return bool
 
216
         */
 
217
        public function isDisabled()
 
218
        {
 
219
                return $this->disabled === TRUE;
 
220
        }
 
221
 
 
222
 
 
223
        /**
 
224
         * Sets whether control value is excluded from $form->getValues() result.
 
225
         * @param  bool
 
226
         * @return self
 
227
         */
 
228
        public function setOmitted($value = TRUE)
 
229
        {
 
230
                $this->omitted = (bool) $value;
 
231
                return $this;
 
232
        }
 
233
 
 
234
 
 
235
        /**
 
236
         * Is control value excluded from $form->getValues() result?
 
237
         * @return bool
 
238
         */
 
239
        public function isOmitted()
 
240
        {
 
241
                return $this->omitted;
 
242
        }
 
243
 
 
244
 
 
245
        /********************* rendering ****************d*g**/
 
246
 
 
247
 
 
248
        /**
 
249
         * Generates control's HTML element.
 
250
         * @return Nette\Utils\Html
 
251
         */
 
252
        public function getControl()
 
253
        {
 
254
                $this->setOption('rendered', TRUE);
 
255
 
 
256
                $rules = self::exportRules($this->rules);
 
257
                $el = clone $this->control;
 
258
                return $el->addAttributes(array(
 
259
                        'name' => $this->getHtmlName(),
 
260
                        'id' => $this->getHtmlId(),
 
261
                        'required' => $this->isRequired(),
 
262
                        'disabled' => $this->isDisabled(),
 
263
                ))->data('nette-rules', $rules ? Nette\Utils\Json::encode($rules) : NULL);
 
264
        }
 
265
 
 
266
 
 
267
        /**
 
268
         * Generates label's HTML element.
 
269
         * @param  string
 
270
         * @return Nette\Utils\Html
 
271
         */
 
272
        public function getLabel($caption = NULL)
 
273
        {
 
274
                $label = clone $this->label;
 
275
                $label->for = $this->getHtmlId();
 
276
                $label->setText($this->translate($caption === NULL ? $this->caption : $caption));
 
277
                return $label;
 
278
        }
 
279
 
 
280
 
 
281
        /**
 
282
         * Returns control's HTML element template.
 
283
         * @return Nette\Utils\Html
 
284
         */
 
285
        final public function getControlPrototype()
 
286
        {
 
287
                return $this->control;
 
288
        }
 
289
 
 
290
 
 
291
        /**
 
292
         * Returns label's HTML element template.
 
293
         * @return Nette\Utils\Html
 
294
         */
 
295
        final public function getLabelPrototype()
 
296
        {
 
297
                return $this->label;
 
298
        }
 
299
 
 
300
 
 
301
        /**
 
302
         * Changes control's HTML id.
 
303
         * @param  string new ID, or FALSE or NULL
 
304
         * @return self
 
305
         */
 
306
        public function setHtmlId($id)
 
307
        {
 
308
                $this->control->id = $id;
 
309
                return $this;
 
310
        }
 
311
 
 
312
 
 
313
        /**
 
314
         * Returns control's HTML id.
 
315
         * @return string
 
316
         */
 
317
        public function getHtmlId()
 
318
        {
 
319
                if (!isset($this->control->id)) {
 
320
                        $this->control->id = sprintf(self::$idMask, $this->lookupPath(NULL));
 
321
                }
 
322
                return $this->control->id;
 
323
        }
 
324
 
 
325
 
 
326
        /**
 
327
         * Changes control's HTML attribute.
 
328
         * @param  string name
 
329
         * @param  mixed  value
 
330
         * @return self
 
331
         */
 
332
        public function setAttribute($name, $value = TRUE)
 
333
        {
 
334
                $this->control->$name = $value;
 
335
                return $this;
 
336
        }
 
337
 
 
338
 
 
339
        /********************* translator ****************d*g**/
 
340
 
 
341
 
 
342
        /**
 
343
         * Sets translate adapter.
 
344
         * @return self
 
345
         */
 
346
        public function setTranslator(Nette\Localization\ITranslator $translator = NULL)
 
347
        {
 
348
                $this->translator = $translator;
 
349
                return $this;
 
350
        }
 
351
 
 
352
 
 
353
        /**
 
354
         * Returns translate adapter.
 
355
         * @return Nette\Localization\ITranslator|NULL
 
356
         */
 
357
        final public function getTranslator()
 
358
        {
 
359
                if ($this->translator === TRUE) {
 
360
                        return $this->getForm(FALSE) ? $this->getForm()->getTranslator() : NULL;
 
361
                }
 
362
                return $this->translator;
 
363
        }
 
364
 
 
365
 
 
366
        /**
 
367
         * Returns translated string.
 
368
         * @param  string
 
369
         * @param  int      plural count
 
370
         * @return string
 
371
         */
 
372
        public function translate($s, $count = NULL)
 
373
        {
 
374
                $translator = $this->getTranslator();
 
375
                return $translator === NULL || $s == NULL || $s instanceof Html  // intentionally ==
 
376
                        ? $s
 
377
                        : $translator->translate((string) $s, $count);
 
378
        }
 
379
 
 
380
 
 
381
        /********************* rules ****************d*g**/
 
382
 
 
383
 
 
384
        /**
 
385
         * Adds a validation rule.
 
386
         * @param  mixed      rule type
 
387
         * @param  string     message to display for invalid data
 
388
         * @param  mixed      optional rule arguments
 
389
         * @return self
 
390
         */
 
391
        public function addRule($operation, $message = NULL, $arg = NULL)
 
392
        {
 
393
                $this->rules->addRule($operation, $message, $arg);
 
394
                return $this;
 
395
        }
 
396
 
 
397
 
 
398
        /**
 
399
         * Adds a validation condition a returns new branch.
 
400
         * @param  mixed     condition type
 
401
         * @param  mixed     optional condition arguments
 
402
         * @return Nette\Forms\Rules      new branch
 
403
         */
 
404
        public function addCondition($operation, $value = NULL)
 
405
        {
 
406
                return $this->rules->addCondition($operation, $value);
 
407
        }
 
408
 
 
409
 
 
410
        /**
 
411
         * Adds a validation condition based on another control a returns new branch.
 
412
         * @param  Nette\Forms\IControl form control
 
413
         * @param  mixed      condition type
 
414
         * @param  mixed      optional condition arguments
 
415
         * @return Nette\Forms\Rules      new branch
 
416
         */
 
417
        public function addConditionOn(IControl $control, $operation, $value = NULL)
 
418
        {
 
419
                return $this->rules->addConditionOn($control, $operation, $value);
 
420
        }
 
421
 
 
422
 
 
423
        /**
 
424
         * @return Nette\Forms\Rules
 
425
         */
 
426
        final public function getRules()
 
427
        {
 
428
                return $this->rules;
 
429
        }
 
430
 
 
431
 
 
432
        /**
 
433
         * Makes control mandatory.
 
434
         * @param  mixed  state or error message
 
435
         * @return self
 
436
         */
 
437
        public function setRequired($value = TRUE)
 
438
        {
 
439
                $this->rules->setRequired($value);
 
440
                return $this;
 
441
        }
 
442
 
 
443
 
 
444
        /**
 
445
         * Is control mandatory?
 
446
         * @return bool
 
447
         */
 
448
        final public function isRequired()
 
449
        {
 
450
                return $this->rules->isRequired();
 
451
        }
 
452
 
 
453
 
 
454
        /**
 
455
         * Performs the server side validation.
 
456
         * @return void
 
457
         */
 
458
        public function validate()
 
459
        {
 
460
                if ($this->isDisabled()) {
 
461
                        return;
 
462
                }
 
463
                $this->cleanErrors();
 
464
                $this->rules->validate();
 
465
        }
 
466
 
 
467
 
 
468
        /**
 
469
         * Adds error message to the list.
 
470
         * @param  string  error message
 
471
         * @return void
 
472
         */
 
473
        public function addError($message)
 
474
        {
 
475
                $this->errors[] = $message;
 
476
        }
 
477
 
 
478
 
 
479
        /**
 
480
         * Returns errors corresponding to control.
 
481
         * @return string
 
482
         */
 
483
        public function getError()
 
484
        {
 
485
                return $this->errors ? implode(' ', array_unique($this->errors)) : NULL;
 
486
        }
 
487
 
 
488
 
 
489
        /**
 
490
         * Returns errors corresponding to control.
 
491
         * @return array
 
492
         */
 
493
        public function getErrors()
 
494
        {
 
495
                return array_unique($this->errors);
 
496
        }
 
497
 
 
498
 
 
499
        /**
 
500
         * @return bool
 
501
         */
 
502
        public function hasErrors()
 
503
        {
 
504
                return (bool) $this->errors;
 
505
        }
 
506
 
 
507
 
 
508
        /**
 
509
         * @return void
 
510
         */
 
511
        public function cleanErrors()
 
512
        {
 
513
                $this->errors = array();
 
514
        }
 
515
 
 
516
 
 
517
        /**
 
518
         * @return array
 
519
         */
 
520
        protected static function exportRules($rules)
 
521
        {
 
522
                $payload = array();
 
523
                foreach ($rules as $rule) {
 
524
                        if (!is_string($op = $rule->operation)) {
 
525
                                if (!Nette\Utils\Callback::isStatic($op)) {
 
526
                                        continue;
 
527
                                }
 
528
                                $op = Nette\Utils\Callback::toString($op);
 
529
                        }
 
530
                        if ($rule->type === Rule::VALIDATOR) {
 
531
                                $item = array('op' => ($rule->isNegative ? '~' : '') . $op, 'msg' => $rules->formatMessage($rule, FALSE));
 
532
 
 
533
                        } elseif ($rule->type === Rule::CONDITION) {
 
534
                                $item = array(
 
535
                                        'op' => ($rule->isNegative ? '~' : '') . $op,
 
536
                                        'rules' => static::exportRules($rule->subRules),
 
537
                                        'control' => $rule->control->getHtmlName()
 
538
                                );
 
539
                                if ($rule->subRules->getToggles()) {
 
540
                                        $item['toggle'] = $rule->subRules->getToggles();
 
541
                                }
 
542
                        }
 
543
 
 
544
                        if (is_array($rule->arg)) {
 
545
                                foreach ($rule->arg as $key => $value) {
 
546
                                        $item['arg'][$key] = $value instanceof IControl ? array('control' => $value->getHtmlName()) : $value;
 
547
                                }
 
548
                        } elseif ($rule->arg !== NULL) {
 
549
                                $item['arg'] = $rule->arg instanceof IControl ? array('control' => $rule->arg->getHtmlName()) : $rule->arg;
 
550
                        }
 
551
 
 
552
                        $payload[] = $item;
 
553
                }
 
554
                return $payload;
 
555
        }
 
556
 
 
557
 
 
558
        /********************* validators ****************d*g**/
 
559
 
 
560
 
 
561
        /**
 
562
         * Equal validator: are control's value and second parameter equal?
 
563
         * @return bool
 
564
         */
 
565
        public static function validateEqual(IControl $control, $arg)
 
566
        {
 
567
                $value = $control->getValue();
 
568
                foreach ((is_array($value) ? $value : array($value)) as $val) {
 
569
                        foreach ((is_array($arg) ? $arg : array($arg)) as $item) {
 
570
                                if ((string) $val === (string) $item) {
 
571
                                        return TRUE;
 
572
                                }
 
573
                        }
 
574
                }
 
575
                return FALSE;
 
576
        }
 
577
 
 
578
 
 
579
        /**
 
580
         * Is control's value not equal with second parameter?
 
581
         * @return bool
 
582
         */
 
583
        public static function validateNotEqual(IControl $control, $arg)
 
584
        {
 
585
                return !static::validateEqual($control, $arg);
 
586
        }
 
587
 
 
588
 
 
589
        /**
 
590
         * Filled validator: is control filled?
 
591
         * @return bool
 
592
         */
 
593
        public static function validateFilled(IControl $control)
 
594
        {
 
595
                return $control->isFilled();
 
596
        }
 
597
 
 
598
 
 
599
        /**
 
600
         * Is control not filled?
 
601
         * @return bool
 
602
         */
 
603
        public static function validateBlank(IControl $control)
 
604
        {
 
605
                return !$control->isFilled();
 
606
        }
 
607
 
 
608
 
 
609
        /**
 
610
         * Valid validator: is control valid?
 
611
         * @return bool
 
612
         */
 
613
        public static function validateValid(IControl $control)
 
614
        {
 
615
                return $control->rules->validate();
 
616
        }
 
617
 
 
618
 
 
619
        /**
 
620
         * Rangle validator: is a control's value number in specified range?
 
621
         * @param  Nette\Forms\IControl
 
622
         * @param  array  min and max value pair
 
623
         * @return bool
 
624
         */
 
625
        public static function validateRange(IControl $control, $range)
 
626
        {
 
627
                return Nette\Utils\Validators::isInRange($control->getValue(), $range);
 
628
        }
 
629
 
 
630
 
 
631
        /**
 
632
         * Count/length validator. Range is array, min and max length pair.
 
633
         * @return bool
 
634
         */
 
635
        public static function validateLength(IControl $control, $range)
 
636
        {
 
637
                if (!is_array($range)) {
 
638
                        $range = array($range, $range);
 
639
                }
 
640
                $value = $control->getValue();
 
641
                return Nette\Utils\Validators::isInRange(is_array($value) ? count($value) : Nette\Utils\Strings::length($value), $range);
 
642
        }
 
643
 
 
644
 
 
645
        /**
 
646
         * Min-length validator: has control's value minimal count/length?
 
647
         * @return bool
 
648
         */
 
649
        public static function validateMinLength(IControl $control, $length)
 
650
        {
 
651
                return static::validateLength($control, array($length, NULL));
 
652
        }
 
653
 
 
654
 
 
655
        /**
 
656
         * Max-length validator: is control's value count/length in limit?
 
657
         * @return bool
 
658
         */
 
659
        public static function validateMaxLength(IControl $control, $length)
 
660
        {
 
661
                return static::validateLength($control, array(NULL, $length));
 
662
        }
 
663
 
 
664
 
 
665
        /********************* user data ****************d*g**/
 
666
 
 
667
 
 
668
        /**
 
669
         * Sets user-specific option.
 
670
         * @return self
 
671
         */
 
672
        public function setOption($key, $value)
 
673
        {
 
674
                if ($value === NULL) {
 
675
                        unset($this->options[$key]);
 
676
                } else {
 
677
                        $this->options[$key] = $value;
 
678
                }
 
679
                return $this;
 
680
        }
 
681
 
 
682
 
 
683
        /**
 
684
         * Returns user-specific option.
 
685
         * @return mixed
 
686
         */
 
687
        final public function getOption($key, $default = NULL)
 
688
        {
 
689
                return isset($this->options[$key]) ? $this->options[$key] : $default;
 
690
        }
 
691
 
 
692
 
 
693
        /**
 
694
         * Returns user-specific options.
 
695
         * @return array
 
696
         */
 
697
        final public function getOptions()
 
698
        {
 
699
                return $this->options;
 
700
        }
 
701
 
 
702
}