How to Create a Blog with Laravel 5+ Part 7 (Articles & Comment Form) Part 1
A must have functionality on a Blog not just it let your readers express their opinion, share knowledge related to the article written it also helps discover new blogs by sharing relevant sources and it’s a way to directly interact with the author. Aside from adding comment form, we’ll also work on Helper, I’ll talk more about Helper later.
Let’s get started
Articles
It’s a common relation that comment depends on the articles, so the first thing we need to do is display lists of Blog posts in a new menu item.
_header.blade.php
...
<div class="blog-masthead">
<div class="container">
<nav class="blog-nav">
<ul class="nav navbar-nav">
<li><a class="blog-nav-item {{ null == Request::query() ? 'active' : '' }}" href="/">Home</a></li>
<li><a class="blog-nav-item {{ Request::is('articles') ? 'active' : '' }}" href="/articles">Articles</a></li>
@if( Helper::get_pages() )
@foreach( Helper::get_pages() as $page )
<li><a class="blog-nav-item {{ $page->id == Request::query('page_id') ? 'active' : '' }}" href="/?page_id={{ $page->id }}">{{ $page->post_title }}</a></li>
@endforeach
@endif
</ul>
<!-- Right Side Of Navbar -->
<ul class="nav navbar-nav navbar-right">
<!-- Authentication Links -->
@if (Auth::guest())
<li><a class="blog-nav-item {{ Request::is('login') ? 'active' : '' }}" href="{{ route('login') }}">Login</a></li>
<li><a class="blog-nav-item {{ Request::is('register') ? 'active' : '' }}" href="{{ route('register') }}">Register</a></li>
@else
<li class="dropdown">
<a href="#" class="blog-nav-item dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
{{ Auth::user()->name }} <span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="{{ route('posts.index') }}">All Posts</a></li>
<li><a href="{{ route('posts.create') }}">Add New</a></li>
<li><a href="{{ route('categories.index') }}">Categories</a></li>
<li><hr/></li>
<li><a href="{{ route('pages.index') }}">All Pages</a></li>
<li><a href="{{ route('pages.create') }}">Add New</a></li>
<li><hr/></li>
<!--we'll work on this later-->
<li><a href="{{ route('comments.index') }}">All Comments</a></li>
<li><hr/></li>
<li>
<a class="blog-nav-item" href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">Logout</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
{{ csrf_field() }}
</form>
</li>
</ul>
</li>
@endif
</ul>
</nav>
</div>
</div>
...
ArticlesController
We’ll use this Controller for Articles and Article single page.
php artisan make:controller ArticlesController --resource
Now that ArticlesController
is ready, lets update our routes to reflect the new added Controller.
routes/web.php
Add articles and comments to our routes
<?php
...
Route::get('/', 'PagesController@getIndex')->name('home');
// Added specifically for articles and article single page
Route::get('article/{slug}', 'ArticlesController@getSingle');
Route::get('articles', 'ArticlesController@getIndex');
Route::resource('pages', 'PagesController');
Route::resource('posts', 'PostsController');
Route::resource('categories', 'CategoriesController');
// We'll talk these new route later
Route::resource('comments', 'CommentsController');
Auth::routes();
views/articles/
We’ll use this for our front-end display.
|--index.blade.php // We'll use this blade files to display lists of added blog posts or for Articles new menu ite.
|--single.blade.php // We'll use this blade file to display Article single page.
ArticlesController
Complete code for our Articles controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Post;
class ArticlesController extends Controller
{
/**
* Display articles
*/
public function getIndex() {
$posts = Post::where('post_type', 'post')
->orderBy('created_at', 'desc')
->paginate( 6 );
return view('articles.index', ['posts' => $posts]);
}
/**
* Display single article
*
* $post_slug STRING Article slug
*/
public function getSingle( $post_slug ) {
$post = Post::where('post_slug', '=', $post_slug)->first();
return view('articles.single', ['post' => $post]);
}
}
articles/index.blade.php
Complete code to display all blog posts.
@extends('main')
@section('title', '| Blog Tutorial')
@section('content')
<div class="blog-header">
<h1 class="blog-title">Articles</h1>
<p class="lead blog-description">The official example template of creating a blog with Bootstrap.</p>
</div>
<div class="row">
<div class="col-sm-8 blog-main">
@if( $posts->count() )
@foreach( $posts as $post )
<div class="blog-post">
<h2 class="blog-post-title">
<a href="/article/{{ $post->post_slug }}">{{ $post->post_title }}</a>
</h2>
<p class="blog-post-meta">{{ date('M j, Y', strtotime( $post->created_at )) }} by <a href="#">{{ $post->author_ID }}</a></p>
<div class="blog-content">
{!! nl2br( $post->post_content ) !!}
</div>
</div>
@endforeach
@else
<p>No post added yet!</p>
@endif
{{-- Display pagination only if more than the required pagination --}}
@if( $posts->total() > 6 )
<nav>
<ul class="pager">
@if( $posts->firstItem() > 1 )
<li><a href="{{ $posts->previousPageUrl() }}">Previous</a></li>
@endif
@if( $posts->lastItem() < $posts->total() )
<li><a href="{{ $posts->nextPageUrl() }}">Next</a></li>
@endif
</ul>
</nav>
@endif
</div><!-- /.blog-main -->
<!--Sidebar-->
@include('partials._sidebar')
</div><!-- /.row -->
@endsection
articles/single.blade.php
Complete code to display single blog post and now with comment form.
@extends('main')
@section('title')
| {{ $post->post_title }}
@endsection
@section('content')
<div class="blog-header">
<h1 class="blog-title">{{ $post->post_title }}</h1>
<p>{{ $post->category_ID }} / {{ date('M j, Y', strtotime( $post->created_at )) }} <a href="{{ route('posts.edit', $post->id) }}">{Edit}</a></p>
</div>
<div class="row">
<div class="col-sm-8 blog-main">
<div class="blog-content">
{!! nl2br( $post->post_content ) !!}
</div><!-- /.blog-post -->
<section class="mt-5" id="comment">
{{-- display comment form --}}
@includeIf('comments.form', ['post_id' => $post->id])
</section>
</div><!-- /.blog-main -->
<!--Sidebar-->
@include('partials._sidebar')
</div><!-- /.row -->
@endsection
One more thing…
pages/frontpage.blade.php
Let’s make our front-page posts dynamic too, and link to article single page.
@extends('main')
@section('title', '| Blog Tutorial')
@section('content')
<div class="blog-header">
<h1 class="blog-title">The Bootstrap Blog</h1>
<p class="lead blog-description">The official example template of creating a blog with Bootstrap.</p>
</div>
<div class="row">
<div class="col-sm-8 blog-main">
@if( $posts->count() )
@foreach( $posts as $post )
<div class="blog-post">
<h2 class="blog-post-title">
<a href="/article/{{ $post->post_slug }}">{{ $post->post_title }}</a>
</h2>
<p class="blog-post-meta">{{ date('M j, Y', strtotime( $post->created_at )) }} by <a href="#">{{ $post->author_ID }}</a></p>
<div class="blog-content">
{!! nl2br( $post->post_content ) !!}
</div>
</div>
@endforeach
@else
<p>No post added yet!</p>
@endif
{{-- Display pagination only if more than the required pagination --}}
@if( $posts->total() > 6 )
<nav>
<ul class="pager">
@if( $posts->firstItem() > 1 )
<li><a href="{{ $posts->previousPageUrl() }}">Previous</a></li>
@endif
@if( $posts->lastItem() < $posts->total() )
<li><a href="{{ $posts->nextPageUrl() }}">Next</a></li>
@endif
</ul>
</nav>
@endif
</div><!-- /.blog-main -->
<!--Sidebar-->
@include('partials._sidebar')
</div><!-- /.row -->
@endsection
Comments
A comment also has CRUD methods too and we’ll use resource flag so we’ll just have to call CRUD method in a route at once.
CommentsController
New controller is added at Http/Controllers/CommentsController.php
, no need to add Comment
Model though as we already added it in the previous part, Part 3 (Database Schema Design).
php artisan make:controller CommentsController --resource
views/comments/
Let’s create new directory comments
inside views and add the following blade files.
|-- create.blade.php
|-- edit.blade.php
|-- form.blade.php // We'll use this file to dislay Comment form on each Blog Post
|-- index.blade.php
comments/form.blade.php
Now that the form is already included, it’s time to work on the comment form.
<h2>Leave a Reply</h2>
<form action="{{ route('comments.store') }}" method="POST">
{{ csrf_field() }}
<input type="hidden" name="post_id" value="{{ $post_id }}" />
<div class="form-group{{ $errors->has('comment_author') ? ' has-error' : '' }}">
<label for="comment_author">Name *</label> <br/>
<input type="text" name="comment_author" value="{{ old('comment_author') }}" />
@if ($errors->has('comment_author'))
<span class="help-block">
<strong>{{ $errors->first('comment_author') }}</strong>
</span>
@endif
</div>
<div class="form-group{{ $errors->has('comment_author_email') ? ' has-error' : '' }}">
<label for="comment_author_email">Email Address *</label> <br/>
<input type="text" name="comment_author_email" value="{{ old('comment_author_email') }}" />
@if ($errors->has('comment_author_email'))
<span class="help-block">
<strong>{{ $errors->first('comment_author_email') }}</strong>
</span>
@endif
</div>
<div class="form-group">
<label for="comment_author_url">Website</label> <br/>
<input type="text" name="comment_author_url" value="{{ old('comment_author_url') }}" />
</div>
<div class="form-group">
<label for="comment_content">Message</label> <br/>
<textarea cols="60" rows="6" name="comment_content">{{ old('comment_content') }}</textarea>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Submit Comment" />
</div>
</form>
PagesController
As you’ve noticed we are now using post_slug
instead of the previous query string ?p=
to display single post, update your getIndex()
method from your PagesController
with the below code.
...
/**
* Front page
*/
public function getIndex( Request $request ) {
// Get query string
$page_id = $request->query('page_id');
// If page_id is viewed use `pages.page.php` file to handle the display
// otherwise use and display 404.blade.php file
if( $page_id ) :
$posts = Post::where('id', $page_id)
->where('post_type', 'page')
->first();
// Check if page exist
if( $posts )
return view('pages.page', [ 'page' => $posts ]);
else
return view('errors.404');
// If none of the above is true, then display most recently added Blot posts
else :
$posts = Post::where('post_type', 'post')
->paginate( 6 );
// It replaces the previous `index.blade.php` blade file that is now used in displaying lists of added pages
return view('pages.frontpage', [ 'posts' => $posts ]);
endif;
}
Wrap Up
A lot going on in this articles.
- Articles
- A new menu item for blog posts, we’ve also added
ArticlesController
to dipslay lists of blog posts and article single page. - Frontpage
- Updated
?p=
from previous article topost_slug
which is way more cleaner and SEO friendly. - CommentsController
- We’ve also added
CommentsController
for our comments, we’ll work this in the next part. - PagesController
- Updated
getIndex()
method and removed old query string that is used for article single page, page is still using query string though.
That’s it, happy coding ^_^