Courses

[FREE] Laravel 11 For Beginners: Your First Project

Second Model, GET Parameters and Eloquent

It's time to introduce a second DB table and a relationship. After working with Categories, it is time to take care of the Posts.

We will create an Eloquent Model for posts and define a relationship. Every post should belong to one of the categories. And lastly, on the page, we will build a link to show posts by the selected category.


Creating Model and Migration

First, let's create a Model and Migration. We can do it with two artisan commands, or we can do it with one command make:model and provide -m, which means migration.

php artisan make:model Post -m

Now, we can fill the migration. Besides regular string and text columns, we need one for the category relation.

Laravel has a syntax to create foreign keys. There are a few options, but I prefer the method foreignId() with constrained(). It will create both the DB column and the foreign key.

The name of the relation column should have a format of "xxxxx_id", where "xxxxx" is a singular form of the relations table. And then, you define a constrained(), which is a shorter Laravel method for ->references('id')->on('categories').

database/migrations/xxx_create_posts_table.php:

public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('text');
$table->foreignId('category_id')->constrained();
$table->timestamps();
});
}

We can run the migrations.

php artisan migrate

I have added posts manually and assigned each post to a different category. For now, I did it manually because now we are talking about getting the data. Later, we will talk about how to create it.


Getting Posts in Controller

We need to get posts in the same HomeController alongside categories. We can use the all() method on a Post Model or get the latest posts first.

app/Http/Controllers/HomeController.php:

use App\Models\Post;
 
class HomeController extends Controller
{
public function index()
{
$allCategories = Category::all();
$posts = Post::orderBy('id', 'desc')->get();
 
return view('home', ['categories' => $allCategories]);
}
}

Alternative syntax for the orderBy(): we can use an Eloquent method called latest().

app/Http/Controllers/HomeController.php:

use App\Models\Post;
 
class HomeController extends Controller
{
public function index()
{
$allCategories = Category::all();
$posts = Post::orderBy('id', 'desc')->get();
$posts = Post::latest()->get();
 
return view('home', ['categories' => $allCategories]);
}
}

And we need to pass those posts to a View. We could add posts to the same array.

return view('home', [
'categories' => $allCategories,
'posts' => $posts
]);

But there is a shorter way using the PHP function compact(), just listing the variables one by one. This function can be used if the names of the variables are the same as array keys, so we need to rename the $allCategories variable to $categories.

app/Http/Controllers/HomeController.php:

class HomeController extends Controller
{
public function index()
{
$allCategories = Category::all();
$categories = Category::all();
$posts = Post::latest()->get();
 
return view('home', ['categories' => $allCategories]);
return view('home', compact('categories', 'posts'));
}
}

Show Posts in Blade

Now, let's show the posts in Blade. Also, along the way, let's do some visual cleanup and delete the "Featured post" code and pagination with search and side widgets from the Bootstrap theme because we won't have those features in our demo.

resources/views/home.blade.php:

@extends('layouts.app')
 
@section('content')
<!-- Page header with logo and tagline-->
<header class="py-5 bg-light border-bottom mb-4">
<div class="container">
<div class="text-center my-5">
<h1 class="fw-bolder">Welcome to Blog Home!</h1>
<p class="lead mb-0">A Bootstrap 5 starter layout for your next blog homepage</p>
</div>
</div>
</header>
<!-- Page content-->
<div class="container">
<div class="row">
<!-- Blog entries-->
<div class="col-lg-8">
<!--
<div class="card mb-4">
<a href="#!"><img class="card-img-top" src="https://dummyimage.com/850x350/dee2e6/6c757d.jpg" alt="..." /></a>
<div class="card-body">
<div class="small text-muted">January 1, 2023</div>
<h2 class="card-title">Featured Post Title</h2>
<p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reiciendis aliquid atque, nulla? Quos cum ex quis soluta, a laboriosam. Dicta expedita corporis animi vero voluptate voluptatibus possimus, veniam magni quis!</p>
<a class="btn btn-primary" href="#!">Read more →</a>
</div>
</div>
<!-- Nested row for non-featured blog posts-->
<div class="row">
@foreach($posts as $post)
<div class="col-lg-6">
<!-- Blog post-->
<div class="card mb-4">
<a href="#!"><img class="card-img-top" src="https://dummyimage.com/700x350/dee2e6/6c757d.jpg" alt="..." /></a>
<div class="card-body">
<div class="small text-muted">{{ $post->created_at }}</div>
<h2 class="card-title h4">{{ $post->title }}</h2>
<p class="card-text">{{ $post->text }}</p>
<a class="btn btn-primary" href="#!">Read more →</a>
</div>
</div>
</div>
@endforeach
<div class="col-lg-6">
<!-- Blog post-->
<div class="card mb-4">
<a href="#!"><img class="card-img-top" src="https://dummyimage.com/700x350/dee2e6/6c757d.jpg" alt="..." /></a>
<div class="card-body">
<div class="small text-muted">January 1, 2023</div>
<h2 class="card-title h4">Post Title</h2>
<p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reiciendis aliquid atque, nulla.</p>
<a class="btn btn-primary" href="#!">Read more →</a>
</div>
</div>
<!-- Blog post-->
<div class="card mb-4">
<a href="#!"><img class="card-img-top" src="https://dummyimage.com/700x350/dee2e6/6c757d.jpg" alt="..." /></a>
<div class="card-body">
<div class="small text-muted">January 1, 2023</div>
<h2 class="card-title h4">Post Title</h2>
<p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reiciendis aliquid atque, nulla.</p>
<a class="btn btn-primary" href="#!">Read more →</a>
</div>
</div>
</div>
<div class="col-lg-6">
<!-- Blog post-->
<div class="card mb-4">
<a href="#!"><img class="card-img-top" src="https://dummyimage.com/700x350/dee2e6/6c757d.jpg" alt="..." /></a>
<div class="card-body">
<div class="small text-muted">January 1, 2023</div>
<h2 class="card-title h4">Post Title</h2>
<p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reiciendis aliquid atque, nulla.</p>
<a class="btn btn-primary" href="#!">Read more →</a>
</div>
</div>
<!-- Blog post-->
<div class="card mb-4">
<a href="#!"><img class="card-img-top" src="https://dummyimage.com/700x350/dee2e6/6c757d.jpg" alt="..." /></a>
<div class="card-body">
<div class="small text-muted">January 1, 2023</div>
<h2 class="card-title h4">Post Title</h2>
<p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reiciendis aliquid atque, nulla? Quos cum ex quis soluta, a laboriosam.</p>
<a class="btn btn-primary" href="#!">Read more →</a>
</div>
</div>
</div>
</div>
<!--
<nav aria-label="Pagination">
<hr class="my-0" />
<ul class="pagination justify-content-center my-4">
<li class="page-item disabled"><a class="page-link" href="#" tabindex="-1" aria-disabled="true">Newer</a></li>
<li class="page-item active" aria-current="page"><a class="page-link" href="#!">1</a></li>
<li class="page-item"><a class="page-link" href="#!">2</a></li>
<li class="page-item"><a class="page-link" href="#!">3</a></li>
<li class="page-item disabled"><a class="page-link" href="#!">...</a></li>
<li class="page-item"><a class="page-link" href="#!">15</a></li>
<li class="page-item"><a class="page-link" href="#!">Older</a></li>
</ul>
</nav>
</div>
<!-- Side widgets-->
<div class="col-lg-4">
<!--
<div class="card mb-4">
<div class="card-header">Search</div>
<div class="card-body">
<div class="input-group">
<input class="form-control" type="text" placeholder="Enter search term..." aria-label="Enter search term..." aria-describedby="button-search" />
<button class="btn btn-primary" id="button-search" type="button">Go!</button>
</div>
</div>
</div>
<!-- Categories widget-->
<div class="card mb-4">
<div class="card-header">Categories</div>
<div class="card-body">
<div class="row">
<div class="col-sm-6">
<ul class="list-unstyled mb-0">
@foreach($categories as $category)
<li><a href="#!">{{ $category->name }}</a></li>
@endforeach
</ul>
</div>
</div>
</div>
</div>
<!--
<div class="card mb-4">
<div class="card-header">Side Widget</div>
<div class="card-body">You can put anything you want inside of these side widgets. They are easy to use, and feature the Bootstrap 5 card component!</div>
</div>
</div>
</div>
</div>
@endsection

On the home page, we can see the posts from the database.


Showing Posts By Category

Now, we will add a link to view the category with its posts. The link will have a Route to the home page with a parameter. There are a few ways to add a parameter, but we will use the easiest way: with the URL like /?category_id=XXXXX

resources/views/home.blade.php:

// ...
 
<!-- Categories widget-->
<div class="card mb-4">
<div class="card-header">Categories</div>
<div class="card-body">
<div class="row">
<div class="col-sm-6">
<ul class="list-unstyled mb-0">
@foreach($categories as $category)
<li><a href="#!">{{ $category->name }}</a></li>
<li><a href="{{ route('home') }}?category_id={{ $category->id }}">{{ $category->name }}</a></li>
@endforeach
</ul>
</div>
</div>
</div>
</div>
 
// ...

Then, in the Controller, we can filter posts with a where() method and get a category from a request using the request() helper.

app/Http/Controllers/HomeController.php:

class HomeController extends Controller
{
public function index()
{
$categories = Category::all();
$posts = Post::latest()->get();
$posts = Post::where('category_id', request('category_id'))->latest()->get();
 
return view('home', compact('categories', 'posts'));
}
}

After refreshing the home page, we see an empty posts list because we have no category parameter.

But if you click on a category, the posts that belong to that category are shown.


Fix Showing All Posts

Now, let's fix the page to show all categories when the category isn't selected. This will be an example of how powerful Eloquent is.

Eloquent has a when() method, which accepts the condition as a first parameter. If a condition is true, Eloquent will execute a closure function, a second parameter of the when() method.

app/Http/Controllers/HomeController.php:

class HomeController extends Controller
{
public function index()
{
$categories = Category::all();
$posts = Post::where('category_id', request('category_id'))->latest()->get();
$posts = Post::when(request('category_id'), function ($query) {
$query->where('category_id', request('category_id'));
})->latest()->get();
 
return view('home', compact('categories', 'posts'));
}
}

This syntax means when a category_id from requests exists, the where condition will be added to the query.

After refreshing the home page, all posts are shown, and when the category is selected, only posts that belong to the selected category are shown. All done!

No comments or questions yet...