WordPress-Like Blog Laravel 5.7 and AdminLTE 3 (6) – Post Categories and Markdown
In this sixth part of creating WordPress-Like Blog using Laravel 5.6/5.7 and AdminLTE 3, we will :
- Enable Markdown Support
- Create the Categories of the posts
Enable Markdown Support
Markdown is a lightweight markup language with plain text formatting syntax. It is designed so that it can be converted to HTML and many other formats using a tool by the same name. Markdown is used in most readme.md file when you open github. We will use markdown on our project to be used in the post body.
Tool to translate markdown into html, we will use LaravelMarkdown. Open the terminal and type the command :
1 |
composer require graham-campbell/markdown |
After installation has finished, open config/app.php and insert these two lines under providers and aliases.
1 2 3 |
GrahamCampbell\Markdown\MarkdownServiceProvider::class, 'Markdown' => GrahamCampbell\Markdown\Facades\Markdown::class, |
Next, open show.blade.php, on the $post->body part, change into the following syntax.
1 2 |
<!-- Post Content --> {!! $post->body_html !!} |
{!! !!}} means renders directly to html.
Before we run the url, add this method to app\Post.php
1 2 3 4 5 6 7 |
public function getBodyHtmlAttribute($value){ return $this->body ? Markdown::convertToHtml(e($this->body)) : NULL ; } public function getExcerptHtmlAttribute($value){ return $this->excerpt ? Markdown::convertToHtml(e($this->excerpt)) : NULL ; } |
Don’t forget to add the markdown namespace :
1 |
use GrahamCampbell\Markdown\Facades\Markdown; |
Open index.blade.php and insert the excerpt_html method as well :
1 |
<p class="card-text">{!! $post->excerpt_html !!}</p> |
Open localhost:8000, there will be no errors occured, you have successfully implemented markdown support.
Post Categories
We will add master categories and relate it to the post model.
First step we have to do is create the categories table.
1 |
php artisan make:migration create_categories --create=categories |
Create another migration to add a new column in the post table.
1 |
php artisan make:migration alter_posts_add_category_id --table=posts |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AlterPostsAddCategoryId extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('posts', function (Blueprint $table) { $table->integer('category_id')->unsigned(); $table->foreign('category_id')->references('id')->on('categories')->onDelete('restrict'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('posts', function (Blueprint $table) { $table->dropForeign(['category_id']); $table->dropColumn('category_id'); }); } } |
Let’s create a new seeder, CategoriesTableSeeder :
1 |
php artisan make:seeder CategoriesTableSeeder |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<?php use Illuminate\Database\Seeder; class CategoriesTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('categories')->truncate(); DB::table('categories')->insert([ [ 'title' => 'Web Design', 'slug' => 'web-design', ], [ 'title' => 'Web Programming', 'slug' => 'web-programming', ], [ 'title' => 'Internet', 'slug' => 'internet', ], [ 'title' => 'Digital Marketing', 'slug' => 'digital-marketing', ], [ 'title' => 'Social Media', 'slug' => 'social-media', ], ]); //update post data for($post_id =1; $post_id <= 10; $post_id++){ $category_id = rand(1,5); DB::table('posts') ->where('id',$post_id) ->update(['category_id'=>$category_id]); } } } |
Add the CategoriesTableSeeder to DatabaseSeeder.php :
1 2 3 4 5 6 |
public function run() { $this->call(UsersTableSeeder::class); $this->call(PostsTableSeeder::class); $this->call(CategoriesTableSeeder::class); } |
Next, open config/database.php and change ‘strict’ to false :
1 |
'strict' => false, |
Run the migration and seed :
1 |
php artisan migrate:refresh --seed |
Create the category Model :
1 |
php artisan make:model Category |
Define the relationship to post model, also add getRouteKeyName method to filter by categories later.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Category extends Model { public function posts(){ return $this->hasMany(Post::class); } public function getRouteKeyName(){ return 'slug'; } } |
Open Post model and define a relationship to category model :
1 2 3 |
public function category(){ return $this->belongsTo(Category::class); } |
Next, we will display the categories on the sidebar. Previously we need to modify BlogController action index() and add category() method, don’t forget to add use App\Category.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public function index(){ $categories = Category::with(['posts' => function($query){ $query->published(); }]) ->orderBy('title','asc') ->get(); $posts = Post::with('author') ->latestFirst() ->published() ->paginate($this->limit); return view("blog.index", compact('posts','categories')); } public function category(Category $category){ $categoryName = $category->title; $categories = Category::with(['posts' => function($query){ $query->published(); }]) ->orderBy('title','asc') ->get(); $posts = $category->posts() ->with('author') ->latestFirst() ->published() ->paginate($this->limit); return view("blog.index", compact('posts','categories','categoryName')); } |
Add a new route in routes/web.php
1 2 3 4 |
Route::get('/category/{category}',[ 'uses' => 'BlogController@category', 'as' => 'category' ]); |
Add this category widget in views/layouts/sidebar.blade.php
1 2 3 4 5 6 7 8 9 10 |
<div class="card my-4"> <h5 class="card-header">Categories</h5> <div> <ul class="list-group list-group-flush"> @foreach($categories as $category) <li class="list-group-item"><a href="{{ route('category', $category->slug)}}">{{ $category->title }} <span class="badge badge-pill badge-secondary float-right">{{ $category->posts->count() }}</span></a></li> @endforeach </ul> </div> </div> |
Now, we will create an alert to give information in which category we are in the page :
Open index.blade.php and add alert :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
@extends('layouts.main') @section('content') <!-- Page Content --> <div class="container"> <div class="row"> <!-- Blog Entries Column --> <div class="col-md-8"> <h1 class="my-4"> </h1> @if(!$posts->count()) <div class="alert alert-warning"> <p>Nothing Found</p> </div> @else @if(isset($categoryName)) <div class="alert alert-info"> <p>Category: <strong>{{ $categoryName }}</strong></p> </div> @endif @foreach($posts as $post) <!-- Blog Post --> <div class="card mb-4"> @if($post->image_url) <img class="card-img-top" src="{{ $post->image_url }}" alt="{{ $post->slug }}"> @endif <div class="card-body"> <h2 class="card-title"><a href="{{ route('blog.show', $post->slug) }}">{{ $post->title }}</a></h2> <p class="card-text">{!! $post->excerpt_html !!}</p> <a href="{{ route('blog.show', $post->slug) }}" class="btn btn-primary">Read More →</a> </div> <div class="card-footer text-muted"> Posted on {{ $post->date }} in category <a href="{{ route('category', $post->category->slug )}}">{{ $post->category->title }}</a> </div> </div> @endforeach @endif <!-- Pagination --> <ul class="pagination justify-content-center mb-4"> {{ $posts->links() }} </ul> </div> @include('layouts.sidebar') </div> <!-- /.row --> </div> <!-- /.container --> @endsection |
There will be an error if we click the blog title right now. We will modify some codes to prevent this error and code duplication. In BlogController currently we have duplication on how to get the categories in every method. We will be using view composer to tidy things up.
Let’s create a new provider. Open terminal and type the command :
1 |
php artisan make:provider ComposerServiceProvider |
All necessary codes will be loaded in this Provider. Open the newly created file in the App\providers directory, don’t forget to use App\Category :
1 2 3 4 5 6 7 8 9 10 |
public function boot() { view()->composer('layouts.sidebar', function($view){ $categories = Category::with(['posts' => function($query){ $query->published(); }])->orderBy('title','asc')->get(); return $view->with('categories',$categories); }); } |
We need to register this provider to config/app.php
1 |
App\Providers\ComposerServiceProvider::class, |
Now let’s clean up our BlogController.php. The end result :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Post; use App\Category; class BlogController extends Controller { protected $limit = 5; public function index(){ $posts = Post::with('author') ->latestFirst() ->published() ->paginate($this->limit); return view("blog.index", compact('posts')); } public function category(Category $category){ $categoryName = $category->title; $posts = $category->posts() ->with('author') ->latestFirst() ->published() ->paginate($this->limit); return view("blog.index", compact('posts','categoryName')); } public function show(Post $post){ return view("blog.show", compact('post')); } } |
php artisan serve and open the project, everything should be working perfectly now.
Github Commit.