How to build a Project Management App in Laravel 5 – Part 8
Let’s provide the ability to add Comments to the various tasks.
Create a new folder named comments in the views directory and create a form.blade.php inside the comments directory.
Move the part of the show.blade.php that has to do with the comment form into form.blade.php file like so:
form.blade.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<h4 class="page-header"> Comments </h4> <div class="row" style="margin-left:5px;padding:5px;"> <form class="form-vertical" role="form" method="post" action="#"> <div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}"> <textarea name="comment" class="form-control" style="width:80%;" id="comment" rows="5" cols="5"></textarea> @if ($errors->has('comment')) <span class="help-block">{{ $errors->first('comment') }}</span> @endif </div> <div class="form-group"> <button type="submit" class="btn btn-info">Add Comment</button> </div> <input type="hidden" name="_token" value="{{ csrf_token() }}"> </form> </div> |
Replace the part in show.blade.php with @include('comments.form')
like so:
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 |
@extends('layouts.master') @section('content') @include('layouts.partials.sidebar') <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> @include('layouts.partials.alerts') @if( $project ) <h1 class="page-header"> {!! $project->project_name !!} </h1> <div class="container"> <div class="row"> <div class="col-md-3" style="border:1px solid #ccc;margin-left:5px;padding:10px;"> <p>Due : {!! date_format(new DateTime($project->due_date), "D, m Y") !!}</p> <p>Status: {!! $project->project_status !!}</p> <p>Tasks: 0</p> <p>Comments: 0</p> <p>Attachments: 0</p> <p><a href="/projects/{{ $project->id }}/edit">Edit</a></p> <button class="btn btn-circle btn-danger delete" data-action="{{ url('projects/' . $project->id) }}" data-token="{{csrf_token()}}"> <i class="fa fa-trash-o"></i>Delete </button> </div> </div> <hr> <div class="row"> @include('tasks.form') @include('files.form') </div> <hr> <div class="row"> @include('comments.form') </div> </div> @endif </div> @stop |
Go to routes.php and add this:
1 2 3 4 5 6 |
# Comment routes Route::post('projects/{projects}/comments', [ 'uses' => 'Project\CommentsController@postNewComment', 'as' => 'projects.comments.create', 'middleware' => ['auth'] ]); |
Now, let’s create a Comment Model.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php namespace Prego; use Auth; use Illuminate\Database\Eloquent\Model; class Comment extends Model { /** * The attributes that are mass assignable. * * @var array */ protected $fillable = ['comments', 'project_id', 'user_id']; } |
Create a ProjectCommentsController.php and add this:
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 |
<?php namespace Prego\Http\Controllers; use Illuminate\Http\Request; use Auth; use Prego\Comment; use Prego\Http\Requests; use Prego\Http\Controllers\Controller; class ProjectCommentsController extends Controller { /** * Display a listing of the resource. * * @return Response */ public function postNewComment(Request $request, $id, Comment $comment) { $this->validate($request, [ 'comments' => 'required|min:5', ]); $comment->comments = $request->input('comments'); $comment->project_id = $id; $comment->user_id = Auth::user()->id; $comment->save(); return redirect()->back()->with('info', 'Comment posted successfully'); } } |
We are going to use the principle of Dependency Injection here by creating an instance of Comment and passing it as an argument to postNewComment function.
Now, let’s write a function to get all the comments that belong to a Project.
Back to Comment Model, The Comment Model will look like this now:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?php namespace Prego; use Auth; use Illuminate\Database\Eloquent\Model; class Comment extends Model { /** * The attributes that are mass assignable. * * @var array */ protected $fillable = ['comments', 'project_id', 'user_id']; public function scopeProject($query, $id) { return $query->where('project_id', $id); } } |
The query scope will return all the comments that belong to just one project.
Go to ProjectController.php
Import the Comment Model by writing
from the top of the Controller file.use Prego\Comment
Add this function to retrieve all the comments
1 2 3 4 5 6 7 8 9 10 |
/** * Get all the comments that were made on a Project * @param integer $id * @return collection */ public function getComments($id) { $comments = Comment::project($id)->get(); return $comments; } |
Just scroll up the show function. We will call the getComments function in the show function. We want to return all the comments to the projects show view.
Modify the Show function to look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/** * Display the specified resource. * * @param int $id * @return Response */ public function show($id) { $project = Project::find($id); $tasks = $this->getTasks($id); $files = $this->getFiles($id); $comments = $this->getComments($id); return view('projects.show')->withProject($project)->withTasks($tasks)->withFiles($files)->withComments($comments); } |
Now, the function returns the project with all its comments to the show view.
the form.blade.php in comments folder will change, because we will add the ability to see all the comments with an edit and delete button.
form.blade.php now looks like this:
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 |
<h4 class="page-header"> Comments </h4> <div class="row" style="margin-left:5px;padding:5px;"> @if($comments) @foreach( $comments as $comment) <div> <div><i class="fa fa-check-square-o"></i> <span>{{ $comment->comments }}</span></div> <a href="/projects/{{ $project->id }}/comments/{{ $comment->id }}/edit">Edit</a> <button class="btn btn-danger delete pull-right" data-action="/projects/{{ $project->id }}/comments/{{ $comment->id }}" data-token="{{csrf_token()}}"> <i class="fa fa-trash-o"></i>Delete </button> </div> <hr/> @endforeach @endif <form class="form-vertical" role="form" method="post" action="{{ route('projects.comments.create', $project->id) }}"> <div class="form-group{{ $errors->has('comments') ? ' has-error' : '' }}"> <textarea name="comments" class="form-control" style="width:80%;" id="comment" rows="5" cols="5"></textarea> @if ($errors->has('comments')) <span class="help-block">{{ $errors->first('comments') }}</span> @endif </div> <div class="form-group"> <button type="submit" class="btn btn-info">Add Comment</button> </div> <input type="hidden" name="_token" value="{{ csrf_token() }}"> </form> </div> |
Now test out the functionality.
Yaaay! it works.
Now, we need to know who posted the comment. Hold up, we actually already know but there should be a text representation of that on the screen. We would soon start adding collaborators and they would be able to comment on the projects they were added to.
Now go back to the Comment Model. Let’s write a function that will establish the relationship between comments and users. Add this to the Comment Model like so:
1 2 3 4 5 6 7 8 9 10 11 |
... ... /** * Get the user that is reponsible for a comment * @return collection */ public function user() { return $this->belongsTo(User::class); } |
Go to the form.blade.php in the comments view directory, it will now become this:
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 |
<h4 class="page-header"> Comments </h4> <div class="row" style="margin-left:5px;padding:5px;"> @if($comments) @foreach( $comments as $comment) <div> <div><i class="fa fa-check-square-o"></i> <span>{{ $comment->comments }} by <span style="font-style: italic;color: #09f;"> {{ ($comment->user()->first()->username === auth()->user()->username) ? 'You' : $comment->user()->first()->username }} </span> </span></div> <a href="/projects/{{ $project->id }}/comments/{{ $comment->id }}/edit">Edit</a> <button class="btn btn-danger delete pull-right" data-action="/projects/{{ $project->id }}/comments/{{ $comment->id }}" data-token="{{csrf_token()}}"> <i class="fa fa-trash-o"></i>Delete </button> </div> <hr/> @endforeach @endif <form class="form-vertical" role="form" method="post" action="{{ route('projects.comments.create', $project->id) }}"> <div class="form-group{{ $errors->has('comments') ? ' has-error' : '' }}"> <textarea name="comments" class="form-control" style="width:80%;" id="comment" rows="5" cols="5"></textarea> @if ($errors->has('comments')) <span class="help-block">{{ $errors->first('comments') }}</span> @endif </div> <div class="form-group"> <button type="submit" class="btn btn-info">Add Comment</button> </div> <input type="hidden" name="_token" value="{{ csrf_token() }}"> </form> </div> |
Now, let me explain this section:
1 2 3 4 5 |
<span>{{ $comment->comments }} by <span style="font-style: italic;color: #09f;"> {{ isset($comment->user()->first()->username) ? 'You' : $comment->user()->first()->username }} </span> </span> |
$comment->user()
calls the user
method we defined the Comment Model earlier.
$comment->user()->first()
returns the first instance of the collection that is retrieved from the User Model via the user_id that was resolved from the comments
table.
$comment->user()->first()->username
retrieves the username attribute from the object.
Now,
1 |
{{ ($comment->user()->first()->username === auth()->user()->username) ? 'You' : $comment->user()->first()->username }} |
this just says it should return ‘You’ if it’s the user that posted the comment that owns the project else return the collaborator’s username.
Brace Up, Let’s work on editing a Comment
Create an edit.blade.php in the comments directory and add this:
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 |
@extends('layouts.master') @section('content') @include('layouts.partials.sidebar') <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> <h1 class="page-header"> Edit Comment</h1> @include('layouts.partials.alerts') <div class="col-lg-6"> <form class="form-vertical" role="form" method="post" action="{{ url('projects/' .$projectId .'/comments/' . $comment->id) }}"> <div class="form-group{{ $errors->has('comments') ? ' has-error' : '' }}"> <input type="text" name="comments" class="form-control" id="comments" value="{!! $comment->comments ?: '' !!}"> @if ($errors->has('comments')) <span class="help-block">{{ $errors->first('comments') }}</span> @endif </div> <div class="form-group"> <button type="submit" class="btn btn-info">Update Commment</button> </div> <input type="hidden" name="_token" value="{{ csrf_token() }}"> {!! method_field('PUT') !!} </form> </div> </div> @stop |
Let’s go to our routes.php file and add the route for editing the comment like so:
1 2 3 4 5 6 7 8 9 |
... ... Route::get('projects/{projects}/comments/{comments}/edit', [ 'uses' => 'Project\CommentsController@getOneProjectComment', 'as' => 'projects.comments' ]); .. |
Now that we have added that, let’s go back to ProjectCommentsController and add the getOneComment method like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * Get just one comment for a particular Project * @param int $projectId * @param int $commentId * @return view */ public function getOneProjectComment($projectId, $commentId) { $comment = Comment::where('project_id', $projectId) ->where('id', $commentId) ->first(); return view('comments.edit')->withComment($comment)->with('projectId', $projectId); } |
Now, let’s test if it works
Awesome!,…Now, let’s add the ability to update.
Go to your routes.php and add this:
1 2 3 4 5 6 |
... ... Route::put('projects/{projects}/comments/{comments}', [ 'uses' => 'Project\CommentsController@updateOneProjectComment', ]); |
We are using
because we want to carry out an update operation.Route::put
Go to ProjectCommentsController.php, add this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/** * Update One Project Comment * @param Request $request * @param int $projectId * @param int $commentId * @return view */ public function updateOneProjectComment(Request $request, $projectId, $commentId) { $this->validate($request, [ 'comments' => 'required|min:3', ]); Comment::where('project_id', $projectId) ->where('id', $commentId) ->update(['comments' => $request->input('comments')]); return redirect()->back()->with('info','Your Comment has been updated successfully'); } |
It validates the comments field, then updates the comments
table with the new comment.
Test out the functionality:
Yaay!!. Now we can update a comment successfully.
Let’s add the ability to delete a comment
Go to the routes.php file and add this:
1 2 3 4 5 6 |
... ... Route::delete('projects/{projects}/comments/{comments}', [ 'uses' => 'Project\CommentsController@deleteOneProjectComment', ]); |
Head over to ProjectCommentsController.php and add this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * Delete One Project Comment * @param int $projectId * @param int $commentId * @return view */ public function deleteOneProjectComment($projectId, $commentId) { Comment::where('project_id', $projectId) ->where('id', $commentId) ->delete(); return redirect()->route('projects.show')->with('info', 'Comment deleted successfully'); } |
Now, test the functionality.
Boom!!!..Yes, it deletes the comment
One More thing, You will discover that on any Particular project, we still have comments, tasks and attachments as 0.
Let’s change that ASAP!
Go to your show.blade.php in the projects directory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<div class="row"> <div class="col-md-3" style="border:1px solid #ccc;margin-left:5px;padding:10px;"> <p>Due : {!! date_format(new DateTime($project->due_date), "D, m Y") !!}</p> <p>Status: {!! $project->project_status !!}</p> <p>Tasks: 0</p> <p>Comments: 0</p> <p>Attachments: 0</p> <p><a href="/projects/{{ $project->id }}/edit">Edit</a></p> <button class="btn btn-circle btn-danger delete" data-action="{{ url('projects/' . $project->id) }}" data-token="{{csrf_token()}}"> <i class="fa fa-trash-o"></i>Delete </button> </div> </div> |
In this section, we are gonna change it to this below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<div class="row"> <div class="col-md-3" style="border:1px solid #ccc;margin-left:5px;padding:10px;"> <p>Due : {!! date_format(new DateTime($project->due_date), "D, m Y") !!}</p> <p>Status: {!! $project->project_status !!}</p> <p>Tasks: {{ count($tasks) }} </p> <p>Comments: {{ count($comments) }}</p> <p>Attachments: {{ count($files) }} </p> <p><a href="/projects/{{ $project->id }}/edit">Edit</a></p> <button class="btn btn-circle btn-danger delete" data-action="{{ url('projects/' . $project->id) }}" data-token="{{csrf_token()}}"> <i class="fa fa-trash-o"></i>Delete </button> </div> </div> |
We used a PHP built-in count()
function, it counts the elements in the $tasks
, $comments
and $files
collection. Yes…it’s that simple!!!
Yes!!!, it shows the amount of Tasks, comments and attachments on a project.
Now, pat yourself on the back, grab a coffee, then Netflix and Chill because you have done well.
Sorry we couldn’t deal with Adding Collaborators in this post, We’ll do that in the next post.
Please if you have any questions or observations, let me know in the comments section.
- How to build your own Youtube – Part 10 - August 1, 2016
- How to build your own Youtube – Part 9 - July 25, 2016
- How to build your own Youtube – Part 8 - July 23, 2016
- How to build your own Youtube – Part 6 - July 6, 2016
- Introducing Laravel Password v1.0 - July 3, 2016
- How to build your own Youtube – Part 5 - June 28, 2016
- How to build your own Youtube – Part 4 - June 23, 2016
- How to build your own Youtube – Part 3 - June 15, 2016
- How to build your own Youtube – Part 2 - June 8, 2016
- How to build your own Youtube – Part 1 - June 1, 2016