In the final article of this tutorial, we are going to create the frontend part of the app. First of all, let’s make a plan.
For this blog application, we’ll have a homepage displaying a list of all recent posts, a category page displaying a list of posts under a specific category, a tag page displaying a list of posts with a specific tag, a post page displaying the content of a specific post, and lastly, a search page displaying a list of posts based on a search query.
All of these pages will have a sidebar with a search box, a list of categories and tags. The post page will also have a related posts section at the bottom.
We’ve dealt with the database and the models in the previous article, so here we’ll start with the routes.
...// Homepage
Route::get('/', [PostController::class, 'home'])->name('home');
// A list of posts under this category
Route::get('/category/{category}', [CategoryController::class, 'category'])->name('category');
// A list of posts with this tag
Route::get('/tag/{tag}', [TagController::class, 'tag'])->name('tag');
// Display a single post
Route::get('/post/{post}', [PostController::class, 'post'])->name('post');
// A list of posts based on search query
Route::post('/search', [PostController::class, 'search'])->name('search');
...
<?php
namespace App\Http\Controllers;
use App\Models\Category;
use App\Models\Post;
use App\Models\Tag;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\Builder;
classPostControllerextends Controller
{
/**
* Display the home page
*/publicfunctionhome(): View
{
$posts= Post::where('is_published', true)->paginate(env('PAGINATE_NUM'));
$categories= Category::all();
$tags= Tag::all();
return view('home', [
'posts'=>$posts,
'categories'=>$categories,
'tags'=>$tags ]);
}
...}
Line 22, there are two things you need to note here.
First, where('is_published', true) makes sure that only published articles are retrieved.
And second, paginate() method is one of Laravel’s built-in method, allowing you to easily create pagination in your app. The paginate() takes an integer as input. For example, paginate(10) means 10 items will be displayed on each page. Since this input variable will be used on many pages, you can create an environmental variable in the .env file, and then you can retrieve it anywhere using env() method.
.env
1
2
. . .
PAGINATE_NUM=12
Next, let’s create the corresponding homepage view. Here is the template structure I’ve created, and this is the view structure:
Line 8, by default, Laravel uses Vite for asset bundling. In the previous article, we have installed Laravel Breeze, which uses TailwindCSS. This line of code will automatically import the corresponding app.css and app.js for you.
You can use a different framework of course, but you’ll have to consult the respective documentations for details on how to use them with Laravel or Vite.
Line 6, Storage::url($post->cover) generates the URL that points to the cover image.
Line 9, the way Laravel stores timestamps is not user friendly, so here we are using Carbon to reformat the timestamps.
Line 13, here we are using strip_tags() to remove the HTML tags, and then limit() sets the maximum length of the string, the excessive part will be replaced with ....
Line 21, remember be used paginate() method to create paginator in the controller? this is how we can display the paginator in the view.
<?php
namespace App\Http\Controllers;
use App\Models\Category;
use App\Models\Tag;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
classCategoryControllerextends Controller
{
/**
* Display a list of posts belong to the category
*/publicfunctioncategory(string $id): View
{
$category= Category::find($id);
$posts=$category->posts()->where('is_published', true)->paginate(env('PAGINATE_NUM'));
$categories= Category::all();
$tags= Tag::all();
return view('category', [
'category'=>$category,
'posts'=>$posts,
'categories'=>$categories,
'tags'=>$tags ]);
}
...}
<?php
namespace App\Http\Controllers;
use App\Models\Category;
use App\Models\Tag;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
classTagControllerextends Controller
{
/**
* Display a list of posts belong to the category
*/publicfunctiontag(string $id): View
{
$tag= Tag::find($id);
$posts=$tag->posts()->where('is_published', true)->paginate(env('PAGINATE_NUM'));
$categories= Category::all();
$tags= Tag::all();
return view('tag', [
'tag'=>$tag,
'posts'=>$posts,
'categories'=>$categories,
'tags'=>$tags ]);
}
...}
<?php
namespace App\Http\Controllers;
use App\Models\Category;
use App\Models\Post;
use App\Models\Tag;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\Builder;
classPostControllerextends Controller
{
.../**
* Display requested post
*/publicfunctionpost(string $id): View
{
$post= Post::find($id);
$categories= Category::all();
$tags= Tag::all();
$related_posts= Post::where('is_published', true)->whereHas('tags', function (Builder $query) use ($post) {
return$query->whereIn('name', $post->tags->pluck('name'));
})->where('id', '!=', $post->id)->take(3)->get();
return view('post', [
'post'=>$post,
'categories'=>$categories,
'tags'=>$tags,
'related_posts'=>$related_posts ]);
}
...}
Line 27-29, this is how we are able to retrieve related posts. The idea is to get the posts with the same tags. This chain of methods looks kind of scary, but don’t worry, let’s analyze them one by one.
The first method is easy, where('is_published', true) returns all the posts that are published.
The whereHas() method is where things get complicated. To understand whereHas() we need to first talk about has(). has() is a Laravel Eloquent method that allows us to check the existence of a relationship. For example:
1
$posts= Post::has('comments', '>', 3)->get();
This code will retrieve all the posts that have more than 3 comments. Notice that you cannot use where() to do this because comments is not a column in the posts table, it is another table that has a relation with posts.
whereHas() works just like has(), only it offers a little more power. Its second parameter is a function that allows us to inspect the content that “another table”, which in our case, is the tags table. We can access the tags table through the variable $q.
Line 28, the whereIn() method takes two parameters, the first one is the specified column, the second is an array of acceptable values. The method will return the records with only the acceptable values and exclude the rest.
The rest should be very easy to understand. where('id', '!=', $post->id) exclude the current post, and take(3) takes the first three records.