How to Paginate a Model with Relationship in Laravel

Laravel provides a built-in pagination system that fully supports Eloquent Models as a source. This includes data retrieved from relationships, such as a one-to-many relationship between a Book and its Category, for example.

The syntax to paginate a model and its relationship in Laravel looks like:

$paginated = Book::with('category')->paginate(4);

In this tutorial, we’ll build a complete example demonstrating how to paginate these Models in a user-friendly manner. We’ll cover everything from setting up migrations and models to creating controllers, views, and populating data using Laravel’s artisan commands.

Let’s get started!

Step 1: Create Laravel Project

Begin by creating a new Laravel project named Bookstore using the following artisan command:

composer create-project --prefer-dist laravel/laravel Bookstore

Step 2: Generate Model and Migration

Next, let’s create migrations and models for the Book and Category entities. Run the following commands in your terminal:

php artisan make:model Category -m

Pause a bit, then run the command below.

The pause is needed because the migration for books contains a foreign key that requires the categories table to exist. Laravel will run the oldest migration first and a pause ensures it has a later timestamp in the filename (Laravel will run the oldest first.)

php artisan make:model Book -m

Step 3: Add Migration Code

Open the migration files created in the database/migrations directory. Define the schema for the books and categories tables in the up() method and the drop schema in the down() method.

For the table categories, use:

database/migrations/2024_02_12_215916_create_categories_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('categories');
    }
};

For the table books, use:

laracoding/database/migrations/2024_02_12_215921_create_books_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('books', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->decimal('price', 6, 2);
            $table->foreignId('category_id')->constrained()->onDelete('cascade');
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('books');
    }
};

Step 4: Run Migrations

Now, create the tables in the database by running the following artisan command:

php artisan migrate

Step 5: Add Model Code

In this step we’ll add code to the models Book and Category to define the relationships between them, and set $fillable to specify which properties should allow mass assignment. This will make it easier for us to use Eloquent’s create() method to fill multiple values at once.

Now open the respective model files (app/Models/Book.php and app/Models/Category.php and add the code shown below.

For the Book model, use:

app/Models/Book.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Book extends Model
{
    protected $fillable = ['title', 'price', 'category_id'];

    public function category(): BelongsTo
    {
        return $this->belongsTo(Category::class);
    }
}

For the Category model, use:

app/Models/Category.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Category extends Model
{
    protected $fillable = ['name'];

    public function books(): HasMany
    {
        return $this->hasMany(Book::class);
    }
}

Step 6: Create Controller and Views

Now use the following artisan command to generate a controller named BookController:

php artisan make:controller BookController

Step 7: Add Controller Code

Now open the generated controller at app/Http/Controllers/BookController.php and add:

app/Http/Controllers/BookController.php
<?php

namespace App\Http\Controllers;

use App\Models\Book;

class BookController extends Controller
{
    public function index()
    {
        $books = Book::with('category')->paginate(4);
        return view('books.index', compact('books'));
    }
}

Step 8: Create a View

The Blade code provided below presents all books in a paginated view, applying Tailwind CSS styling, which is Laravel’s default.

Alternatively, if you prefer to use Bootstrap styles for your pagination, you can refer to my other article: How to Use Bootstrap Pagination in Laravel Blade.

To create the blade view file, navigate to resources/views/books/index.blade.php and add the following code:

resources/views/books/index.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Books</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5 ml-5 mr-5">
    <h1 class="text-3xl font-bold mb-5">Welcome to Book Store!</h1>
    <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
        @foreach($books as $book)
            <div class="flex justify-center">
                <div class="max-w-xs w-full bg-white shadow-md rounded-lg overflow-hidden mx-auto">
                    <div class="px-4 py-2">
                        <h3 class="font-bold text-xl mb-2">{{ $book->title }}</h3>
                        <p class="text-gray-600 text-sm">Author: {{ $book->author }}</p>
                        <p class="text-gray-600 text-sm">Price: ${{ $book->price }}</p>
                    </div>
                </div>
            </div>
        @endforeach
    </div>
    <div class="d-flex justify-content-center mt-3">
        {{ $books->links() }}
    </div>
</div>
</body>
</html>

Step 9: Create a Route

Now open routes/web.php and add the code shown below. This will define a route that will call the index() method of our BookController.

routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\BookController;

// Route to display paginated books
Route::get('/books', [BookController::class, 'index'])->name('books.index');

Step 10: Populate Sample Data

Now let’s populate the ‘categories’ and ‘books’ tables with some sample data. We’re going to use Laravel Tinker to manually insert data.

Run the following command to open Tinker:

php artisan tinker

Paste the following code and press enter:

PHP
use App\Models\Category;

// Create 2 categories
$fantasy = Category::create(['name' => 'Fantasy']);
$programming = Category::create(['name' => 'Programming']);

// Create 3 books in the `Fantasy` Category
$fantasy->books()->createMany([
    ['title' => 'Dune', 'author' => 'Frank Herbert', 'price' => 20.99],
    ['title' => 'The Lord of the Rings', 'author' => 'J.R.R. Tolkien', 'price' => 25.99],
    ['title' => 'Harry Potter', 'author' => 'J.K. Rowling', 'price' => 18.99],
]);

// Create 3 books in the `Programming` Category
$programming->books()->createMany([
    ['title' => 'Laravel Up and Running', 'author' => 'Matt Stauffer', 'price' => 30.99],
    ['title' => 'MySQL Essentials', 'author' => 'Tech Academy', 'price' => 22.99],
    ['title' => 'PHP Programming', 'author' => 'Larry Ullman', 'price' => 28.99],
]);

After running the code the database should look like:

The Contents of Our categories and books Tables After Inserting the Sample Data

Step 11: Test the Application

Now let’s start the application by running the following Artisan command:

php artisan serve

Afterwards you can visit http://127.0.0.1:8000/books in your browser to view our books table. It should look like this:

Our Paginated Books List With Eager Loaded Category Name

Conclusion

By following the steps outlined in this tutorial, you’ve built a Laravel application that presents paginated data from a Model and its relationship.

In this guide we only retrieved a small amount of records. To ensure the application’s performance holds up with much larger datasets, I’d recommend testing with larger amounts of data. To do this, one typically uses factories and seeders to fill your tables with arbitrary amounts of dummy data. This process is explained in more detail in my other post: How to Use Bootstrap Pagination in Laravel Blade (Tutorial).

Now go ahead and apply pagination using your own custom Model and its relation.

Happy coding!

References

Johan van den Broek

Johan is the creator of laracoding.com. As a child, he began tinkering with various programming languages, many of which have been long forgotten today. Currently, he works exclusively with PHP and Laravel, and his passion for programming remains to this day.

Leave a Reply

Your email address will not be published. Required fields are marked *

Recent Posts