views:

213

answers:

1

I'm trying to make Cakephp paginate take advantage of the SQL_CALC_FOUND_ROWS feature in mysql to return a count of total rows while using LIMIT. Hopefully, this can eliminate the double query of paginateCount(), then paginate().

I've put this in my app_model.php, and it basically works, but it could be done better. Can someone help me figure out how to override paginate/paginateCount so it executes only 1 SQL stmt?

/**
 * Overridden paginateCount method, to using SQL_CALC_FOUND_ROWS
 */
public function paginateCount($conditions = null, $recursive = 0, $extra = array()) {
    $options = array_merge(compact('conditions', 'recursive'), $extra);
    $options['fields'] = "SQL_CALC_FOUND_ROWS `{$this->alias}`.*";

Q: how do you get the SAME limit value used in paginate()?

    $options['limit'] = 1;   // ideally, should return same rows as in paginate()     

Q: can we somehow get the query results to paginate directly, without the extra query?

    $cache_results_from_paginateCount = $this->find('all', $options);   
    /*
     * note: we must run the query to get the count, but it will be cached for multiple paginates, so add comment to query
     */
    $found_rows =  $this->query("/* do not cache for {$this->alias} */ SELECT FOUND_ROWS();");
    $count = array_shift($found_rows[0][0]);
    return $count;
}   

/**
 * Overridden paginate method
 */
public function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) {
    $options = array_merge(compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive'), $extra);

Q: can we somehow get $cache_results_for_paginate directly from paginateCount()?

    return $cache_results_from_paginateCount;  // paginate in only 1 SQL stmt
    return $this->find('all', $options);   // ideally, we could skip this call entirely
}
A: 

The way I solve this for paginating results from a web service which returns the results for the current page and the total number of results available in the same response is to copy the paginate property from the controller to the model in your controller action, before you call $this->paginate(); so that my paging settings are available in the model's paginateCount() method. Then in paginateCount() issue the call to the webservice to get the results. Then store the results in a property of the model then return the total number of results available. Then in the model's paginate() method, I return the results I fetched and stored in the paginateCount() method.

Perhaps something along these lines might work for you too?

neilcrookes
I used a class static var to cache the paginateResponse, but it never occured to me that I could also pass the paginate options through the same mechanism. It seems to work fine as long as I can set recursive=-1, but if there is an association in the pagination, then the "SELECT FOUND ROWS" gets called AFTER the association queries, and returns the wrong results.
michael
also having a hard time getting the $fields value correct in the find(). currently using $fields = is_array($fields) ? array_unshift($fields, 'SQL_CALC_FOUND_ROWS') : !empty($fields) ? array('SQL_CALC_FOUND_ROWS', $fields) : "SQL_CALC_FOUND_ROWS `{$this->alias}`.*";but really only tested for $fields=;;
michael