CakePHP 3 time column gets date added

后端 未结 2 1366
庸人自扰
庸人自扰 2020-12-04 02:59

I have a number of columns defined in my MySQL database as \"time\". That is, they have a time, but not a date. When they are read by the CakePHP 3 ORM, they are being conve

相关标签:
2条回答
  • 2020-12-04 03:23

    the fact is cakePHP perform your time value as Time Object which extends carbon and dateTime so u get a full dateTime instance included date :(

    however u can easily perform an automatic set with your entity. U only have to use Accessors & Mutators describe here: http://book.cakephp.org/3.0/en/orm/entities.html#accessors-mutators

    0 讨论(0)
  • 2020-12-04 03:29

    Date/Time values are all being casted to the same base structure, that is a DateTime or DateTimeImmutable object, and so naturally date-only values will have a time value added (00:00:00), and time-only values come with a date (the current date).

    CakePHP will uses specific subclasses depending on the SQL datatype, that is

    • \Cake\I18n\Time or \Cake\I18n\FrozenTime for TIME, TIMESTAMP, and DATETIME
    • \Cake\I18n\Date or \Cake\I18n\FrozenDate for DATE

    In earlier CakePHP 3 versions there was only \Cake\I18n\Time.

    It would be nice if there would be a separate class for time-only types, which would have a proper time-only default output format set, but until something like that is added, you'll have to take care of the output format yourself.

    Format in your views

    It is up to you how to display this in your views. You can easily use the i18nFormat() method of the Time class instance

    $record['start_time']->i18nFormat(
        [\IntlDateFormatter::NONE, \IntlDateFormatter::SHORT]
    )
    

    or the Time helper, to show only the time part

    $this->Time->i18nFormat(
        $record['start_time'],
        [\IntlDateFormatter::NONE, \IntlDateFormatter::SHORT]
    )
    

    Guess it wouldn't hurt if bake would generate similar code according to the type of column, you may want to suggest that as an enhancement. As mentioned using additional classes (or maybe options) for time-only columns may be something worth to consider too.

    Use a custom time class

    If you'd wanted this behavior everywhere where the string representation of the object is being used, without having to manually invoke the formatter, then you could make use of an extended \Cake\I18n\Time or \Cake\I18n\FrozenTime class with an overriden $_toStringFormat property, so that it formats the date accordingly.

    src/I18n/FrozenTimeOnly.php

    namespace App\I18n;
    
    use Cake\I18n\FrozenTime;
    
    class FrozenTimeOnly extends FrozenTime
    {
        protected static $_toStringFormat = [
            \IntlDateFormatter::NONE,
            \IntlDateFormatter::SHORT
        ];
    }
    

    src/config/bootstrap.php

    use Cake\Database\Type\TimeType;
    use App\I18n\FrozenTimeOnly;
    TimeType::$dateTimeClass = FrozenTimeOnly::class;
    
    // remove the default `useImmutable()` call, you may however
    // want to keep further calls for formatting and stuff
    Type::build('time'); 
    // ...
    

    This should pretty much self-explantory, time columns that are being mapped to TimeType, will now use App\I18n\FrozenTimeOnly instead of the default Cake\I18n\Time.

    DateTimeType::$dateTimeClass is deprecated

    In order to cope with that, a custom database type will be required, which is rather simple too.

    src/Database/Type/TimeOnlyType.php

    namespace App\Database\Type;
    
    use App\I18n\FrozenTimeOnly;
    use Cake\Database\Type\TimeType;
    
    class TimeOnlyType extends TimeType
    {
        public function __construct($name)
        {
            parent::__construct($name);
            $this->_setClassName(FrozenTimeOnly::class, \DateTimeImmutable::class);
        }
    }
    

    It should be note that currently this will instantiate a data/time class twice, as the parent constructor will invoke _setClassName() too, which is where an instance of the given class will be instantiated.

    src/config/bootstrap.php

    use App\Database\Type\TimeOnlyType;
    Type::map('time', TimeOnlyType::class);
    

    So what this will do, is override the default time type mapping to use the custom \App\Database\Type\TimeOnlyType class, which in turn will use the \App\I18n\TimeOnly class when converting database values to PHP objects, which when converted to a string, will use the time-only format.

    See also

    • Cookbook > Time > Setting the Default Locale and Format String
    • Cookbook > Database Access & ORM > Database Basics > Adding Custom Types
    0 讨论(0)
提交回复
热议问题