sverreg sverreg - 1 year ago 77
PHP Question

Override Cakephp 3.1 data type using typeMap()

When I have a query:

$jobs = $this->Jobs->find('all', [
'fields' => [
'id',
'existingdatetime',
'newdatetime' => '"2016-05-10 16:12:10"',
],
]);


The existingdatetime is returned as a DateTime object, while the newdatetime is returned as a string, which makes sense because there is no data definition for the non-existent field newdatetime. Based on Cakephp-3.x alias changing the data type in find method I tried the following:

debug($jobs->typeMap());
$jobs->typeMap()->addDefaults([
'Jobs.newdatetime' => 'datetime',
'newdatetime' => 'datetime',
]);
debug($jobs->typeMap());
$results = $jobs->all();
debug($results);


At first it appears that the TypeMap has been successfully updated, here's the old version:

/src/Controller/Admin/JobsController.php (line 97)

object(Cake\Database\TypeMap) {
[protected] _defaults => [
'Jobs.id' => 'integer',
'id' => 'integer',
'Jobs.existingdatetime' => 'datetime',
'existingdatetime' => 'datetime'
]
[protected] _types => []
}


And the new version as a result of calling addDefaults() it appears that the datatypes are updated fine...

/src/Controller/Admin/JobsController.php (line 102)

object(Cake\Database\TypeMap) {
[protected] _defaults => [
'Jobs.id' => 'integer',
'id' => 'integer',
'Jobs.existingdatetime' => 'datetime',
'existingdatetime' => 'datetime',
'Jobs.newdatetime' => 'datetime'
'newdatetime' => 'datetime',
]
[protected] _types => []
}


And yet the result still has newdatetime as a string:

/src/Controller/Admin/JobsController.php (line 106)

object(Cake\ORM\ResultSet) {
'query' => object(Cake\ORM\Query) {
'(help)' => 'This is a Query object, to get the results execute or iterate it.',
'sql' => 'SELECT Jobs.id AS `Jobs__id`, Jobs.existingdatetime AS `Jobs__existingdatetime`, "2016-05-10 16:12:10" AS `newdatetime` FROM jobs Jobs',
'params' => [],
'defaultTypes' => [
'Jobs.id' => 'integer',
'id' => 'integer',
'Jobs.existingdatetime' => 'datetime',
'existingdatetime' => 'datetime',
'Jobs.newdatetime' => 'datetime'
'newdatetime' => 'datetime',
],
'decorators' => (int) 0,
...snip...
}
'items' => [
(int) 0 => object(App\Model\Entity\Job) {
'id' => (int) 1,
'existingdatetime' => object(Cake\I18n\Time) {
'time' => '2016-04-27T03:21:32+1000',
'timezone' => 'Australia/Melbourne',
'fixedNowTime' => false
},
'newdatetime' => '2016-05-10 16:12:10',
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[repository]' => 'Jobs'
}
]
}


What am I missing? Why can't I get my extra field to be a datetime?

Please don't suggest completely different approaches, in reality the field newdatetime is a complex left inner join subquery that I have taken out of this question to keep things simple.

Answer Source

It doesn't use the TypeMap function but it does convert the custom field into the datetime type you require

$jobs = $this->Jobs->find()
->select([ 'id', 'existingdatetime', 'newdatetime' => '"2016-05-10 16:12:10"' ])
->decorateResults(function ($row) {
    $row['newdatetime'] = new \Cake\I18n\Time($row['newdatetime']);
    return $row;
});

You can see more about it here: CakePHP 3 Decorate Results