Skip to content

Commit

Permalink
Episode 14 Complete
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffreyWay committed Jan 15, 2019
1 parent 0533ad7 commit 10c7531
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 10 deletions.
30 changes: 29 additions & 1 deletion app/Http/Controllers/ProjectTasksController.php
Expand Up @@ -3,21 +3,49 @@
namespace App\Http\Controllers;

use App\Project;
use App\Task;

class ProjectTasksController extends Controller
{
/**
* Add a task to the given project.
*
* @param \App\Project $project
* @param Project $project
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Project $project)
{
if (auth()->user()->isNot($project->owner)) {
abort(403);
}

request()->validate(['body' => 'required']);

$project->addTask(request('body'));

return redirect($project->path());
}

/**
* Update the project.
*
* @param Project $project
* @param Task $task
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Project $project, Task $task)
{
if (auth()->user()->isNot($project->owner)) {
abort(403);
}

request()->validate(['body' => 'required']);

$task->update([
'body' => request('body'),
'completed' => request()->has('completed')
]);

return redirect($project->path());
}
}
4 changes: 2 additions & 2 deletions app/Http/Controllers/ProjectsController.php
Expand Up @@ -56,8 +56,8 @@ public function store()
'description' => 'required'
]);

auth()->user()->projects()->create($attributes);
$project = auth()->user()->projects()->create($attributes);

return redirect('/projects');
return redirect($project->path());
}
}
20 changes: 20 additions & 0 deletions app/Task.php
Expand Up @@ -12,4 +12,24 @@ class Task extends Model
* @var array
*/
protected $guarded = [];

/**
* Get the owning project.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function project()
{
return $this->belongsTo(Project::class);
}

/**
* Get the path to the task.
*
* @return string
*/
public function path()
{
return "/projects/{$this->project->id}/tasks/{$this->id}";
}
}
4 changes: 1 addition & 3 deletions database/factories/ProjectFactory.php
Expand Up @@ -6,8 +6,6 @@
return [
'title' => $faker->sentence(4),
'description' => $faker->sentence(4),
'owner_id' => function () {
return factory(App\User::class)->create()->id;
}
'owner_id' => factory(App\User::class)
];
});
3 changes: 2 additions & 1 deletion database/factories/TaskFactory.php
Expand Up @@ -4,6 +4,7 @@

$factory->define(App\Task::class, function (Faker $faker) {
return [
'body' => $faker->sentence
'body' => $faker->sentence,
'project_id' => factory(\App\Project::class)
];
});
Expand Up @@ -17,6 +17,7 @@ public function up()
$table->increments('id');
$table->unsignedInteger('project_id');
$table->text('body');
$table->boolean('completed')->default(false);
$table->timestamps();
});
}
Expand Down
23 changes: 21 additions & 2 deletions resources/views/projects/show.blade.php
Expand Up @@ -4,7 +4,8 @@
<header class="flex items-center mb-3 py-4">
<div class="flex justify-between items-end w-full">
<p class="text-grey text-sm font-normal">
<a href="/projects" class="text-grey text-sm font-normal no-underline hover:underline">My Projects</a> / {{ $project->title }}
<a href="/projects" class="text-grey text-sm font-normal no-underline hover:underline">My Projects</a>
/ {{ $project->title }}
</p>

<a href="/projects/create" class="button">New Project</a>
Expand All @@ -19,8 +20,26 @@

{{-- tasks --}}
@foreach ($project->tasks as $task)
<div class="card mb-3">{{ $task->body }}</div>
<div class="card mb-3">
<form method="POST" action="{{ $task->path() }}">
@method('PATCH')
@csrf

<div class="flex">
<input name="body" value="{{ $task->body }}" class="w-full {{ $task->completed ? 'text-grey' : '' }}">
<input name="completed" type="checkbox" onChange="this.form.submit()" {{ $task->completed ? 'checked' : '' }}>
</div>
</form>
</div>
@endforeach

<div class="card mb-3">
<form action="{{ $project->path() . '/tasks' }}" method="POST">
@csrf

<input placeholder="Add a new task..." class="w-full" name="body">
</form>
</div>
</div>

<div>
Expand Down
1 change: 1 addition & 0 deletions routes/web.php
Expand Up @@ -22,6 +22,7 @@
Route::post('/projects', 'ProjectsController@store');

Route::post('/projects/{project}/tasks', 'ProjectTasksController@store');
Route::patch('/projects/{project}/tasks/{task}', 'ProjectTasksController@update');

Route::get('/home', 'HomeController@index')->name('home');
});
Expand Down
5 changes: 4 additions & 1 deletion tests/Feature/ManageProjectsTest.php
Expand Up @@ -2,6 +2,7 @@

namespace Tests\Feature;

use App\Project;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
Expand Down Expand Up @@ -35,7 +36,9 @@ public function a_user_can_create_a_project()
'description' => $this->faker->paragraph
];

$this->post('/projects', $attributes)->assertRedirect('/projects');
$response = $this->post('/projects', $attributes);

$response->assertRedirect(Project::where($attributes)->first()->path());

$this->assertDatabaseHas('projects', $attributes);

Expand Down
51 changes: 51 additions & 0 deletions tests/Feature/ProjectTasksTest.php
Expand Up @@ -18,6 +18,33 @@ public function guests_cannot_add_tasks_to_projects()
$this->post($project->path() . '/tasks')->assertRedirect('login');
}

/** @test */
function only_the_owner_of_a_project_may_add_tasks()
{
$this->signIn();

$project = factory('App\Project')->create();

$this->post($project->path() . '/tasks', ['body' => 'Test task'])
->assertStatus(403);

$this->assertDatabaseMissing('tasks', ['body' => 'Test task']);
}

/** @test */
function only_the_owner_of_a_project_may_update_a_task()
{
$this->signIn();

$project = factory('App\Project')->create();
$task = $project->addTask('test task');

$this->patch($task->path(), ['body' => 'changed'])
->assertStatus(403);

$this->assertDatabaseMissing('tasks', ['body' => 'changed']);
}

/** @test */
public function a_project_can_have_tasks()
{
Expand All @@ -33,6 +60,30 @@ public function a_project_can_have_tasks()
->assertSee('Test task');
}

/** @test */
function a_task_can_be_updated()
{
$this->withoutExceptionHandling();

$this->signIn();

$project = auth()->user()->projects()->create(
factory(Project::class)->raw()
);

$task = $project->addTask('test task');

$this->patch($project->path() . '/tasks/' . $task->id, [
'body' => 'changed',
'completed' => true
]);

$this->assertDatabaseHas('tasks', [
'body' => 'changed',
'completed' => true
]);
}

/** @test */
public function a_task_requires_a_body()
{
Expand Down
29 changes: 29 additions & 0 deletions tests/Unit/TaskTest.php
@@ -0,0 +1,29 @@
<?php

namespace Tests\Unit;

use App\Project;
use App\Task;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class TaskTest extends TestCase
{
use RefreshDatabase;

/** @test */
function it_belongs_to_a_project()
{
$task = factory(Task::class)->create();

$this->assertInstanceOf(Project::class, $task->project);
}

/** @test */
function it_has_a_path()
{
$task = factory(Task::class)->create();

$this->assertEquals("/projects/{$task->project->id}/tasks/{$task->id}", $task->path());
}
}

0 comments on commit 10c7531

Please sign in to comment.