CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Team
    • Issues (Github)
    • YouTube Channel
    • Get Involved
    • Bakery
    • Featured Resources
    • Newsletter
    • Certification
    • My CakePHP
    • CakeFest
    • Facebook
    • Twitter
    • Help & Support
    • Forum
    • Stack Overflow
    • IRC
    • Slack
    • Paid Support
CakePHP

C CakePHP 3.8 Red Velvet API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 3.8
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Namespaces

  • Cake
    • Auth
      • Storage
    • Cache
      • Engine
    • Collection
      • Iterator
    • Command
    • Console
      • Exception
    • Controller
      • Component
      • Exception
    • Core
      • Configure
        • Engine
      • Exception
      • Retry
    • Database
      • Driver
      • Exception
      • Expression
      • Schema
      • Statement
      • Type
    • Datasource
      • Exception
    • Error
      • Middleware
    • Event
      • Decorator
    • Filesystem
    • Form
    • Http
      • Client
        • Adapter
        • Auth
      • Cookie
      • Exception
      • Middleware
      • Session
    • I18n
      • Formatter
      • Middleware
      • Parser
    • Log
      • Engine
    • Mailer
      • Exception
      • Transport
    • Network
      • Exception
    • ORM
      • Association
      • Behavior
        • Translate
      • Exception
      • Locator
      • Rule
    • Routing
      • Exception
      • Filter
      • Middleware
      • Route
    • Shell
      • Helper
      • Task
    • TestSuite
      • Fixture
      • Stub
    • Utility
      • Exception
    • Validation
    • View
      • Exception
      • Form
      • Helper
      • Widget
  • None

Classes

  • BasicWidget
  • ButtonWidget
  • CheckboxWidget
  • DateTimeWidget
  • FileWidget
  • LabelWidget
  • MultiCheckboxWidget
  • NestingLabelWidget
  • RadioWidget
  • SelectBoxWidget
  • TextareaWidget
  • WidgetLocator

Interfaces

  • WidgetInterface
  1: <?php
  2: /**
  3:  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4:  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5:  *
  6:  * Licensed under The MIT License
  7:  * For full copyright and license information, please see the LICENSE.txt
  8:  * Redistributions of files must retain the above copyright notice.
  9:  *
 10:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 11:  * @link          https://cakephp.org CakePHP(tm) Project
 12:  * @since         3.0.0
 13:  * @license       https://opensource.org/licenses/mit-license.php MIT License
 14:  */
 15: namespace Cake\View\Widget;
 16: 
 17: use Cake\View\Form\ContextInterface;
 18: use Cake\View\StringTemplate;
 19: use DateTime;
 20: use Exception;
 21: use RuntimeException;
 22: 
 23: /**
 24:  * Input widget class for generating a date time input widget.
 25:  *
 26:  * This class is intended as an internal implementation detail
 27:  * of Cake\View\Helper\FormHelper and is not intended for direct use.
 28:  */
 29: class DateTimeWidget implements WidgetInterface
 30: {
 31:     /**
 32:      * Select box widget.
 33:      *
 34:      * @var \Cake\View\Widget\SelectBoxWidget
 35:      */
 36:     protected $_select;
 37: 
 38:     /**
 39:      * List of inputs that can be rendered
 40:      *
 41:      * @var string[]
 42:      */
 43:     protected $_selects = [
 44:         'year',
 45:         'month',
 46:         'day',
 47:         'hour',
 48:         'minute',
 49:         'second',
 50:         'meridian',
 51:     ];
 52: 
 53:     /**
 54:      * Template instance.
 55:      *
 56:      * @var \Cake\View\StringTemplate
 57:      */
 58:     protected $_templates;
 59: 
 60:     /**
 61:      * Constructor
 62:      *
 63:      * @param \Cake\View\StringTemplate $templates Templates list.
 64:      * @param \Cake\View\Widget\SelectBoxWidget $selectBox Selectbox widget instance.
 65:      */
 66:     public function __construct(StringTemplate $templates, SelectBoxWidget $selectBox)
 67:     {
 68:         $this->_select = $selectBox;
 69:         $this->_templates = $templates;
 70:     }
 71: 
 72:     /**
 73:      * Renders a date time widget
 74:      *
 75:      * - `name` - Set the input name.
 76:      * - `disabled` - Either true or an array of options to disable.
 77:      * - `val` - A date time string, integer or DateTime object
 78:      * - `empty` - Set to true to add an empty option at the top of the
 79:      *   option elements. Set to a string to define the display value of the
 80:      *   empty option.
 81:      *
 82:      * In addition to the above options, the following options allow you to control
 83:      * which input elements are generated. By setting any option to false you can disable
 84:      * that input picker. In addition each picker allows you to set additional options
 85:      * that are set as HTML properties on the picker.
 86:      *
 87:      * - `year` - Array of options for the year select box.
 88:      * - `month` - Array of options for the month select box.
 89:      * - `day` - Array of options for the day select box.
 90:      * - `hour` - Array of options for the hour select box.
 91:      * - `minute` - Array of options for the minute select box.
 92:      * - `second` - Set to true to enable the seconds input. Defaults to false.
 93:      * - `meridian` - Set to true to enable the meridian input. Defaults to false.
 94:      *   The meridian will be enabled automatically if you choose a 12 hour format.
 95:      *
 96:      * The `year` option accepts the `start` and `end` options. These let you control
 97:      * the year range that is generated. It defaults to +-5 years from today.
 98:      *
 99:      * The `month` option accepts the `name` option which allows you to get month
100:      * names instead of month numbers.
101:      *
102:      * The `hour` option allows you to set the following options:
103:      *
104:      * - `format` option which accepts 12 or 24, allowing
105:      *   you to indicate which hour format you want.
106:      * - `start` The hour to start the options at.
107:      * - `end` The hour to stop the options at.
108:      *
109:      * The start and end options are dependent on the format used. If the
110:      * value is out of the start/end range it will not be included.
111:      *
112:      * The `minute` option allows you to define the following options:
113:      *
114:      * - `interval` The interval to round options to.
115:      * - `round` Accepts `up` or `down`. Defines which direction the current value
116:      *   should be rounded to match the select options.
117:      *
118:      * @param array $data Data to render with.
119:      * @param \Cake\View\Form\ContextInterface $context The current form context.
120:      * @return string A generated select box.
121:      * @throws \RuntimeException When option data is invalid.
122:      */
123:     public function render(array $data, ContextInterface $context)
124:     {
125:         $data = $this->_normalizeData($data);
126: 
127:         $selected = $this->_deconstructDate($data['val'], $data);
128: 
129:         $templateOptions = ['templateVars' => $data['templateVars']];
130:         foreach ($this->_selects as $select) {
131:             if ($data[$select] === false || $data[$select] === null) {
132:                 $templateOptions[$select] = '';
133:                 unset($data[$select]);
134:                 continue;
135:             }
136:             if (!is_array($data[$select])) {
137:                 throw new RuntimeException(sprintf(
138:                     'Options for "%s" must be an array|false|null',
139:                     $select
140:                 ));
141:             }
142:             $method = "_{$select}Select";
143:             $data[$select]['name'] = $data['name'] . '[' . $select . ']';
144:             $data[$select]['val'] = $selected[$select];
145: 
146:             if (!isset($data[$select]['empty'])) {
147:                 $data[$select]['empty'] = $data['empty'];
148:             }
149:             if (!isset($data[$select]['disabled'])) {
150:                 $data[$select]['disabled'] = $data['disabled'];
151:             }
152:             if (isset($data[$select]['templateVars']) && $templateOptions['templateVars']) {
153:                 $data[$select]['templateVars'] = array_merge(
154:                     $templateOptions['templateVars'],
155:                     $data[$select]['templateVars']
156:                 );
157:             }
158:             if (!isset($data[$select]['templateVars'])) {
159:                 $data[$select]['templateVars'] = $templateOptions['templateVars'];
160:             }
161:             $templateOptions[$select] = $this->{$method}($data[$select], $context);
162:             unset($data[$select]);
163:         }
164:         unset($data['name'], $data['empty'], $data['disabled'], $data['val']);
165:         $templateOptions['attrs'] = $this->_templates->formatAttributes($data);
166: 
167:         return $this->_templates->format('dateWidget', $templateOptions);
168:     }
169: 
170:     /**
171:      * Normalize data.
172:      *
173:      * @param array $data Data to normalize.
174:      * @return array Normalized data.
175:      */
176:     protected function _normalizeData($data)
177:     {
178:         $data += [
179:             'name' => '',
180:             'empty' => false,
181:             'disabled' => null,
182:             'val' => null,
183:             'year' => [],
184:             'month' => [],
185:             'day' => [],
186:             'hour' => [],
187:             'minute' => [],
188:             'second' => [],
189:             'meridian' => null,
190:             'templateVars' => [],
191:         ];
192: 
193:         $timeFormat = isset($data['hour']['format']) ? $data['hour']['format'] : null;
194:         if ($timeFormat === 12 && !isset($data['meridian'])) {
195:             $data['meridian'] = [];
196:         }
197:         if ($timeFormat === 24) {
198:             $data['meridian'] = false;
199:         }
200: 
201:         return $data;
202:     }
203: 
204:     /**
205:      * Deconstructs the passed date value into all time units
206:      *
207:      * @param string|int|array|\DateTime|null $value Value to deconstruct.
208:      * @param array $options Options for conversion.
209:      * @return array
210:      */
211:     protected function _deconstructDate($value, $options)
212:     {
213:         if ($value === '' || $value === null) {
214:             return [
215:                 'year' => '', 'month' => '', 'day' => '',
216:                 'hour' => '', 'minute' => '', 'second' => '',
217:                 'meridian' => '',
218:             ];
219:         }
220:         try {
221:             if (is_string($value) && !is_numeric($value)) {
222:                 $date = new DateTime($value);
223:             } elseif (is_bool($value)) {
224:                 $date = new DateTime();
225:             } elseif (is_int($value) || is_numeric($value)) {
226:                 $date = new DateTime('@' . $value);
227:             } elseif (is_array($value)) {
228:                 $dateArray = [
229:                     'year' => '', 'month' => '', 'day' => '',
230:                     'hour' => '', 'minute' => '', 'second' => '',
231:                     'meridian' => '',
232:                 ];
233:                 $validDate = false;
234:                 foreach ($dateArray as $key => $dateValue) {
235:                     $exists = isset($value[$key]);
236:                     if ($exists) {
237:                         $validDate = true;
238:                     }
239:                     if ($exists && $value[$key] !== '') {
240:                         $dateArray[$key] = str_pad($value[$key], 2, '0', STR_PAD_LEFT);
241:                     }
242:                 }
243:                 if ($validDate) {
244:                     if (!isset($dateArray['second'])) {
245:                         $dateArray['second'] = 0;
246:                     }
247:                     if (!empty($value['meridian'])) {
248:                         $isAm = strtolower($dateArray['meridian']) === 'am';
249:                         $dateArray['hour'] = $isAm ? $dateArray['hour'] : $dateArray['hour'] + 12;
250:                     }
251:                     if (!empty($dateArray['minute']) && isset($options['minute']['interval'])) {
252:                         $dateArray['minute'] += $this->_adjustValue($dateArray['minute'], $options['minute']);
253:                         $dateArray['minute'] = str_pad((string)$dateArray['minute'], 2, '0', STR_PAD_LEFT);
254:                     }
255: 
256:                     return $dateArray;
257:                 }
258: 
259:                 $date = new DateTime();
260:             } else {
261:                 /* @var \DateTime $value */
262:                 $date = clone $value;
263:             }
264:         } catch (Exception $e) {
265:             $date = new DateTime();
266:         }
267: 
268:         if (isset($options['minute']['interval'])) {
269:             $change = $this->_adjustValue((int)$date->format('i'), $options['minute']);
270:             $date->modify($change > 0 ? "+$change minutes" : "$change minutes");
271:         }
272: 
273:         return [
274:             'year' => $date->format('Y'),
275:             'month' => $date->format('m'),
276:             'day' => $date->format('d'),
277:             'hour' => $date->format('H'),
278:             'minute' => $date->format('i'),
279:             'second' => $date->format('s'),
280:             'meridian' => $date->format('a'),
281:         ];
282:     }
283: 
284:     /**
285:      * Adjust $value based on rounding settings.
286:      *
287:      * @param int $value The value to adjust.
288:      * @param array $options The options containing interval and possibly round.
289:      * @return int The amount to adjust $value by.
290:      */
291:     protected function _adjustValue($value, $options)
292:     {
293:         $options += ['interval' => 1, 'round' => null];
294:         $changeValue = $value * (1 / $options['interval']);
295:         switch ($options['round']) {
296:             case 'up':
297:                 $changeValue = ceil($changeValue);
298:                 break;
299:             case 'down':
300:                 $changeValue = floor($changeValue);
301:                 break;
302:             default:
303:                 $changeValue = round($changeValue);
304:         }
305: 
306:         return ($changeValue * $options['interval']) - $value;
307:     }
308: 
309:     /**
310:      * Generates a year select
311:      *
312:      * @param array $options Options list.
313:      * @param \Cake\View\Form\ContextInterface $context The current form context.
314:      * @return string
315:      */
316:     protected function _yearSelect($options, $context)
317:     {
318:         $options += [
319:             'name' => '',
320:             'val' => null,
321:             'start' => date('Y', strtotime('-5 years')),
322:             'end' => date('Y', strtotime('+5 years')),
323:             'order' => 'desc',
324:             'templateVars' => [],
325:             'options' => []
326:         ];
327: 
328:         if (!empty($options['val'])) {
329:             $options['start'] = min($options['val'], $options['start']);
330:             $options['end'] = max($options['val'], $options['end']);
331:         }
332:         if (empty($options['options'])) {
333:             $options['options'] = $this->_generateNumbers($options['start'], $options['end']);
334:         }
335:         if ($options['order'] === 'desc') {
336:             $options['options'] = array_reverse($options['options'], true);
337:         }
338:         unset($options['start'], $options['end'], $options['order']);
339: 
340:         return $this->_select->render($options, $context);
341:     }
342: 
343:     /**
344:      * Generates a month select
345:      *
346:      * @param array $options The options to build the month select with
347:      * @param \Cake\View\Form\ContextInterface $context The current form context.
348:      * @return string
349:      */
350:     protected function _monthSelect($options, $context)
351:     {
352:         $options += [
353:             'name' => '',
354:             'names' => false,
355:             'val' => null,
356:             'leadingZeroKey' => true,
357:             'leadingZeroValue' => false,
358:             'templateVars' => [],
359:         ];
360: 
361:         if (empty($options['options'])) {
362:             if ($options['names'] === true) {
363:                 $options['options'] = $this->_getMonthNames($options['leadingZeroKey']);
364:             } elseif (is_array($options['names'])) {
365:                 $options['options'] = $options['names'];
366:             } else {
367:                 $options['options'] = $this->_generateNumbers(1, 12, $options);
368:             }
369:         }
370: 
371:         unset($options['leadingZeroKey'], $options['leadingZeroValue'], $options['names']);
372: 
373:         return $this->_select->render($options, $context);
374:     }
375: 
376:     /**
377:      * Generates a day select
378:      *
379:      * @param array $options The options to generate a day select with.
380:      * @param \Cake\View\Form\ContextInterface $context The current form context.
381:      * @return string
382:      */
383:     protected function _daySelect($options, $context)
384:     {
385:         $options += [
386:             'name' => '',
387:             'val' => null,
388:             'leadingZeroKey' => true,
389:             'leadingZeroValue' => false,
390:             'templateVars' => [],
391:         ];
392:         $options['options'] = $this->_generateNumbers(1, 31, $options);
393: 
394:         unset($options['names'], $options['leadingZeroKey'], $options['leadingZeroValue']);
395: 
396:         return $this->_select->render($options, $context);
397:     }
398: 
399:     /**
400:      * Generates a hour select
401:      *
402:      * @param array $options The options to generate an hour select with
403:      * @param \Cake\View\Form\ContextInterface $context The current form context.
404:      * @return string
405:      */
406:     protected function _hourSelect($options, $context)
407:     {
408:         $options += [
409:             'name' => '',
410:             'val' => null,
411:             'format' => 24,
412:             'start' => null,
413:             'end' => null,
414:             'leadingZeroKey' => true,
415:             'leadingZeroValue' => false,
416:             'templateVars' => [],
417:         ];
418:         $is24 = $options['format'] == 24;
419: 
420:         $defaultStart = $is24 ? 0 : 1;
421:         $defaultEnd = $is24 ? 23 : 12;
422:         $options['start'] = max($defaultStart, $options['start']);
423: 
424:         $options['end'] = min($defaultEnd, $options['end']);
425:         if ($options['end'] === null) {
426:             $options['end'] = $defaultEnd;
427:         }
428: 
429:         if (!$is24 && $options['val'] > 12) {
430:             $options['val'] = sprintf('%02d', $options['val'] - 12);
431:         }
432:         if (!$is24 && in_array($options['val'], ['00', '0', 0], true)) {
433:             $options['val'] = 12;
434:         }
435: 
436:         if (empty($options['options'])) {
437:             $options['options'] = $this->_generateNumbers(
438:                 $options['start'],
439:                 $options['end'],
440:                 $options
441:             );
442:         }
443: 
444:         unset(
445:             $options['end'],
446:             $options['start'],
447:             $options['format'],
448:             $options['leadingZeroKey'],
449:             $options['leadingZeroValue']
450:         );
451: 
452:         return $this->_select->render($options, $context);
453:     }
454: 
455:     /**
456:      * Generates a minute select
457:      *
458:      * @param array $options The options to generate a minute select with.
459:      * @param \Cake\View\Form\ContextInterface $context The current form context.
460:      * @return string
461:      */
462:     protected function _minuteSelect($options, $context)
463:     {
464:         $options += [
465:             'name' => '',
466:             'val' => null,
467:             'interval' => 1,
468:             'round' => 'up',
469:             'leadingZeroKey' => true,
470:             'leadingZeroValue' => true,
471:             'templateVars' => [],
472:         ];
473:         $options['interval'] = max($options['interval'], 1);
474:         if (empty($options['options'])) {
475:             $options['options'] = $this->_generateNumbers(0, 59, $options);
476:         }
477: 
478:         unset(
479:             $options['leadingZeroKey'],
480:             $options['leadingZeroValue'],
481:             $options['interval'],
482:             $options['round']
483:         );
484: 
485:         return $this->_select->render($options, $context);
486:     }
487: 
488:     /**
489:      * Generates a second select
490:      *
491:      * @param array $options The options to generate a second select with
492:      * @param \Cake\View\Form\ContextInterface $context The current form context.
493:      * @return string
494:      */
495:     protected function _secondSelect($options, $context)
496:     {
497:         $options += [
498:             'name' => '',
499:             'val' => null,
500:             'leadingZeroKey' => true,
501:             'leadingZeroValue' => true,
502:             'options' => $this->_generateNumbers(0, 59),
503:             'templateVars' => [],
504:         ];
505: 
506:         unset($options['leadingZeroKey'], $options['leadingZeroValue']);
507: 
508:         return $this->_select->render($options, $context);
509:     }
510: 
511:     /**
512:      * Generates a meridian select
513:      *
514:      * @param array $options The options to generate a meridian select with.
515:      * @param \Cake\View\Form\ContextInterface $context The current form context.
516:      * @return string
517:      */
518:     protected function _meridianSelect($options, $context)
519:     {
520:         $options += [
521:             'name' => '',
522:             'val' => null,
523:             'options' => ['am' => 'am', 'pm' => 'pm'],
524:             'templateVars' => [],
525:         ];
526: 
527:         return $this->_select->render($options, $context);
528:     }
529: 
530:     /**
531:      * Returns a translated list of month names
532:      *
533:      * @param bool $leadingZero Whether to generate month keys with leading zero.
534:      * @return array
535:      */
536:     protected function _getMonthNames($leadingZero = false)
537:     {
538:         $months = [
539:             '01' => __d('cake', 'January'),
540:             '02' => __d('cake', 'February'),
541:             '03' => __d('cake', 'March'),
542:             '04' => __d('cake', 'April'),
543:             '05' => __d('cake', 'May'),
544:             '06' => __d('cake', 'June'),
545:             '07' => __d('cake', 'July'),
546:             '08' => __d('cake', 'August'),
547:             '09' => __d('cake', 'September'),
548:             '10' => __d('cake', 'October'),
549:             '11' => __d('cake', 'November'),
550:             '12' => __d('cake', 'December'),
551:         ];
552: 
553:         if ($leadingZero === false) {
554:             $i = 1;
555:             foreach ($months as $key => $name) {
556:                 unset($months[$key]);
557:                 $months[$i++] = $name;
558:             }
559:         }
560: 
561:         return $months;
562:     }
563: 
564:     /**
565:      * Generates a range of numbers
566:      *
567:      * ### Options
568:      *
569:      * - leadingZeroKey - Set to true to add a leading 0 to single digit keys.
570:      * - leadingZeroValue - Set to true to add a leading 0 to single digit values.
571:      * - interval - The interval to generate numbers for. Defaults to 1.
572:      *
573:      * @param int $start Start of the range of numbers to generate
574:      * @param int $end End of the range of numbers to generate
575:      * @param array $options Options list.
576:      * @return array
577:      */
578:     protected function _generateNumbers($start, $end, $options = [])
579:     {
580:         $options += [
581:             'leadingZeroKey' => true,
582:             'leadingZeroValue' => true,
583:             'interval' => 1
584:         ];
585: 
586:         $numbers = [];
587:         $i = $start;
588:         while ($i <= $end) {
589:             $key = (string)$i;
590:             $value = (string)$i;
591:             if ($options['leadingZeroKey'] === true) {
592:                 $key = sprintf('%02d', $key);
593:             }
594:             if ($options['leadingZeroValue'] === true) {
595:                 $value = sprintf('%02d', $value);
596:             }
597:             $numbers[$key] = $value;
598:             $i += $options['interval'];
599:         }
600: 
601:         return $numbers;
602:     }
603: 
604:     /**
605:      * Returns a list of fields that need to be secured for this widget.
606:      *
607:      * When the hour picker is in 24hr mode (null or format=24) the meridian
608:      * picker will be omitted.
609:      *
610:      * @param array $data The data to render.
611:      * @return array Array of fields to secure.
612:      */
613:     public function secureFields(array $data)
614:     {
615:         $data = $this->_normalizeData($data);
616: 
617:         $fields = [];
618:         foreach ($this->_selects as $select) {
619:             if ($data[$select] === false || $data[$select] === null) {
620:                 continue;
621:             }
622: 
623:             $fields[] = $data['name'] . '[' . $select . ']';
624:         }
625: 
626:         return $fields;
627:     }
628: }
629: 
Follow @CakePHP
#IRC
OpenHub
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Logos & Trademarks
  • Community
  • Team
  • Issues (Github)
  • YouTube Channel
  • Get Involved
  • Bakery
  • Featured Resources
  • Newsletter
  • Certification
  • My CakePHP
  • CakeFest
  • Facebook
  • Twitter
  • Help & Support
  • Forum
  • Stack Overflow
  • IRC
  • Slack
  • Paid Support

Generated using CakePHP API Docs