2

我在用户模型中自定义了查找类型方法:

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;
}

现在从其他控制器(命名ReportsController)我想对这个查找的结果进行分页。我这样做是这样的:

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')
    ));
}

问题是我只能按User.idUser.username字段(通常是用户字段)排序。即使我通过它的第三个参数指定了允许的字段Controller::paginate()也不起作用。这些Paginator Helper链接会生成正确的 URL(例如/admin/reports/index/sort:Click.clicks/direction:asc),但我在 SQL 查询中看不到 ORDER BY 部分。我已经debug($query)输入,例如当该_findStats()字段为空时。 Click.clicks$query['order']

4

2 回答 2

1

好的,我发现 $query 还接收“排序”和“方向”键。所以,这里有解决方案:

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;
}

当然,这不是安全的解决方案,因为它省略了PaginatorComponent::validateSort().

于 2013-07-16T12:43:04.787 回答
0

按不存在的字段排序

从问题中的代码来看 - 看起来你已经在做正确的事情了。

分页器组件验证排序顺序是否来自请求:

验证可以在 $object 上执行所需的排序。只能对字段或虚拟字段进行排序。方向参数也将被清理。最后排序+方向键将转换为模型友好的顺序键。

您可以使用白名单参数来控制哪些列/字段可用于排序。这有助于防止用户对未索引值的大型结果集进行排序。

如问题所示,传递白名单应该允许跳过此逻辑,但如果它不调试分页器组件,则可能会$field识别 . 的名称和 . 中的值之间存在某种不匹配$whitelist

作为明确的作品

请记住,问题中的调用是管理路由 - 您可以放弃验证排序参数的“安全性”,只需从请求中明确设置排序顺序:

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')
    ));
}
于 2013-07-16T08:32:47.600 回答