Order by doesn't work with custom model find while paginating

爷,独闯天下 提交于 2019-12-12 16:31:13

问题


I've custom find type method in User model:

protected function _findStats($state, $query, $results = array()) {
    if ($state === 'before') {
        debug($query);
        $query['fields'] = array('User.id', 'User.username', 'Click.clicks', 'Click.unique', 'Sale.sales_1', 'Sale.sales_2');
        $query['joins'] = array(
            array(
                'table' => '(' . $this->__buildSubQueryClicks() . ')',
                'alias' => 'Click',
                'type' => 'LEFT',
                'conditions' => array('User.id = Click.id')
            ),
            array(
                'table' => '(' . $this->__buildSubQuerySales() . ')',
                'alias' => 'Sale',
                'type' => 'LEFT',
                'conditions' => array('User.id = Sale.id')
            )
        );
        return $query;
    }
    return $results;
}

Now from other Controller (named ReportsController) I would like to paginate results of this find. I'm doing it like this:

public function admin_index() {     
    list($from, $to) = $this->_prepareCommonVariables();
    $this->paginate = array('stats');
    $this->User->recursive = 0;
    $this->set('users', $this->paginate('User',
        array(
            'Click.created BETWEEN ? AND ?' => array($from, $to),
            'Sale.sold BETWEEN ? AND ?' => array($from, $to)
        ),
        array('User.username', 'Click.unique', 'Click.clicks', 'Sale.sales_1', 'Sale.sales_2')
    ));
}

The problem is that I can only order by User.id and User.username fields (generally User-fields). Even if I specified the allowed fields by third parameter of Controller::paginate() it doesn't work. The Paginator Helper links generate proper URLs (eg. /admin/reports/index/sort:Click.clicks/direction:asc) but I don't see ORDER BY part in SQL query. I've put debug($query) in _findStats() and when the field is for example Click.clicks the $query['order'] is empty.


回答1:


Ok, I've found that $query also recive 'sort' and 'direction' keys. So, here goes the solution:

protected function _findStats($state, $query, $results = array()) {
    if ($state === 'before') {
        $query['fields'] = array('User.id', 'User.username', 'Click.clicks', 'Click.unique', 'Sale.sales_1', 'Sale.sales_2');
        $query['joins'] = array(
            array(
                'table' => '(' . $this->__buildSubQueryClicks() . ')',
                'alias' => 'Click',
                'type' => 'LEFT',
                'conditions' => array('User.id = Click.id')
            ),
            array(
                'table' => '(' . $this->__buildSubQuerySales() . ')',
                'alias' => 'Sale',
                'type' => 'LEFT',
                'conditions' => array('User.id = Sale.id')
            )
        );
        if (isset($query['sort'], $query['direction'])) {
            $query['order'] = array($query['sort'] => $query['direction']);
        }
        return $query;
    }
    return $results;
}

Of course this is not safe solution, because it omits PaginatorComponent::validateSort().




回答2:


Sorting by fields that don't exist

From the code in the question - it looks like you're already doing the right thing.

The paginator component validates the sort order if it came from the request:

Validate that the desired sorting can be performed on the $object. Only fields or virtualFields can be sorted on. The direction param will also be sanitized. Lastly sort + direction keys will be converted into the model friendly order key.

You can use the whitelist parameter to control which columns/fields are available for sorting. This helps prevent users from ordering large result sets on un-indexed values.

Passing a whitelist, as shown in the question, should permit skipping this logic but if it doesn't debugging the paginator component will probably identify some kind of mismatch between the name of the $field, and the values in the $whitelist.

Being explicit works

Bearing in mind that the call in the question is an admin route - you can forego the "security" of validating the sort parameters and just set the sort order explicitly from the request:

public function admin_index() {     
    list($from, $to) = $this->_prepareCommonVariables();

    //$this->paginate = array('stats'); // <- not the right syntax
    $settings = array('findType' => 'stats');

    if (isset($this->request->params['named']['sort'])) {
        $sortField = $this->request->params['named']['sort'];
        $dir = $this->requestparams['named']['direction'];
        $settings['order'] = array($sortField => $dir);
    }

    $this->paginate = $settings;

    $this->User->recursive = 0;
    $this->set('users', $this->paginate('User',
        array(
            'Click.created BETWEEN ? AND ?' => array($from, $to),
            'Sale.sold BETWEEN ? AND ?' => array($from, $to)
        ),
        array('User.username', 'Click.unique', 'Click.clicks', 'Sale.sales_1', 'Sale.sales_2')
    ));
}


来源:https://stackoverflow.com/questions/17670986/order-by-doesnt-work-with-custom-model-find-while-paginating

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!