Cakephp and paginating multiple result sets

2019-02-19 13:28发布

I have pagination working fine for when $this->paginate is called once in an action, but it does not appear to be very good support for multiple calls. Here is what I'm looking to do ...

function admin_index() {

    $published = $this->pagination('Post', array('status'=>1));
    $unpublished = $this->pagination('Post', array('status'=>0));

    $this->set('published', $published);
    $this->set('unpublished', $unpublished);
}

A simplified version of my view would be:

<h1>Unpublished Posts</h1>
<?php echo $this->element('table', array('posts'=>$unpublished)); ?>
<h1>Published Posts</h1>
<?php echo $this->element('table', array('posts'=>$published)); ?>

Simple, and I can re-use the same element to display different types of data. Is there really no way around paginating each of these table separately other than jquery table sorter / hacks to Cakephp?

I understand that I could stick all the data in one table and then just use sorting to differentiate between the statuses, but I wanted to check if I was missing something first.

2条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-02-19 14:02

Since there was not much activity on this, I went ahead and did the reasonable thing and figured out a workaround. Below is what I came up with:

Controller:

function index() {
    $active = $this->paginate('Post', array('Post.status'=>1));     
    $activePaging = $this->request->params['paging']['Post'];

    $inactive = $this->paginate('Post', array('Post.status'=>0));
    $inActivePaging = $this->request->params['paging']['Post'];

    $this->set('active', $active);
    $this->set('inactive', $inactive);

    $this->request->params['paging']['Active'] = $activePaging;
    $this->request->params['paging']['InActive'] = $inActivePaging;
    unset($this->request->params['paging']['Post']);
}

public function paginate() {
    if ($this->request->is('ajax')) {
        $model = $this->request->params['named']['model'];
        $status = $model === 'Active' ? 1 : 0;

        $posts = $this->paginate('Post', array('Post.status'=>$status));
        $paging = $this->request->params['paging']['Post'];
        unset($this->request->params['paging']['Post']);
        $this->request->params['paging'][$model] = $paging;

        $this->set('posts', $posts);
        $this->set('model', $model);
        $this->render('/Posts/ajax_posts_paginated', 'ajax');
    }
}

Views: (simplified)

index.ctp

<h3>Inactive Posts</h3>
<div class="well" id="InActive">
<?php echo $this->element('posts_table_paginated', array('posts'=>$inactive, 'model'=>'InActive')); ?>
</div>

<h3>Active Posts</h3>
<div class="well" id="Active">
<?php echo $this->element('posts_table_paginated', array('posts'=>$active, 'model'=>'Active')); ?>
</div>

elements/posts_table_paginated.ctp

<?php
$this->Paginator->options(array(
    'update' => '#'.$model,
    'evalScripts' => true,
    'url'=> array('controller'=>'posts', 'action'=>'paginate', 'model'=>$model)
));
?>
<table>
    <tr>
        <th><?php echo $this->Paginator->sort('id', 'ID', array('model'=>$model)); ?></th>
        <th><?php echo $this->Paginator->sort('title', 'Title', array('model'=>$model)); ?></th>
        <th><?php echo $this->Paginator->sort('created', 'Created', array('model'=>$model)); ?></th>
        <th>Created by</th>
    </tr>
    <?php 
    foreach ($posts as $post): 
    ?>
    <tr>
        <td><?php echo $post['Post']['id']; ?></td>
        <td><?php echo $post['Post']['title']; ?></td>
        <td><?php echo $post['Post']['created']; ?></td>
        <td><?php echo $post['User']['username']; ?></td>
    </tr>
    <?php 
    endforeach; 
    ?>
</table>
<?php
    if ($this->params['paging'][$model]['count'] > 0) {
?>
<div id="pagination">
    <ul id="paginate">
        <?php
            echo $this->Paginator->prev(__('previous'), array('tag' => 'li', 'model'=>$model), null, array('tag'=>'li', 'class'=>'prev off'));
            echo $this->Paginator->numbers(array('tag'=>'li', 'model'=>$model, 'separator' => false));
            echo $this->Paginator->next(__('next'), array('tag' => 'li', 'model'=>$model), null, array('tag'=>'li', 'class'=>'next off'));
        ?>
    </ul>
</div>

<?php 
    }
    echo $this->Js->writeBuffer(); 
?>
查看更多
劫难
3楼-- · 2019-02-19 14:06

I had the same problem and solved the problem with the following Classes in cakephp version 2.5.3 I have not checked whether my approach also works in older versions. First, I created a Helper and Component class which extends PaginationComponent and PaginationHelper and overwrote some Methods.

/app/Controller/Component/_PaginatorComponent.php

<?php
/**
 * Filename:    _PaginatorComponent.php
 * User:        Mitja Stelzner
 * Date:        18.01.14
 */
App::import('Component', 'Paginator');
class _PaginatorComponent extends PaginatorComponent{
    public function paginate($object = null, $scope = array(), $whitelist = array())
    {
        //Adopt from the beginning of parent::paginate
        if (is_array($object))
            $object = null;
        $object = $this->_getObject($object);

        $options = $this->mergeOptions($object->alias); //get options
        $params = $this->Controller->request->params; //get params

        //if custom page name is set
        if(isset($params['named'][$options['pageRequestName']]))
        {
            //get the page number and set it as 'page' in the request (for other methods in the Component)
            $page = $params['named'][$options['pageRequestName']];
            $this->Controller->request->params['named']['page'] =intval($page);
        }
        else
            $this->Controller->request->params['named']['page'] = 1; //else set the page number to 1

        $pageRequestName = null; //important for parent::paginate

        $results = parent::paginate($object, $scope, $whitelist); // TODO: Change the autogenerated stub
        unset($this->Controller->request->params['named']['page']); //remove the appended page number

        //get the options again
        $options = $this->mergeOptions($object->alias);
        //and add the pageRequestName to the paging array
        if(isset($options['pageRequestName']))
        $this->Controller->request['paging'] = array_merge_recursive(
            (array)$this->Controller->request['paging'],
            array($object->alias => array('pageRequestName'=>$options['pageRequestName']))
        );
        return $results;
    }

}

/app/View/Helper/_PaginatorHelper.php

<?php
/**
 * Filename:    _PaginatorHelper.php
 * User:        Mitja Stelzner
 * Date:        18.01.14
 */
App::uses('PaginatorHelper', 'View/Helper');
class _PaginatorHelper extends PaginatorHelper{
    public function link($title, $url = array(), $options = array()) {
        //change the page parameter 'page' to the custom pageRequestName
        if(isset($url['page']))
        {
            $params = $this->params($this->model);
            if(isset($params['pageRequestName']))
            {
                $url[$params['pageRequestName']] = $url['page'];
                unset($url['page']);
            }
        }
        return parent::link($title,$url,$options);
    }

    protected function _pagingLink($which, $title = null, $options = array(), $disabledTitle = null, $disabledOptions = array())
    {
        //Prevent the deletion of the variable model
        $disabledOptions['model'] = $options['model'];
        return parent::_pagingLink($which, $title, $options, $disabledTitle, $disabledOptions); // TODO: Change the autogenerated stub
    }

    private $model = null;
    public function numbers($options = array())
    {
        //Only to set the model again
        if(isset($options['model']))
            $this->model = $options['model'];
        return parent::numbers($options); // TODO: Change the autogenerated stub
    }


}

And a View Element to use the Pagination quickly:

/app/View/Element/paging.ctp

<div class="paging">
    <?php
    $options = !isset($modelName)?array():array('model'=>$modelName);
    echo $this->Paginator->prev('< ' . __('zurück'), $options, null, array('class' => 'prev disabled'));
    echo $this->Paginator->numbers(array('separator' => '')+$options);
    echo $this->Paginator->next(__('vor') . ' >', $options, null, array('class' => 'next disabled'));
    ?>
</div>

You can call the paging-element in your view like this: (Documentation)

<?php
echo $this->element('paging',array('modelName'=>'Customer'));
?>

If you want use the new created paginator, you have to say cakephp to use your class instead of the normal paginator. In your controller, you set the PaginatorHelper and PaginatorComponent like this: (found here)

public $helpers = array('Paginator' => array('className' => '_Paginator' ));
public $components = array('Paginator' => array('className' => '_Paginator' ));

The usage inside your controller. The $paginate have to be grouped into the Modelgroups: (Documentation)

public $paginate = array(
        'Customer' => array (
            'limit' => 25,
            'pageRequestName' => 'cpage'
        ),
        'Employer' => array (
            'limit' => 25,
            'pageRequestName' => 'erpage'
        ),
        'Employee' => array (
            'limit' => 25,
            'pageRequestName' => 'eepage'
        )
    );

But I use it in the following context:

$this->Paginator->settings['Customer'] = array(
            'conditions'=>array(
                'OR'=>array(
                    array('name LIKE'=>'%'.$search_query.'%'),
                )
            ),
            'limit' => 5,
            'pageRequestName' => 'cpage'
        );
        $result = $this->Paginator->paginate('Customer');

I hope I could help some people who do not want a javascript hack, but a real possibility to have several Paginations on one side.

At the moment you can only use 'paramType' => 'named'

In my opinion, the original code in PaginationComponent and PaginationHelper are horrible to read, extend and adapt.

查看更多
登录 后发表回答