The default paginator throws a 404 on "OutOfBounds", so when you happen to access the pagination with a too high page number.
What can be the issue with this?
So first: This is not very user friendly.
Often times this comes from an old indexed (e.g. Google) page, and therefore the user would rather expect to land on the last page here instead of having to try to guess how to fix the URL here instead.
Another issue is that in a delete of exactly the first element of the last page on that pagination list, the redirect "back" would also end up in a 404, since now the page count decreased in the meantime, either by that user or someone else or even the system (deactivation/cleanup through background tasks).
In all those cases the last actual page to automatically opening up would totally suffice and be much better in terms of usability.
Finally, the error logs are filling up on totally unnecessary info here.
I already separate my logs on actual issues vs 404s, and 404s that are internally triggered (referer is own site) end up on the actual error list. So having those false positives filtered out helps to alert me about the real things going boom or dead links that require fixing.
The solution: Redirect
The Shim plugin now ships with a trait to do that for you.
Add it to your AppController and enjoy the out-of-the-box magic.
...
use Shim\Controller\RedirectOutOfBoundsTrait;
class AppController extends Controller {
use RedirectOutOfBoundsTrait;
...
}
Some notes:
- It by default only redirects for non-ext URLs, so basically normal templates. It still throws (and should) 404s for e.g. JSON, XML views, since those are not for humans, and here the 404 is useful to indicate that this page is gone.
- It uses default 302 redirects as 301 would be a bit too harsh, those are usually cached and could result in wrong browser results when a page gets added again.
This trait is currently for CakePHP 5, but should be easily backportable to v4.
Other improvements the Shim plugin offers
Usually a custom Paginator has to be set in each controller separately.
The Shim.Controller, if extended, allows a more global configuration.
use Shim\Controller\Controller;
class AppController extends Controller {
}
In your config set Paginator.className
for what Paginator should be used across all controllers:
'Paginator' => [
'className' => \My\Special\CustomPaginator::class,
],
Your custom Paginator class should still extend Cake\Datasource\Paging\PaginatorInterface
, of course.
Note: The same improvement also directly comes with the RedirectOutOfBoundsTrait
above, so if you are using that one, you are already covered.