Quite some time ago I wrote a blog post about saving CakePHP 1.x paging data in the session so that they can be available at next page visit. The basic idea was that you could store the page, sort and direction named parameters in the session and restore them back when no paging parameters were available.
When I tried applying the same technique in CakePHP 2.x, I run into a very serious obstacle and that was the fact that the Paginator::numbers()
function does not anymore include the page:1 named parameter when creating the link for the first (or previous) page. This created a phenomenon that when someone visited a page without any paging parameters, it was impossible to know whether they were there because of a link to the first page or as a result of a redirect from somewhere else, in which case the session had to be checked and paging data to be restored.
The thing had been puzzling me for some days now. I tried different remedies without any luck or results until I cried for help at stackoverflow.com. It was there that Ilie Pandia shook things a bit and then I managed, thanks to his advice, to create the following component that mimics the original Cake 1.x PaginationRecallComponent
by mattc found in the Bakery, from which I borrowed the name and structure.
This version is new (April-2015) and hopefully includes the bug-fixes pointed out by all comments. The tests where made using CakePHP version 2.6.3.
<?php App::uses('Component', 'Controller'); /** * Pagination Recall CakePHP Component * CakePHP 2.x version. Thanassis Bakalidis abakalidis.blogspot.com= * * @author Thanassis Bakalidis * @version 2.2 * @license MIT * @property SessionComponent $Sesion Session handler to save paging data into */ class PaginationRecallComponent extends Component { const PREV_DATA_KEY = 'Paginaion-PrevData'; public $components = ['Session']; private $_controller = NULL; private $_action = NULL; private $_previousUrl; public function initialize(\Controller $controller) { $this->_controller = $controller; $this->_action = $controller->params['action']; } public function startup(Controller $controller) { if ($this->_controller->name === 'CakeError') return; $this->_restorePagingParams(); // save the current controller and action for the next time $this->Session->write( self::PREV_DATA_KEY, [ 'controller' => $this->_controller->name, 'action' => $this->_action ] ); } private function _restorePagingParams() { $sessionKey = "Pagination.{$this->_controller->name}.{$this->_action}"; // extract paging data from the request parameters $pagingParams = $this->_extractPagingParams(); // if paging data exist write them in the session if (!empty($pagingParams)) { $this->Session->write( $sessionKey, $pagingParams); return; } // no paging data. // construct the previous URL $this->_previousUrl = $this->Session->check(self::PREV_DATA_KEY) ? $this->Session->read(self::PREV_DATA_KEY) : [ 'controller' => '', 'action' => '' ]; // and check if the current page is the same as the previous if ($this->_previousUrl['controller'] === $this->_controller->name && $this->_previousUrl['action'] === $this->_action) { // in this case we have a link from our own paging::numbers() function // to move to page 1 pf the current page, delete any paging data $this->Session->delete($sessionKey); return; } // we are comming from a different page so if we have any session data if ($this->Session->check($sessionKey)) // then restore and use them $this->_controller->request->params['named'] = array_merge( $this->_controller->request->params['named'], $this->Session->read($sessionKey) ); } private function _extractPagingParams() { $pagingParams = $this->_controller->request->params['named']; $vars = ['page', 'sort', 'direction']; $keys = array_keys($pagingParams); $count = count($keys); for ($i = 0; $i < $count; $i++) if (!in_array($keys[$i], $vars)) unset($pagingParams[$keys[$i]]); return $pagingParams; } }
Note: It turns out that the components shutdown method is not called when the owner controller's action returns a redirect().
Hence, this last update was about getting rid of any shutdown()
functionality and performing everything in the components startup()
moethod code. This hopefully, fixes the bug of loosing paging data after a call to the delete() method which returns an immediate redirect..