Zenzs
Zenzs

Reputation: 138

Using the paginator directly and defining the sort on an associated column not working

CakePHP Version: 4.0.1

Introduction

This problem follows on from another problem I had here where a not ideal solution was to redirect back to itself. Unfortunately the controller I was testing didn't have any associated columns so this new problem was not identified.

I referenced this in the cookbook.

Hopefully the below code will allow the problem to be reproduced.


Contacts Table

public function initialize(array $config): void
{
    parent::initialize($config);

    $this->setTable('contacts');

    $this->belongsTo('Users', [
        'foreignKey' => 'user_id',
        'joinType' => 'INNER'
    ]);

    $this->belongsTo('Accounts', [
        'foreignKey' => 'account_id'
    ]);

}

// Custom Finder
public function findSuperuserAllContacts(Query $query, array $options): object
{
    $query
        ->where(['Contacts.status' => $options['status']])
        ->andWhere([
            'Users.client_id' => $options['client_id'],
            'Users.status' => 1
        ]);

    return $query;
}

Contacts Controller

public $paginate = [
    'sortWhitelist' => [
       'Contacts.first_name',
       'Contacts.last_name',
       'Accounts.account_name',
       'Users.first_name',
       'Users.last_name',
       'Contacts.primary_tel',
       'Contacts.mobile',
       'Contacts.email'
    ]
];

public function index() {

    $query = (object) $this->Contacts->find('superuserAllContacts', [
        'contain' => ['Users', 'Accounts'],
        'status' => 1,
        'client_id' => 1001
    ]);

    $page = '';
    $sort = 'Accounts.account_name';
    $direction = 'asc';

    $config = $this->paginate = [                
        'page' => $page,
        'sort' => $sort,
        'direction' => $direction,
        'limit' => 10
    ];        

    $contacts = $this->Paginator->paginate($query, $config);
    $this->set(compact('contacts'));

}

What Happens

The page displays a type error in the framework.

C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\View\Helper\PaginatorHelper.php

strtolower() expects parameter 1 to be a string, bool given

public function sortDir(?string $model = null, array $options = []): string
{
    $dir = null;

    if (empty($options)) {
        $options = $this->params($model);
    }

    if (isset($options['direction'])) {

        debug($options['direction']);  // THIS IS FALSE NOT asc or desc?

        $dir = strtolower($options['direction']);
    }

    if ($dir === 'desc') {
        return 'desc';
    }

    return 'asc';
}

Stacktrace from error.log

2020-06-01 10:33:20 Error: [TypeError] strtolower() expects parameter 1 to be string, bool given in C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\View\Helper\PaginatorHelper.php on line 264

Stack Trace:
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\View\Helper\PaginatorHelper.php:264
- C:\xampp\htdocs\crm\templates\Contacts\index.php:5
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\View\View.php:1164
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\View\View.php:1125
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\View\View.php:750
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Controller\Controller.php:691
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Controller\Controller.php:533
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Controller\ControllerFactory.php:79
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\BaseApplication.php:229
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\Runner.php:77
- C:\xampp\htdocs\crm\vendor\cakephp\authentication\src\Middleware\AuthenticationMiddleware.php:122
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\Runner.php:73
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\I18n\Middleware\LocaleSelectorMiddleware.php:70
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\Runner.php:73
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\Runner.php:77
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\Middleware\CsrfProtectionMiddleware.php:132
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\Runner.php:73
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\Runner.php:58
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Routing\Middleware\RoutingMiddleware.php:162
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\Runner.php:73
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Routing\Middleware\AssetMiddleware.php:68
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\Runner.php:73
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Error\Middleware\ErrorHandlerMiddleware.php:118
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\Runner.php:73
- C:\xampp\htdocs\crm\vendor\cakephp\debug_kit\src\Middleware\DebugKitMiddleware.php:60
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\Runner.php:73
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\Runner.php:58
- C:\xampp\htdocs\crm\vendor\cakephp\cakephp\src\Http\Server.php:90
- C:\xampp\htdocs\crm\webroot\index.php:40

Request URL: /contacts
Referer URL: https://localhost/crm/welcome


Question

Why is a type error displayed instead of loading the index template with the sort on the account_name?

Thanks Z.


EDIT

I've just tried a fresh install with

composer self-update && composer create-project --prefer-dist cakephp/app:4.* crm

and baked users, contacts and accounts.

The new version is 4.0.8 and I added the code I have above in the new project but unfortunatley got exactly the same type error.

If the sort is on the same table, ie: Contacts.last_name the last name has the sort on it but if I change it to the associated table Accounts.account_name the type error is displayed.

Upvotes: 0

Views: 720

Answers (1)

Zenzs
Zenzs

Reputation: 138

The sort field whitelist must be passed in the config when using the paginator directly.

$config = $this->paginate = [
    'sortWhitelist' => [
        'Contacts.first_name',
        'Contacts.last_name',
        'Accounts.account_name',
        'Users.first_name',
        'Users.last_name',
        'Contacts.primary_tel',
        'Contacts.mobile',
        'Contacts.email'
    ],
    'page' => $page,
    'sort' => $sort,
    'direction' => $direction,
    'limit' => 10
];

$contacts = $this->Paginator->paginate($query, $config);
$this->set(compact('contacts'));

The sort is now displayed on the account_name.

Upvotes: 1

Related Questions