How to build a Project Management App in Laravel 5 – Part 9
Create a collaborators directory in the resources/views directory and create a form.blade.php.
In the form.blade.php, add this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<div class="col-md-4" style="border:1px solid #ccc;margin-left:15px;padding:10px;"> <h4 class="page-header"> Collaborators </h4> <form class="form-vertical" role="form" method="post" action="{{ route('projects.tasks.create', $project->id) }}"> <div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}"> <label> Add New </label> <input type="text" name="task_name" class="form-control" id="name" value="{{ old('task_name') ?: '' }}"> @if ($errors->has('task_name')) <span class="help-block">{{ $errors->first('task_name') }}</span> @endif </div> <div class="form-group"> <button type="submit" class="btn btn-info">Add User</button> </div> <input type="hidden" name="_token" value="{{ csrf_token() }}"> </form> </div> |
Then go to your show.blade.php in your projects directory and add
@include('collaborators.form')
just immediately after the div that gives us information about the number of comments, tasks and attachments like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<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> @include('collaborators.form') </div> |
So, when you navigate to a particular project, it should be like this:
Now that we have done that.
Let’s add facebook type of mention to our project. So that once you @ and specify a user’s handle, it can suggest several potential users for collaboration.
Luckily, I have written a package for that called laravel-mentions
. So you can read the documentation and use it.
Open your composer.json file, add this in the require
section:
1 |
"unicodeveloper/laravel-mentions": "1.1.*" |
Run composer update
.
Then go to the config/app.php
and add this:
1 2 |
Unicodeveloper\Mention\MentionServiceProvider::class, Collective\Html\HtmlServiceProvider::class, |
to the providers array.
After doing that, then run php artisan vendor:publish
from your terminal. That will copy some files to your project like mentions.php and a js file.
Go to your config/mentions.php file now and change App\User
to Prego\User
.
Head over to master.blade.php file and add this:
1 2 3 4 5 |
<script src="{{ asset('js/jquery.atwho.js') }}"></script> <script src="{{ asset('js/jquery.caret.js') }}"></script> <link rel="stylesheet" href="{{ asset('css/jquery.atwho.min.css') }}"> @include('mentions::assets') |
You can grab jquery.atwho.js here, jquery.caret.js here and jquery.atwho.min.css here and place them in your js and css folders respectively.
Once you have done this, go to your collaborators/form.blade.php and replace it with this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<div class="col-md-4" style="border:1px solid #ccc;margin-left:15px;padding:10px;"> <h4 class="page-header"> Collaborators </h4> <form class="form-vertical" role="form" method="post" action="{{ route('projects.collaborators.create', $project->id) }}"> <div class="form-group{{ $errors->has('collaborator') ? ' has-error' : '' }}"> <label> Add New </label> {!! mention()->asText('collaborator', old('collaborator'), 'users', 'username', 'form-control') !!} @if ($errors->has('collaborator')) <span class="help-block">{{ $errors->first('collaborator') }}</span> @endif </div> <div class="form-group"> <button type="submit" class="btn btn-info">Add User</button> </div> <input type="hidden" name="_token" value="{{ csrf_token() }}"> </form> </div> |
Here
1 |
{!! mention()->asText('collaborator', old('collaborator'), 'users', 'username', 'form-control') !!} |
According to the package documentation, users
refers to the users table and username
refer to the column of the users table. That’s where we want the auto-suggest data to come from.
Now go to your routes.php, let’s add a route for adding Collaborators.
1 2 3 4 5 6 7 8 |
... ... # Collaborator routes Route::post('projects/{projects}/collaborators', [ 'uses' => 'Project\Collaborators\Controller@addCollaborator', 'as' => 'projects.collaborators.create', 'middleware' => ['auth'] ]); |
Let’s create a Collaboration Model
Collaboration.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php namespace Prego; use Illuminate\Database\Eloquent\Model; class Collaboration extends Model { protected $table = 'project_collaborator'; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = ['project_id', 'collaborator_id']; } |
Now, create ProjectCollaboratorsController.php in Http/Controllers directory and add this:
ProjectCollaboratorsController.php
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 61 62 63 64 65 66 67 68 69 70 71 |
<?php namespace Prego\Http\Controllers; use Illuminate\Http\Request; use Auth; use Prego\User; use Prego\Project; use Prego\Collaboration; use Prego\Http\Requests; use Prego\Http\Controllers\Controller; class ProjectCollaboratorsController extends Controller { /** * Display a listing of the resource. * * @return Response */ public function addCollaborator(Request $request, $id, Collaboration $collaboration) { $this->validate($request, [ 'collaborator' => 'required|min:5', ]); $collaborator_username = substr(trim($request->input('collaborator')),1); $collaboration->project_id = $id; if( is_null($this->getId($collaborator_username))) { return redirect()->back()->with('warning', 'This user does not exist'); } $collaborator = $this->isCollaborator($id, $this->getId($collaborator_username)); if(! is_null($collaboration)) { return redirect()->back()->with('warning', 'This user is already a collaborator on this project'); } $collaboration->collaborator_id = $this->getId($collaborator_username); $collaboration->save(); return redirect()->back()->with('info', "{$collaborator_username} has been added to your project successfully"); } /** * Get id of the user * @param string $username * @return mixed null | integer */ private function getId($username) { $result = User::where('username', $username)->first(); return (is_null($result)) ? null : $result->id; } /** * Check if the user about to be added as a collaborator is already one on the project * @param int $projectId * @param int $collaboratorId * @return boolean */ private function isCollaborator($projectId, $collaboratorId) { return Collaboration::where('project_id', $projectId) ->where('collaborator_id', $collaboratorId) ->first(); } } |
Let’s break this down for better understanding.
This line of code below gets the collaborator’s username and yanks off the @ symbol.
1 |
$collaborator_username = substr(trim($request->input('collaborator')),1); |
So the username is passed to the getId function to retrieve the id
1 |
$collaboration->collaborator_id = $this->getId($collaborator_username); |
This isCollaborator method checks if the user is already a collaborator on this particular project.
1 2 3 4 5 6 |
private function isCollaborator($projectId, $collaboratorId) { return Collaboration::where('project_id', $projectId) ->where('collaborator_id', $collaboratorId) ->first(); } |
So if it returns a result other than null
, then he/she is already a collaborator…so it sends an appropriate warning message that the user can not be added a second time.
1 2 3 4 |
if(! is_null($collaboration)) { return redirect()->back()->with('warning', 'This user is already a collaborator on this project'); } |
This code below checks if the potential collaborator exists as a user or not. If someone types in a random value in the text field for adding new collaborators..then the result would be null and it will give an appropriate error message.
1 2 3 4 |
if( is_null($this->getId($collaborator_username))) { return redirect()->back()->with('warning', 'This user does not exist'); } |
Let’s test these functionalities.
Awesome!!..All our checks are working!
Now, let’s add the ability to see the collaborator’s avatar.
collaborators/form.blade.php will now be:
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 |
<div class="col-md-4" style="border:1px solid #ccc;margin-left:15px;padding:10px;"> <h4 class="page-header"> Collaborators </h4> @if( $collaborators) @foreach( $collaborators as $collaborator) <div> <div> <span> <img src="{{ $collaborator->user()->first()->getAvatarUrl() }}" /> </span> </div> <button class="btn btn-sm btn-danger delete" style="margin-top:5px;padding:4px;width:35px;" data-action="/projects/{{ $project->id }}/collaborators/{{ $collaborator->collaborator_id }}" data-token="{{csrf_token()}}"> <i class="fa fa-trash-o"></i> </button> </div> <hr/> @endforeach @endif <form class="form-vertical" role="form" method="post" action="{{ route('projects.collaborators.create', $project->id) }}"> <div class="form-group{{ $errors->has('collaborator') ? ' has-error' : '' }}"> <label> Add New </label> {!! mention()->asText('collaborator', old('collaborator'), 'users', 'username', 'form-control') !!} @if ($errors->has('collaborator')) <span class="help-block">{{ $errors->first('collaborator') }}</span> @endif </div> <div class="form-group"> <button type="submit" class="btn btn-info">Add User</button> </div> <input type="hidden" name="_token" value="{{ csrf_token() }}"> </form> </div> |
In this code below: I am calling the user() function on $collaborator which is an instance of Collaboration model.
1 2 3 |
<span> <img src="{{ $collaborator->user()->first()->getAvatarUrl() }}" /> </span> |
Therefore, head over to the Collaboration Model and add this:
1 2 3 4 5 6 7 8 |
/** * Get the user that is a collaborator on another project * @return collection */ public function user() { return $this->belongsTo(User::class, 'collaborator_id'); } |
Now, I am putting collaborator_id
as the second argument there because that’s the foreign_key that establishes a relationship between the Collaboration and User Model. By default, it will try to look for user_id, but I used collaborator_id
in place of that.
Now, we also need to add the queryScope method to the Collaboration Model that will return information about the current project a user might be working on.
1 2 3 4 5 6 7 8 9 10 |
/** * Query scope to return information about the current project * @param $query * @param int $id * @return query */ public function scopeProject($query, $id) { return $query->where('project_id', $id); } |
Go to ProjectController.php and add this method:
1 2 3 4 5 6 7 8 9 10 |
/** * Get all the collaborators on this project * @param int $id * @return collection */ public function getCollaborators($id) { $collaborators = Collaboration::project($id)->get(); return $collaborators; } |
Now, we have to return information about the collaborators to the page through the show method of the ProjectController.
show method 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 |
/** * 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); $collaborators = $this->getCollaborators($id); return view('projects.show') ->withProject($project) ->withTasks($tasks) ->withFiles($files) ->withComments($comments) ->withCollaborators($collaborators); } |
So, try out the functionality now:
Yaay!, adding a collaborator worked!
Now, let’s check deleting of a collaborator
That also worked.
Remaining Features
– When a collaborator is added to a project, he/she should receive an email.
– When a collaborator logs in, he/she should be able to see the project that he has been invited to collaborate on, maybe a badge on the project that shows it’s a shared project.
– A Collaborator on a project should be able to see the project, comment on the project, add tasks and attachments to the project.
– A user should be able to add/edit and delete todos.
– A user should be able to update his/her account.
– Hosting on Heroku
Those are the remaining features listed above for us to be done with this very simple and basic project management application.
Let’s Quickly treat sending of emails before ending this blog post.
Sending Email When A User is added as a Collaborator on a Project
I like to use Mandrill when dealing with transactional emails, so you can go ahead and create an account with Mandrill. Go to the settings page, create an api key, that will serve as a your password. The host, username , driver details are all on that page.
Go to your .env file and add those details like so:
1 2 3 4 5 |
MAIL_DRIVER=smtp MAIL_HOST=smtp.mandrillapp.com MAIL_PORT=587 MAIL_USERNAME=xxxxxx MAIL_PASSWORD=xxxxxxxxx |
where xxxxx is your username and password respectively.
Now, we are gonna use the concept of Model Events and Observers.
Laravel gives you the luxury of listening to Model events such as saved
, saving
, deleted
, deleting
, created
, creating
, saving
, saved
, restored
, restoring
out of the box.
Don’t be worried. It’s actually so simple, nothing complex at all.
Head over to providers/EventServiceProvider.php file, we are gonna put our logic in the boot method. We will listen to the saved
method of the Collaboration
method. So 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 |
/** * Register any other events for your application. * * @param IlluminateContractsEventsDispatcher $events * @return void */ public function boot(DispatcherContract $events) { parent::boot($events); Collaboration::saved( function($collaboration) { $data = [ 'username' => Auth::user()->username, 'project_name' => session('project_name') ]; $subject = 'Call For Collaboration'; $user = session('user_email'); Mail::send('emails.collaborate', $data, function($message) use ($user, $subject){ $message->from('no-reply@prego.com', 'Prego'); $message->to($user) ->subject($subject); }); }); } |
So once a user has been added as a collaborator, it sends an email to the collaborator notifying that he/she has been added as a collaborator.
Do not forget to add these:
1 2 3 |
use Prego\Collaboration; use Auth; use Mail; |
at the top in the EventServiceProvider.php file
Go to ProjectCollaboratorsController.php to add this two methods:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/** * Get the email of a user for use in sending emails * @param string $username * @return string */ private function getEmail($username) { return User::where('username', $username)->first()->email; } /** * Get the Project Name for use in sending email for Collaboration * @param int $id * @return string */ private function getProjectName($id) { return Project::where('id', $id)->first()->project_name; } |
We need these two methods to get the project name and the email of the collaborator that will be added to the project.
So add this just before $collaboration->save()
in addCollaborator
method.
1 2 3 |
session(['project_name' => $this->getProjectName($id), 'user_email' => $this->getEmail($collaborator_username) ]); |
So that our EventServiceProvider can have access to project_name
and user_email
Also, create an emails directory, create a file called collaborate.blade.php
.
Add this to the file:
1 |
{{ '@' . $username }} has added you as a Collaborator on {{ $project_name }} |
This is our email template text, it takes in $username
and $project_name
variables. Those variables are passed in from the $data
array that we declared before the Mail::send
method.
Try out the functionality now
Yaaay!, It works!
Please let me know if you have any questions or observation in the comments section below.

- 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