Aman Rawat
Aman Rawat

Reputation: 2625

Set Date as keyField in list Cakephp 3

I have used list to get data from the model and it works well

This example is from Cakephp docs

$query = $articles->find('list', [
    'keyField' => 'slug',
    'valueField' => 'title'
]);
$data = $query->toArray();

I have a table name holidays

id[int] | date[date] | name[varchar(200)] | created[datetime]

So want a list which has date as the key and holiday as value Somthing like this

[
    '2016-01-01'=>'New Year',
    '2016-01-26'=>'Republic Day',
]

So i created model by cake bake and used this code to achive it

$holidays = TableRegistry::get('Holidays');
$holidays = $holidays->find('list',[
    'keyField' => 'date',
    'valueField'=>'name',
])->toArray();

But its giving me error

Illegal offset type InvalidArgumentException

When i change the keyField from date with name it works great.

Here is the logs

2016-04-29 04:26:41 Error: [InvalidArgumentException] Illegal offset type Request URL: /sfworxerp/api/attendances/getMonthAttendanceData Stack Trace: F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\Collection\Iterator\MapReduce.php(160): Cake\Collection\Iterator\MapReduce::emit() F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\Collection\CollectionTrait.php(386): Cake\Collection\Iterator\MapReduce->emit('New Year', Object(Cake\I18n\FrozenDate)) F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\Collection\Iterator\MapReduce.php(177): Cake\ORM\ResultSet->Cake\Collection{closure}(Object(App\Model\Entity\Holiday), 0, Object(Cake\Collection\Iterator\MapReduce)) F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\Collection\Iterator\MapReduce.php(132): Cake\Collection\Iterator\MapReduce->_execute() [internal function]: Cake\Collection\Iterator\MapReduce->getIterator() F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\Collection\Collection.php(50): IteratorIterator->__construct(Object(Cake\Collection\Iterator\MapReduce)) F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\Collection\CollectionTrait.php(405): Cake\Collection\Collection->__construct(Object(Cake\Collection\Iterator\MapReduce)) F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\ORM\Table.php(1062): Cake\ORM\ResultSet->combine('date', 'name', NULL) F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\Datasource\QueryTrait.php(490): Cake\ORM\Table->Cake\ORM{closure}(Object(Cake\ORM\ResultSet)) F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\ORM\Query.php(1141): Cake\ORM\Query->_applyDecorators(Object(Cake\ORM\ResultSet)) F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\Datasource\QueryTrait.php(272): Cake\ORM\Query->_decorateResults(Object(Cake\ORM\ResultSet)) F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\ORM\Query.php(871): Cake\ORM\Query->_all() F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\Datasource\QueryTrait.php(288): Cake\ORM\Query->all() F:\public_html\sfworxerp\src\Controller\Api\AttendancesController.php(133): Cake\ORM\Query->toArray() [internal function]: App\Controller\Api\AttendancesController->getMonthAttendanceData() F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\Controller\Controller.php(429): call_user_func_array(Array, Array) F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\Routing\Dispatcher.php(114): Cake\Controller\Controller->invokeAction() F:\public_html\sfworxerp\vendor\cakephp\cakephp\src\Routing\Dispatcher.php(87): Cake\Routing\Dispatcher->_invoke(Object(App\Controller\Api\AttendancesController)) F:\public_html\sfworxerp\webroot\index.php(37): Cake\Routing\Dispatcher->dispatch(Object(Cake\Network\Request), Object(Cake\Network\Response)) {main}

Any help is highly appreciated

Upvotes: 4

Views: 2806

Answers (2)

arilia
arilia

Reputation: 9398

I tried a similar example and I got your same error.

Looking at the error stack seems it's related to the fact that cake returns a Time object and not a string.

I found a workaround: you can created a virtual field in your entity

Holyday Entity

protected function _getFormattedDate()
{
    if(isset($this->date))
        return $this->date->format('Y-m-d');
    return null;
}

so you can do

$holidays = $holidays->find('list',[
    'keyField' => 'formatted_date',
    'valueField'=>'name',
])->toArray();

you can also use Dave's suggestion to create a custom finder.

HolidaysTable

public function findDate(Query $query, array $options)
{

    $options['keyField'] = function ($e) {
        if(isset($e->date))
            return $e->date->format('Y-m-d');
        return null;
    };
    $options['valueField'] = 'name';

    return  $this->findList($query, $options);
}

I just added a control on whether date field is set otherwise you could get an error.

controller

$holidays->find('date')->toArray();

Upvotes: 1

Dave
Dave

Reputation: 29121

The Problem:

The problem is, for 'date' fields, CakePHP 3 returns an object, not a string:

'date' => object(Cake\I18n\FrozenDate) {
    'time' => '1997-01-03T00:00:00+00:00',
    'timezone' => 'UTC',
    'fixedNowTime' => false
},

Trying to use that as the key doesn't work, and throws the error you're seeing.

How to handle:

Per this area in the CakePHP book, to accommodate this, you can:

use closures to access entity mutator methods in your list finds.

TLDR Do this:

$query = $articles->find('list', [
    'keyField' => function ($e) {
         return $e->date->format('Y-m-d');
     },
    'valueField' => 'title'
]);

Explanation for how it works:

Basically, it puts the value of each entity (in your case each article) into the variable $e, and allows you to use/modify it's data, and return the string you want to use as the key (or value) field. In the case above, it's getting the date object, and formatting it into a string before returning it to be used as the key.

Upvotes: 8

Related Questions