I am using Laravel 5.8. Task was to make pagination like next http://some-url/page-N instead of http://some-url?page=N. It cannot be accomplished by editing /resources/views/vendor/pagination/blade-name-here.blade.php template (it could be generated by
php artisan vendor:publish --tag=laravel-pagination command). Here I had to extend core classes.
My model used paginate method of DB instance, like next:
$essays = DB::table($this->table)
->select('essays.*', 'categories.name', 'categories.id as category_id')
->join('categories', 'categories.id', '=', 'essays.category_id')
->where('category_id', $categoryId)
->where('is_published', $isPublished)
->orderBy('h1')
->paginate( // here I need to extend this method
$perPage,
'*',
'page',
$page
);
Let's get started. paginate() method placed inside of \Illuminate\Database\Query\Builder and returns Illuminate\Pagination\LengthAwarePaginator object. LengthAwarePaginator extends Illuminate\Pagination\AbstractPaginator, which has public function url($page) method, which need to be extended:
/**
* Get the URL for a given page number.
*
* @param int $page
* @return string
*/
public function url($page)
{
if ($page <= 0) {
$page = 1;
}
// If we have any extra query string key / value pairs that need to be added
// onto the URL, we will put them in query string form and then attach it
// to the URL. This allows for extra information like sortings storage.
$parameters = [$this->pageName => $page];
if (count($this->query) > 0) {
$parameters = array_merge($this->query, $parameters);
}
// this part should be overwrited
return $this->path
. (Str::contains($this->path, '?') ? '&' : '?')
. Arr::query($parameters)
. $this->buildFragment();
}
Step by step guide (part of information I took from this nice article):
- Create Extended folder in app directory.
- In Extended folder create 3 files CustomConnection.php, CustomLengthAwarePaginator.php, CustomQueryBuilder.php:
2.1 CustomConnection.php file:
namespace App\Extended;
use \Illuminate\Database\MySqlConnection;
/**
* Class CustomConnection
* @package App\Extended
*/
class CustomConnection extends MySqlConnection {
/**
* Get a new query builder instance.
*
* @return \App\Extended\CustomQueryBuilder
*/
public function query() {
// Here core QueryBuilder is overwrited by CustomQueryBuilder
return new CustomQueryBuilder(
$this,
$this->getQueryGrammar(),
$this->getPostProcessor()
);
}
}
2.2 CustomLengthAwarePaginator.php file - this file contains main part of information which need to be overwrited:
namespace App\Extended;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
/**
* Class CustomLengthAwarePaginator
* @package App\Extended
*/
class CustomLengthAwarePaginator extends LengthAwarePaginator
{
/**
* Get the URL for a given page number.
* Overwrited parent class method
*
*
* @param int $page
* @return string
*/
public function url($page)
{
if ($page <= 0) {
$page = 1;
}
// here the MAIN overwrited part of code BEGIN
$parameters = [];
if (count($this->query) > 0) {
$parameters = array_merge($this->query, $parameters);
}
$path = $this->path . "/{$this->pageName}-$page";
// here the MAIN overwrited part of code END
if($parameters) {
$path .= (Str::contains($this->path, '?') ? '&' : '?') . Arr::query($parameters);
}
$path .= $this->buildFragment();
return $path;
}
}
2.3 CustomQueryBuilder.php file:
namespace App\Extended;
use Illuminate\Container\Container;
use \Illuminate\Database\Query\Builder;
/**
* Class CustomQueryBuilder
* @package App\Extended
*/
class CustomQueryBuilder extends Builder
{
/**
* Create a new length-aware paginator instance.
* Overwrite paginator's class, which will be used for pagination
*
* @param \Illuminate\Support\Collection $items
* @param int $total
* @param int $perPage
* @param int $currentPage
* @param array $options
* @return \Illuminate\Pagination\LengthAwarePaginator
*/
protected function paginator($items, $total, $perPage, $currentPage, $options)
{
// here changed
// CustomLengthAwarePaginator instead of LengthAwarePaginator
return Container::getInstance()->makeWith(CustomLengthAwarePaginator::class, compact(
'items', 'total', 'perPage', 'currentPage', 'options'
));
}
}
In /config/app.php need to change db provider:
'providers' => [
// comment this line
// illuminate\Database\DatabaseServiceProvider::class,
// and add instead:
App\Providers\CustomDatabaseServiceProvider::class,
In your controller (or other place where you receive paginated data from db) you need to change pagination's settings:
// here are paginated results
$essaysPaginated = $essaysModel->getEssaysByCategoryIdPaginated($id, config('custom.essaysPerPage'), $page);
// init your current page url (without pagination part)
// like http://your-site-url/your-current-page-url
$customUrl = "/my-current-url-here";
// set url part to paginated results before showing to avoid
// pagination like http://your-site-url/your-current-page-url/page-2/page-3 in pagination buttons
$essaysPaginated->withPath($customUrl);
Add pagination links in your view (/resources/views/your-controller/your-blade-file.blade.php), like next:
<nav>
{!!$essays->onEachSide(5)->links('vendor.pagination.bootstrap-4')!!}
</nav>
ENJOY! :) Your custom pagination should work now