Today, Laravelcode share sommething new and very usefull tutorials with every one. Realtime comment system built using laravel and vuejs. comment system required every website. so, we are created here comment system with some awesome functionality. when user comment and post it then instently comment show in the page.
In this tutorials we are create one sapareted comment module and in this comment module we are cover following functionality. after user register then they will be able to do all functionality or action.
NOTE : In this tutorials we are not adding some file like css, we are cover some basiv stuff in this tutorials. if you get woring code so, please download from my github account we are also add link here Download working code
- Add new comments
- Reply to comments
- Up and Down votes
- Mark spam comments
In this our tutorials for make comment system we are using following technologies :
- Bootstrap
- Loadash
- Vue-resource
- Laravel Mix
Now we are starting our comment system tutorials step by step.
Step : 1 Create comments table migration
First, we need to create comment table for stare all comment data and aslo span and comment voting data. so, create new one migration by following command.
php artisan make:migration comments
Now copy following migration code and past in your migration file.
Schema::create('comments', function (Blueprint $table) {
$table->increments('id');
$table->text('comment');
$table->integer('votes')->default(0);
$table->integer('spam')->default(0);
$table->integer('reply_id')->default(0);
$table->string('page_id')->default(0);
$table->integer('users_id');
$table->timestamps();
});
Schema::create('comment_user_vote', function (Blueprint $table) {
$table->integer('comment_id');
$table->integer('user_id');
$table->string('vote',11);
});
Schema::create('comment_spam', function (Blueprint $table) {
$table->integer('comment_id');
$table->integer('user_id');
});
Now run your migration file using following command
php artisan migrate
Step : 2 Create Auth for Comment System
Now, we are created laravel built-in auth system using following command
php artisan make:auth
Step : 3 Create Models for the Comment System
Now, we are created model for comments table by run followign command
php artisan make:model Comment
Then open your app/Comment.php file and put into it following code.
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
protected $fillable = ['comment','votes','spam','reply_id','page_id','users_id'];
protected $dates = ['created_at', 'updated_at'];
public function replies()
{
return $this->hasMany('App\Comment','id','reply_id');
}
}
In above code we are define required fillable columns fields with $fillable and create one replies() method which has the one-many relationship with own.
Now create a model for comment_user_vote. Run the following command:
php artisan make:model CommentVote
Now open your app/CommentVote.php file and put into following code.
namespace App;
use Illuminate\Database\Eloquent\Model;
class CommentVote extends Model
{
protected $fillable = ['comment_id','user_id','vote'];
protected $table = "comment_user_vote";
public $timestamps = false;
}
Now, create last one model for comment_spam table
php artisan make:model CommentSpam
Now open your app/CommentSpam.php file and put into following code.
namespace App;
use Illuminate\Database\Eloquent\Model;
class CommentSpam extends Model
{
protected $fillable = ['comment_id','user_id'];
protected $table = "comment_spam";
public $timestamps = false;
}
Step : 4 Create the Routes for Comments
Now, we are creating following route for required in out comment system. open your routes/web.php and past following route in it.
Route::get('/{pageId}', function($pageId) {
return view('page',['pageId' => $pageId]);
});
// Route for index page
Route::get('comments/{pageId}', 'CommentController@index');
// Route for store comment
Route::post('comments', 'CommentController@store');
// Route for update comment
Route::post('comments/{commentId}/{type}', 'CommentController@update');
Step : 5 Create the Controller for Comment
Now, create controller for comment by run following command :
php artisan make:controller CommentController
After run this command then your controller file automatic generated in app/Http/Controllers/CommentController.php open it and place in it following code.
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Comment;
use App\CommentVote;
use App\CommentSpam;
class CommentController extends Controller
{
public function index($pageId)
{
//
$comments = Comment::where('page_id',$pageId)->get();
$commentsData = [];
foreach ($comments as $key) {
$user = User::find($key->users_id);
$name = $user->name;
$replies = $this->replies($key->id);
$photo = $user->first()->photo_url;
// dd($photo->photo_url);
$reply = 0;
$vote = 0;
$voteStatus = 0;
$spam = 0;
if(Auth::user()) {
$voteByUser = CommentVote::where('comment_id',$key->id)->where('user_id',Auth::user()->id)->first();
$spamComment = CommentSpam::where('comment_id',$key->id)->where('user_id',Auth::user()->id)->first();
if($voteByUser) {
$vote = 1;
$voteStatus = $voteByUser->vote;
}
if($spamComment) {
$spam = 1;
}
}
if(sizeof($replies) > 0) {
$reply = 1;
}
if(!$spam) {
array_push($commentsData,[
"name" => $name,
"photo_url" => (string)$photo,
"commentid" => $key->id,
"comment" => $key->comment,
"votes" => $key->votes,
"reply" => $reply,
"votedByUser" =>$vote,
"vote" =>$voteStatus,
"spam" => $spam,
"replies" => $replies,
"date" => $key->created_at->toDateTimeString()
]);
}
}
$collection = collect($commentsData);
return $collection->sortBy('votes');
}
protected function replies($commentId)
{
$comments = Comment::where('reply_id',$commentId)->get();
$replies = [];
foreach ($comments as $key) {
$user = User::find($key->users_id);
$name = $user->name;
$photo = $user->first()->photo_url;
$vote = 0;
$voteStatus = 0;
$spam = 0;
if(Auth::user()) {
$voteByUser = CommentVote::where('comment_id',$key->id)->where('user_id',Auth::user()->id)->first();
$spamComment = CommentSpam::where('comment_id',$key->id)->where('user_id',Auth::user()->id)->first();
if($voteByUser) {
$vote = 1;
$voteStatus = $voteByUser->vote;
}
if($spamComment) {
$spam = 1;
}
}
if(!$spam) {
array_push($replies,[
"name" => $name,
"photo_url" => $photo,
"commentid" => $key->id,
"comment" => $key->comment,
"votes" => $key->votes,
"votedByUser" => $vote,
"vote" => $voteStatus,
"spam" => $spam,
"date" => $key->created_at->toDateTimeString()
]);
}
$collection = collect($replies);
return $collection->sortBy('votes');
}
public function store(Request $request)
{
$this->validate($request, [
'comment' => 'required',
'reply_id' => 'filled',
'page_id' => 'filled',
'users_id' => 'required',
]);
$comment = Comment::create($request->all());
if($comment)
return [ "status" => "true","commentId" => $comment->id ];
}
public function update(Request $request, $commentId, $type)
{
if($type == "vote"){
$this->validate($request, [
'vote' => 'required',
'users_id' => 'required',
]);
$comments = Comment::find($commentId);
$data = [
"comment_id" => $commentId,
'vote' => $request->vote,
'user_id' => $request->users_id,
];
if($request->vote == "up"){
$comment = $comments->first();
$vote = $comment->votes;
$vote++;
$comments->votes = $vote;
$comments->save();
}
if($request->vote == "down"){
$comment = $comments->first();
$vote = $comment->votes;
$vote--;
$comments->votes = $vote;
$comments->save();
}
if(CommentVote::create($data))
return "true";
}
if($type == "spam") {
$this->validate($request, [
'users_id' => 'required',
]);
$comments = Comment::find($commentId);
$comment = $comments->first();
$spam = $comment->spam;
$spam++;
$comments->spam = $spam;
$comments->save();
$data = [
"comment_id" => $commentId,
'user_id' => $request->users_id,
];
if(CommentSpam::create($data))
return "true";
}
}
}
Step : 6 Comment Component Using VueJS
[ADDCODE]
Run following command for install all package.json file dependencies.
npm install
Now, we are required vue-resource so, run following command for it
npm install --save vue-resource
Now, go to the this path resources/assets/js/components and create one blank file Comments.vue. now leave it blank. and open your resources/assets/js/app.js and add following code.
Add the following line after require('./bootstrap');
import VueResource from "vue-resource"
Now, Add the following line after window.Vue = require('vue');
Vue.use(VueResource);
Vue.component('comment', require('./components/Comments.vue'));
Step : 6 Write Code For Comments.vue
We are already create Comments.vue file before but then we are not put any code in it so, open Comments.vue file and put into following templet code
<template>
<div class="comments-app">
<h1>Comments</h1>
<!-- From -->
<div class="comment-form" v-if="user">
<!-- Comment Avatar -->
<div class="comment-avatar">
<img src="storage/commentbox.png">
</div>
<form class="form" name="form">
<div class="form-row">
<textarea class="input" placeholder="Add comment..." required v-model="message"></textarea>
<span class="input" v-if="errorComment" style="color:red">{{errorComment}}</span>
</div>
<div class="form-row">
<input class="input" placeholder="Email" type="text" disabled :value="user.name">
</div>
<div class="form-row">
<input type="button" class="btn btn-success" @click="saveComment" value="Add Comment">
</div>
</form>
</div>
<div class="comment-form" v-else>
<!-- Comment Avatar -->
<div class="comment-avatar">
<img src="storage/commentbox.png">
</div>
<form class="form" name="form">
<div class="form-row">
<a href="login">
<textarea class="input" placeholder="Add comment..." required></textarea>
</a>
</div>
</form>
</div>
<!-- Comments List -->
<div class="comments" v-if="comments" v-for="(comment,index) in commentsData">
<!-- Comment -->
<div v-if="!spamComments[index] || !comment.spam" class="comment">
<!-- Comment Avatar -->
<div class="comment-avatar">
<img src="storage/comment.png">
</div>
<!-- Comment Box -->
<div class="comment-box">
<div class="comment-text">{{comment.comment}}</div>
<div class="comment-footer">
<div class="comment-info">
<span class="comment-author">
<em>{{ comment.name}}</em>
</span>
<span class="comment-date">{{ comment.date}}</span>
</div>
<div class="comment-actions">
<ul class="list">
<li>Votes: {{comment.votes}}
<a v-if="!comment.votedByUser" v-on:click="voteComment(comment.commentid,'directcomment',index,0,'up')">Up Votes</a>
<a v-if="!comment.votedByUser" v-on:click="voteComment(comment.commentid,'directcomment',index,0,'down')">Down Votes</a>
</li>
<li>
<a v-on:click="spamComment(comment.commentId,'directcomment',index,0)">Spam</a>
</li>
<li>
<a v-on:click="openComment(index)">Reply</a>
</li>
</ul>
</div>
</div>
</div>=
<!-- From -->
<div class="comment-form comment-v" v-if="commentBoxs[index]">
<!-- Comment Avatar -->
<div class="comment-avatar">
<img src="storage/comment.png">
</div>
<form class="form" name="form">
<div class="form-row">
<textarea class="input" placeholder="Add comment..." required v-model="message"></textarea>
<span class="input" v-if="errorReply" style="color:red">{{errorReply}}</span>
</div>
<div class="form-row">
<input class="input" placeholder="Email" type="text" :value="user.name">
</div>
<div class="form-row">
<input type="button" class="btn btn-success" v-on:click="replyComment(comment.commentid,index)" value="Add Comment">
</div>
</form>
</div>
<!-- Comment - Reply -->
<div v-if="comment.replies">
<div class="comments" v-for="(replies,index2) in comment.replies">
<div v-if="!spamCommentsReply[index2] || !replies.spam" class="comment reply">
<!-- Comment Avatar -->
<div class="comment-avatar">
<img src="storage/comment.png">
</div>
<!-- Comment Box -->
<div class="comment-box" style="background: grey;">
<div class="comment-text" style="color: white">{{replies.comment}}</div>
<div class="comment-footer">
<div class="comment-info">
<span class="comment-author">
{{replies.name}}
</span>
<span class="comment-date">{{replies.date}}</span>
</div>
<div class="comment-actions">
<ul class="list">
<li>Total votes: {{replies.votes}}
<a v-if="!replies.votedByUser" v-on:click="voteComment(replies.commentid,'replycomment',index,index2,'up')">Up Votes</a>
<a v-if="!replies.votedByUser" v-on:click="voteComment(comment.commentid,'replycomment',index,index2,'down')">Down Votes</a>
</a>
</li>
<li>
<a v-on:click="spamComment(replies.commentid,'replycomment',index,index2)">Spam</a>
</li>
<li>
<a v-on:click="replyCommentBox(index2)">Reply</a>
</li>
</ul>
</div>
</div>
</div>
<!-- From -->
<div class="comment-form reply" v-if="replyCommentBoxs[index2]">
<!-- Comment Avatar -->
<div class="comment-avatar">
<img src="storage/comment.png">
</div>
<form class="form" name="form">
<div class="form-row">
<textarea class="input" placeholder="Add comment..." required v-model="message"></textarea>
<span class="input" v-if="errorReply" style="color:red">{{errorReply}}</span>
</div>
<div class="form-row">
<input class="input" placeholder="Email" type="text" :value="user.name">
</div>
<div class="form-row">
<input type="button" class="btn btn-success" v-on:click="replyComment(comment.commentid,index)" value="Add Comment">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
Step : 6 Add Script For Comments.vue
Now add following script code in Comments.vue file after closing template tag.
<script>
var _ = require('lodash');
export default {
props: ['commentUrl'],
data() {
return {
comments: [],
commentreplies: [],
comments: 0,
commentBoxs: [],
message: null,
replyCommentBoxs: [],
commentsData: [],
viewcomment: [],
show: [],
spamCommentsReply: [],
spamComments: [],
errorComment: null,
errorReply: null,
user: window.user
}
},
http: {
headers: {
'X-CSRF-TOKEN': window.csrf
}
},
methods: {
fetchComments() {
this.$http.get('comments/' + this.commentUrl).then(res => {
this.commentData = res.data;
this.commentsData = _.orderBy(res.data, ['votes'], ['desc']);
this.comments = 1;
});
},
showComments(index) {
if (!this.viewcomment[index]) {
Vue.set(this.show, index, "hide");
Vue.set(this.viewcomment, index, 1);
} else {
Vue.set(this.show, index, "view");
Vue.set(this.viewcomment, index, 0);
}
},
openComment(index) {
if (this.user) {
if (this.commentBoxs[index]) {
Vue.set(this.commentBoxs, index, 0);
} else {
Vue.set(this.commentBoxs, index, 1);
}
}
},
replyCommentBox(index) {
if (this.user) {
if (this.replyCommentBoxs[index]) {
Vue.set(this.replyCommentBoxs, index, 0);
} else {
Vue.set(this.replyCommentBoxs, index, 1);
}
}
},
saveComment() {
if (this.message != null && this.message != ' ') {
this.errorComment = null;
this.$http.post('comments', {
page_id: this.commentUrl,
comment: this.message,
users_id: this.user.id
}).then(res => {
if (res.data.status) {
this.commentsData.push({
"commentid": res.data.commentId,
"name": this.user.name,
"comment": this.message,
"votes": 0,
"reply": 0,
"replies": []
});
this.message = null;
}
});
} else {
this.errorComment = "Please enter a comment to save";
}
},
replyComment(commentId, index) {
if (this.message != null && this.message != ' ') {
this.errorReply = null;
this.$http.post('comments', {
comment: this.message,
users_id: this.user.id,
reply_id: commentId
}).then(res => {
if (res.data.status) {
if (!this.commentsData[index].reply) {
this.commentsData[index].replies.push({
"commentid": res.data.commentId,
"name": this.user.name,
"comment": this.message,
"votes": 0
});
this.commentsData[index].reply = 1;
Vue.set(this.replyCommentBoxs, index, 0);
Vue.set(this.commentBoxs, index, 0);
} else {
this.commentsData[index].replies.push({
"commentid": res.data.commentId,
"name": this.user.name,
"comment": this.message,
"votes": 0
});
Vue.set(this.replyCommentBoxs, index, 0);
Vue.set(this.commentBoxs, index, 0);
}
this.message = null;
}
});
} else {
this.errorReply = "Please enter a comment to save";
}
},
voteComment(commentId, commentType, index, index2, voteType) {
if (this.user) {
this.$http.post('comments/' + commentId + '/vote', {
users_id: this.user.id,
vote: voteType
}).then(res => {
if (res.data) {
if (commentType == 'directcomment') {
if (voteType == 'up') {
this.commentsData[index].votes++;
} else if (voteType == 'down') {
this.commentsData[index].votes--;
}
} else if (commentType == 'replycomment') {
if (voteType == 'up') {
this.commentsData[index].replies[index2].votes++;
} else if (voteType == 'down') {
this.commentsData[index].replies[index2].votes--;
}
}
}
});
}
},
spamComment(commentId, commentType, index, index2) {
console.log("spam here");
if (this.user) {
this.$http.post('comments/' + commentId + '/spam', {
users_id: this.user.id,
}).then(res => {
if (commentType == 'directcomment') {
Vue.set(this.spamComments, index, 1);
Vue.set(this.viewcomment, index, 1);
} else if (commentType == 'replycomment') {
Vue.set(this.spamCommentsReply, index2, 1);
}
});
}
},
},
mounted() {
console.log("mounted");
this.fetchComments();
}
}
</script>
NOTE : In this tutorials we are not adding some file like css, we are cover some basiv stuff in this tutorials. if you get woring code so, please download from my github account we are also add link here Download working code
Now we are ready to run our example so run bellow command ro quick run:
php artisan serve
Now you can open bellow URL on your browser:
http://localhost:8000
If you face any problem then please write a comment or give some suggestions for improvement. Thanks...