As stated in the linked docs, virtual properties cannot be used in finds. That's by design, virtual properties only live in entities, they are built in PHP after data has been retrieved from the database.
So let's forget about virtual properties for a moment, and concentrate on queries and computed columns.
Computed columns need to be specified via sortWhitelist
Just like columns of associated models, computed columns need to be specified in the sortWhitelist
option in order to be useable for sorting!
Cookbook > Controllers > Components > Pagination > Control which Fields Used for Ordering
Via pagination options
You have some options here, for example you could define computed columns in the pagination options:
$this->paginate = [
// ...
'sortWhitelist' => [
'id',
'first_name',
'last_name',
'full_name',
// ...
],
'fields' => [
'id',
'first_name',
'last_name',
'full_name' => $this->Table->query()->func()->concat([
'first_name' => 'literal',
'last_name' => 'literal'
]),
// ...
],
'order' => [
'full_name' => 'DESC'
]
];
A custom finder
Another, more reusable option would be to use a custom finder:
$this->paginate = [
// ...
'sortWhitelist' => [
'id',
'first_name',
'last_name',
'full_name',
// ...
],
'finder' => 'withFullName',
'order' => [
'full_name' => 'DESC'
]
];
public function findWithFullName(\Cake\ORM\Query $query, array $options)
{
return $query->select([
'id',
'first_name',
'last_name',
'full_name' => $query->func()->concat([
'first_name' => 'literal',
'last_name' => 'literal'
]),
// ...
]);
}
Separate custom query
It's also possible to directly pass query objects to Controller::paginate()
:
$this->paginate = [
// ...
'sortWhitelist' => [
'id',
'first_name',
'last_name',
'full_name',
// ...
],
'order' => [
'full_name' => 'DESC'
]
];
$query = $this->Table
->find()
->select(function (\Cake\ORM\Query $query) {
return [
'id',
'first_name',
'last_name',
'full_name' => $query->func()->concat([
'first_name' => 'literal',
'last_name' => 'literal'
]),
// ...
];
});
$results = $this->paginate($query);