How to Create a Blog with Laravel 5+ Part 7 (Articles & Comment Form) Part 1

Advertisement

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
Advertisement

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 to post_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 ^_^

Advertisement