SOLVED - Please see below.
I need to separate the index initial load from the other index loads such as from pagination or a redirect from another function for 3 reasons.
-
To manipulate the sort and direction on initial load to enable DRY coding.
-
To set the limit from the database because my users have the option to set their own results per page for each controller.
-
To persist the same result set after a PRG from index.
Heres a copy/paste working example on a baked v4.5.9 which I originally designed in an early 3 branch.
Articles Table
Custom Finder
public function findMyArticles(Query $query, array $options): object
{
$query
->where([
'user_id' => $options['user_id']
]);
return $query;
}
Articles Controller
Index Method
public function index(string $menuLink = null)
{
/**
* This is for the post only to help explain reason 1.
*
* In my application I have parent accounts and subsidiary accounts as menu
* links and the only difference between them is one column. So rather than
* have two mvc's I separated the initial load from the other loads which
* enabled me to manipulate this incoming data and DRY code it.
*/
$menuLink = 'author';
if (empty($this->request->getQuery('page'))
&& is_null($this->request->getQuery('sort'))
&& is_null($this->request->getQuery('direction'))) {
/**
* INITIAL LOAD
*/
$page = '';
// For reason 1.
if ($menuLink === 'author') {
$sort = 'title';
$direction = 'asc';
} else {
$sort = 'created';
$direction = 'desc';
}
// For reason 2.
$limit = 2; // This value of 2 comes from the database in my app.
/**
* Reason 3.
*
* You MUST manually set the sort, direction and limit
* so they can be passed to the view and stored in hidden fields.
*
* Then they can be posted to the updatedelete method and appended
* to the redirect back to index.
*
* This enables the same result set to be displayed on index as
* there was before the post to the updatedelete method.
*
* For example.
* 1. If your on page 53 with a sort of id asc and limit of 2 and click the update button
* the database will be updated in the updatedelete method and after the redirect
* back to index the result set will be page 53 with a sort of id asc and limit of 2.
*
* 2. If your on page 53 with a sort of id asc and limit of 2 and click the delete button
* the row will be deleted in the updatedelete method and after the redirect
* back to index the result set will be page 1 with a sort of id asc and limit of 2.
*/
} elseif (($this->request->getQuery('sort') !== '')
&& is_string($this->request->getQuery('sort'))
&& !is_numeric($this->request->getQuery('sort'))
&& ($this->request->getQuery('direction') !== '')
&& is_string($this->request->getQuery('direction'))
&& !is_numeric($this->request->getQuery('direction'))) {
/**
* OTHER LOADS
*/
$page = $this->request->getQuery('page');
$sort = $this->request->getQuery('sort');
$direction = $this->request->getQuery('direction');
$limit = $this->request->getQuery('limit');
}
$query = $this->Articles->find('myArticles', [
'user_id' => 1
]);
$settings = $this->paginate = [
'sortableFields' => [
'id',
'user_id',
'title',
'slug',
'published',
'created',
'modified'
],
'page' => $page,
'sort' => $sort,
'direction' => $direction,
'limit' => $limit
];
$articles = $this->paginate($query, $settings);
$this->set(compact('articles'));
$this->set('page', $page);
$this->set('sort', $sort);
$this->set('direction', $direction);
$this->set('limit', $limit);
}
Updatedelete Method
public function updatedelete()
{
$this->request->allowMethod(['post']);
if (is_string($this->request->getData('updaterow'))
&& is_numeric($this->request->getData('updaterow'))
&& is_null($this->request->getData('deleterow'))) {
// Update row
$article = $this->Articles->get((int) $this->request->getData('updaterow'));
$article->published = 0;
if ($this->Articles->save($article)) {
$this->Flash->success(__('The article has been updated.'));
$page = $this->request->getData('page');
} else {
$this->Flash->error(__('The article could not be updated. Please, try again.'));
}
} elseif (is_string($this->request->getData('deleterow'))
&& is_numeric($this->request->getData('deleterow'))
&& is_null($this->request->getData('updaterow'))) {
// Delete row
$article = $this->Articles->get((int) $this->request->getData('deleterow'));
if ($this->Articles->delete($article)) {
$this->Flash->success(__('The article has been deleted.'));
$page = null;
} else {
$this->Flash->error(__('The article could not be deleted. Please, try again.'));
}
}
$redirect = $this->request->getQuery('redirect', [
'action' => 'index',
'?' => [
'page' => $page,
'sort' => $this->request->getData('sort'),
'direction' => $this->request->getData('direction'),
'limit' => $this->request->getData('limit')
]
]);
return $this->redirect($redirect);
}
Index View
$this->Paginator->options([ // ADDED THIS
'url' => [
'?' => [
'limit' => $limit
]
]
]);
?>
<div class="articles index content">
<?= $this->Html->link(__('New Article'), ['action' => 'add'], ['class' => 'button float-right']) ?>
<h3><?= __('Articles') ?></h3>
<div class="table-responsive">
<table>
<thead>
<tr>
<th><?= $this->Paginator->sort('id') ?></th>
<th><?= $this->Paginator->sort('user_id') ?></th>
<th><?= $this->Paginator->sort('title') ?></th>
<th><?= $this->Paginator->sort('slug') ?></th>
<th><?= $this->Paginator->sort('published') ?></th>
<th><?= $this->Paginator->sort('created') ?></th>
<th><?= $this->Paginator->sort('modified') ?></th>
<th class="actions"><?= __('Actions') ?></th>
</tr>
</thead>
<tbody>
<?php
echo $this->Form->create(null, [ // ADDED THIS
'url' => ['action' => 'updatedelete']
]);
foreach ($articles as $article): ?>
<tr>
<td><?= $this->Number->format($article->id) ?></td>
<td><?= $article->has('user') ? $this->Html->link($article->user->email, ['controller' => 'Users', 'action' => 'view', $article->user->id]) : '' ?></td>
<td><?= h($article->title) ?></td>
<td><?= h($article->slug) ?></td>
<td><?= h($article->published) ?></td>
<td><?= h($article->created) ?></td>
<td><?= h($article->modified) ?></td>
<td class="actions">
<?= $this->Html->link(__('View'), ['action' => 'view', $article->id]) ?>
<?= $this->Html->link(__('Edit'), ['action' => 'edit', $article->id]) ?>
<?= $this->Form->button('Update', [ // ADDED THIS
'type' => 'submit',
'name' => 'updaterow',
'value' => $article->id
]); ?>
<?= $this->Form->button('Delete', [ // ADDED THIS
'type' => 'submit',
'name' => 'deleterow',
'value' => $article->id
]); ?>
</td>
</tr>
<?php endforeach;
echo $this->Form->hidden('page', ['value' => $page]); // ADDED THIS
echo $this->Form->hidden('sort', ['value' => $sort]); // ADDED THIS
echo $this->Form->hidden('direction', ['value' => $direction]); // ADDED THIS
echo $this->Form->hidden('limit', ['value' => $limit]); // ADDED THIS
echo $this->Form->end(); // ADDED THIS
?>
</tbody>
</table>
</div>
<div class="paginator">
<ul class="pagination">
<?= $this->Paginator->first('<< ' . __('first')) ?>
<?= $this->Paginator->prev('< ' . __('previous')) ?>
<?= $this->Paginator->numbers() ?>
<?= $this->Paginator->next(__('next') . ' >') ?>
<?= $this->Paginator->last(__('last') . ' >>') ?>
</ul>
<p><?= $this->Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?></p>
</div>
</div>
Heres a copy/paste working example on a baked v5.1.6.
Articles Table
Custom Finder
public function findMyArticles(SelectQuery $query, $user_id): SelectQuery
{
$query
->where([
'user_id' => $user_id
]);
return $query;
}
Articles Controller
Pagination Property
protected array $paginate = [
'sortableFields' => [
'id',
'user_id',
'title',
'slug',
'published',
'created',
'modified'
]
];
Index Method
public function index(string $menuLink = null)
{
$menuLink = 'author';
if (is_null($this->request->getQuery('sort'))
&& is_null($this->request->getQuery('direction'))
&& is_null($this->request->getQuery('limit'))) {
/**
* INITIAL LOAD
*/
// For reason 1.
if ($menuLink === 'author') {
$sort = 'title';
$direction = 'asc';
} else {
$sort = 'created';
$direction = 'desc';
}
$redirect = $this->request->getQuery('redirect', [
'action' => 'index',
'?' => [
'sort' => $sort,
'direction' => $direction,
'limit' => 2 // This value of 2 comes from the database in my app.
]
]);
return $this->redirect($redirect);
exit(0);
}
/**
* OTHER LOADS
*/
$page = $this->request->getQuery('page');
$sort = $this->request->getQuery('sort');
$direction = $this->request->getQuery('direction');
$limit = $this->request->getQuery('limit');
$query = $this->Articles->find('myArticles',
user_id: 1
);
$articles = $this->paginate($query);
$this->set(compact('articles'));
$this->set('page', $page);
$this->set('sort', $sort);
$this->set('direction', $direction);
$this->set('limit', $limit);
}
Updatedelete Method
Exactly the same as v4 example above.
Index View
Exactly the same as v4 example above except the below is removed.
$this->Paginator->options([ // ADDED THIS
'url' => [
'?' => [
'limit' => $limit
]
]
]);
?>
Summary
After analysing the behaviour of the page, sort and direction in the v5 baked tutorial I noticed the following:
If the sort, direction and limit are in the url it will override these parameters in the pagination property. So all I needed to do was ensure there in the url.
I removed the sort, direction and limit from the pagination property and appended them to a redirect on initial load.