first work on To Do Lists

This commit is contained in:
2020-04-19 22:46:28 +02:00
parent c71f3ca4d8
commit c6b94aa1f1
17 changed files with 710 additions and 2 deletions

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Http\Controllers;
use App\Http\Resources\ToDo as ToDoResource;
use App\Http\Resources\ToDoList as ToDoListResource;
use App\Models\ToDo;
use App\Models\ToDoList;
use Illuminate\Http\Request;
class ToDoController extends Controller
{
public function store(ToDoList $toDoList)
{
$this->authorize('create', ToDoList::class);
$toDo = $toDoList->toDos()->create($this->validateData());
return (new ToDoResource($toDo))
->response()
->setStatusCode(201);
}
public function update(ToDoList $toDoList, ToDo $toDo)
{
$this->authorize('update', $toDoList);
$toDo->update($this->validateData());
return (new ToDoResource($toDo))
->response()
->setStatusCode(200);
}
private function validateData()
{
return request()->validate([
'name' => 'required',
]);
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace App\Http\Controllers;
use App\Http\Resources\ToDoList as ToDoListResource;
use App\Models\ToDoList;
use Illuminate\Http\Request;
class ToDoListController extends Controller
{
public function store()
{
$this->authorize('create', ToDoList::class);
$toDoList = request()->user()->toDoLists()->create($this->validateData());
return (new ToDoListResource($toDoList))
->response()
->setStatusCode(201);
}
public function show(ToDoList $toDoList)
{
$this->authorize('view', $toDoList);
return new ToDoListResource($toDoList);
}
public function update(ToDoList $toDoList)
{
$this->authorize('update', $toDoList);
$toDoList->update($this->validateData());
return (new ToDoListResource($toDoList))
->response()
->setStatusCode(200);
}
public function destroy(ToDoList $toDoList)
{
$this->authorize('delete', $toDoList);
$toDoList->delete();
return response([], 204);
}
private function validateData()
{
return request()->validate([
'name' => 'required',
]);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ToDo extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'type' => 'to-dos',
'to_do_id' => $this->id,
'attributes' => [
'data' => [
'name' => $this->name,
'order' => $this->order,
'checked_at' => optional($this->checked_at)->diffForHumans(),
'last_updated' => $this->updated_at->diffForHumans(),
]
]
],
'links' => [
'self' => url('/to-do-lists/'.$this->to_do_list_id),
]
];
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class ToDoCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
'to_dos_count' => $this->count(),
];
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Http\Resources;
use App\Http\Resources\User as UserResource;
use Illuminate\Http\Resources\Json\JsonResource;
class ToDoList extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'type' => 'to-do-lists',
'to_do_list_id' => $this->id,
'attributes' => [
'data' => [
'name' => $this->name,
'to_dos' => new ToDoCollection($this->toDos),
'posted_by' => new UserResource($this->user),
'last_updated' => $this->updated_at->diffForHumans(),
// 'cover_image' => new ImageResource($this->coverImage),
]
]
//'tags' => TagResource::collection($this->tags),
],
'links' => [
'self' => $this->path(),
]
];
}
}

18
app/Models/ToDo.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ToDo extends Model
{
protected $guarded = [];
protected $dates = ['checked_at'];
public function toDoList() :BelongsTo
{
return $this->belongsTo(ToDoList::class);
}
}

28
app/Models/ToDoList.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
namespace App\Models;
use App\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class ToDoList extends Model
{
protected $guarded = [];
public function path()
{
return '/to-do-lists/' . $this->id;
}
public function users(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function toDos(): HasMany
{
return $this->hasMany(ToDo::class);
}
}

View File

@@ -4,6 +4,7 @@ namespace App;
use App\Models\Image; use App\Models\Image;
use App\Models\Memo; use App\Models\Memo;
use App\Models\ToDoList;
use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\Relations\MorphMany;
@@ -82,4 +83,9 @@ class User extends Authenticatable
$userImage->path = 'images/default-cover.jpg'; $userImage->path = 'images/default-cover.jpg';
}); });
} }
public function toDoLists(): HasMany
{
return $this->hasMany(ToDoList::class);
}
} }

View File

@@ -0,0 +1,94 @@
<?php
namespace App\Policies;
use App\Models\ToDoList;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class ToDoListPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any to do lists.
*
* @param \App\User $user
* @return mixed
*/
public function viewAny(User $user)
{
return true;
}
/**
* Determine whether the user can view the to do list.
*
* @param \App\User $user
* @param \App\Models\ToDoList $toDoList
* @return mixed
*/
public function view(User $user, ToDoList $toDoList)
{
return $user->id == $toDoList->user_id;
}
/**
* Determine whether the user can create to do lists.
*
* @param \App\User $user
* @return mixed
*/
public function create(User $user)
{
return true;
}
/**
* Determine whether the user can update the to do list.
*
* @param \App\User $user
* @param \App\Models\ToDoList $toDoList
* @return mixed
*/
public function update(User $user, ToDoList $toDoList)
{
return $user->id == $toDoList->user_id;
}
/**
* Determine whether the user can delete the to do list.
*
* @param \App\User $user
* @param \App\Models\ToDoList $toDoList
* @return mixed
*/
public function delete(User $user, ToDoList $toDoList)
{
return $user->id == $toDoList->user_id;
}
/**
* Determine whether the user can restore the to do list.
*
* @param \App\User $user
* @param \App\Models\ToDoList $toDoList
* @return mixed
*/
public function restore(User $user, ToDoList $toDoList)
{
return false;
}
/**
* Determine whether the user can permanently delete the to do list.
*
* @param \App\User $user
* @param \App\Models\ToDoList $toDoList
* @return mixed
*/
public function forceDelete(User $user, ToDoList $toDoList)
{
return false;
}
}

View File

@@ -17,6 +17,7 @@ class AuthServiceProvider extends ServiceProvider
// 'App\Model' => 'App\Policies\ModelPolicy', // 'App\Model' => 'App\Policies\ModelPolicy',
'App\User' => 'App\Policies\UserPolicy', 'App\User' => 'App\Policies\UserPolicy',
'App\Models\Memo' => 'App\Policies\MemoPolicy', 'App\Models\Memo' => 'App\Policies\MemoPolicy',
'App\Models\ToDoList' => 'App\Policies\ToDoListPolicy',
]; ];
/** /**

View File

@@ -0,0 +1,13 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\Models\ToDoList;
use Faker\Generator as Faker;
$factory->define(ToDoList::class, function (Faker $faker) {
return [
'user_id' => factory(\App\User::class),
'name' => $faker->words(3, [false]),
];
});

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateToDoListsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('to_do_lists', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('to_do_lists');
}
}

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateToDosTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('to_dos', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('to_do_list_id');
$table->string('name');
$table->integer('order')->default(0);
$table->timestamp('checked_at')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('to_dos');
}
}

View File

@@ -23,6 +23,8 @@ Route::middleware('auth:api')->group(function () {
'/users' => 'UserController', '/users' => 'UserController',
'/memos' => 'MemosController', '/memos' => 'MemosController',
'/meteo' => 'MeteoController', '/meteo' => 'MeteoController',
'/to-do-lists' => 'ToDoListController',
'/to-do-lists/{toDoList}/to-do' => 'ToDoController',
// '/users/{user}/posts' => 'UserPostController', // '/users/{user}/posts' => 'UserPostController',
// '/friend-request' => 'FriendRequestController', // '/friend-request' => 'FriendRequestController',
]); ]);

View File

@@ -122,7 +122,7 @@ class MemosTest extends TestCase
$response = $this->patch('/api/memos/' . $memo->id, array_merge($this->data(), ['api_token' => $anotherUser->api_token])); $response = $this->patch('/api/memos/' . $memo->id, array_merge($this->data(), ['api_token' => $anotherUser->api_token]));
$response->assertStatus(Response::HTTP_FORBIDDEN); $response->assertStatus(403);
} }
/** @test */ /** @test */
@@ -138,7 +138,7 @@ class MemosTest extends TestCase
$this->assertCount(0, Memo::all()); $this->assertCount(0, Memo::all());
$response->assertStatus(Response::HTTP_NO_CONTENT); $response->assertStatus(204);
} }
/** @test */ /** @test */

View File

@@ -0,0 +1,124 @@
<?php
namespace Tests\Feature;
use App\Models\ToDo;
use App\Models\ToDoList;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class ToDoItemsTest extends TestCase
{
use RefreshDatabase;
/** @test */
public function a_user_can_create_a_to_do_list()
{
$this->withoutExceptionHandling();
$this->actingAs($user = factory(User::class)->create(), 'api');
$toDoList = factory(ToDoList::class)->create(['id' => 123, 'user_id' => $user->id]);
$response = $this->post('/api/to-do-lists/'. $toDoList->id .'/to-do', [
'name' => 'Test name to do',
])->assertStatus(201);
$toDo = ToDo::first();
$this->assertEquals('Test name to do', $toDo->name);
$this->assertEquals($toDoList->todos[0], $toDo);
$response->assertJson([
'data' => [
'to_do_id' => $toDo->id,
'attributes' => [
'data' => [
'name' => $toDo->name,
'order' => 0,
]
],
],
'links' => [
'self' => url('/to-do-lists/'.$toDo->toDoList->id),
]
]);
}
/** @test */
public function to_do_name_are_required()
{
$this->actingAs($user = factory(User::class)->create(), 'api');
$toDoList = factory(ToDoList::class)->create(['user_id' => $user->id]);
$response = $this->post('/api/to-do-lists/'. $toDoList->id .'/to-do', ['name' => '']);
$response->assertSessionHasErrors('name');
$this->assertCount(0, ToDo::all());
}
/** @test */
public function a_to_do_by_to_do_list_can_be_retrieved()
{
$this->actingAs($user = factory(User::class)->create(), 'api');
$toDoList = factory(ToDoList::class)->create(['user_id' => $user->id]);
$toDoList->toDos()->create([
'name' => 'Test name to do',
]);
$response = $this->get('/api/to-do-lists/' . $toDoList->id );
$response->assertJson([
'data' => [
'to_do_list_id' => $toDoList->id,
'attributes' => [
'data' => [
'name' => $toDoList->name,
'last_updated' => $toDoList->updated_at->diffForHumans(),
'to_dos' => [
'data' => [
[
'data' => [
'to_do_id' => $toDoList->toDos[0]->id,
]
]
],
'to_dos_count' => 1,
]
]
],
]
]);
}
/** @test */
public function a_to_do_can_be_patch()
{
$this->withoutExceptionHandling();
$this->actingAs($user = factory(User::class)->create(), 'api');
$toDoList = factory(ToDoList::class)->create(['id' => 123, 'user_id' => $user->id]);
$toDoList->toDos()->create([
'name' => 'Test name to do',
'order' => 10,
]);
$toDoList = $toDoList->fresh();
// $toDoList = ToDoList::first();
$response = $this->patch('/api/to-do-lists/123/to-do/'. $toDoList->toDos[0]->id, [
'name' => 'To Do update'
])->assertStatus(200)
->assertJson([
'data' => [
'to_do_id' => $toDoList->toDos[0]->id,
'attributes' => [
'data' => [
'name' => 'To Do update',
'order' => 10,
]
]
],
'links' => [
'self' => url('/to-do-lists/'. $toDoList->id),
]
]);
}
}

View File

@@ -0,0 +1,163 @@
<?php
namespace Tests\Feature;
use App\Models\ToDoList;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class ToDoListsTest extends TestCase
{
use RefreshDatabase;
/** @test */
public function a_user_can_create_a_to_do_list()
{
$this->withoutExceptionHandling();
$this->actingAs($user = factory(User::class)->create(), 'api');
$response = $this->post('/api/to-do-lists/', [
'name' => 'Test name to do list'
])->assertStatus(201);
$toDoList = ToDoList::first();
$this->assertEquals('Test name to do list', $toDoList->name);
$response->assertJson([
'data' => [
'type' => 'to-do-lists',
'to_do_list_id' => $toDoList->id,
'attributes' => [
'data' => [
'name' => $toDoList->name,
]
],
],
'links' => [
'self' => $toDoList->path(),
]
]);
}
/** @test */
public function to_do_list_name_are_required()
{
$this->actingAs($user = factory(\App\User::class)->create(), 'api');
$response = $this->post('/api/to-do-lists/', ['name' => '']);
$response->assertSessionHasErrors('name');
$this->assertCount(0, ToDoList::all());
}
/** @test */
public function a_to_do_list_can_be_retrieved()
{
$this->actingAs($user = factory(User::class)->create(), 'api');
$toDoList = factory(ToDoList::class)->create(['user_id' => $user->id]);
$response = $this->get('/api/to-do-lists/' . $toDoList->id );
$response->assertJson([
'data' => [
'to_do_list_id' => $toDoList->id,
'attributes' => [
'data' => [
'name' => $toDoList->name,
'last_updated' => $toDoList->updated_at->diffForHumans(),
]
],
],
'links' => [
'self' => $toDoList->path(),
]
]);
}
/** @test */
public function only_owner_to_do_list_can_retrieved_it()
{
$user = factory(User::class)->create();
$toDoList = factory(ToDoList::class)->create(['user_id' => $user->id]);
$this->actingAs($userAnother = factory(User::class)->create(), 'api');
$response = $this->get('/api/to-do-lists/' . $toDoList->id );
$response->assertStatus(403);
}
/** @test */
public function a_to_do_list_can_be_patch()
{
$this->actingAs($user = factory(User::class)->create(), 'api');
$toDoList = factory(ToDoList::class)->create(['user_id' => $user->id]);
$response = $this->patch('/api/to-do-lists/' . $toDoList->id, [
'name' => 'To Do List Update'
]);
$toDoList = $toDoList->fresh();
$this->assertEquals('To Do List Update', $toDoList->name);
$response->assertStatus(200);
$response->assertJson([
'data' => [
'to_do_list_id' => $toDoList->id,
'attributes' => [
'data' => [
'name' => 'To Do List Update'
]
]
],
'links' => [
'self' => $toDoList->path(),
]
]);
}
/** @test */
public function only_the_owner_can_patch_the_to_do_list()
{
$this->actingAs($user = factory(User::class)->create(), 'api');
$toDoList = factory(ToDoList::class)->create();
$anotherUser = factory(User::class)->create();
$response = $this->patch('/api/to-do-lists/' . $toDoList->id, ['name' => 'Name changed'], ['api_token' => $anotherUser->api_token]);
$response->assertStatus(403);
}
/** @test */
public function a_to_do_list_can_be_delete()
{
$this->actingAs($user = factory(User::class)->create(), 'api');
$toDoList = factory(ToDoList::class)->create(['user_id' => $user->id]);
$response = $this->delete('/api/to-do-lists/' . $toDoList->id);
$toDoList = $toDoList->fresh();
$this->assertCount(0, ToDoList::all());
$response->assertStatus(204);
}
/** @test */
public function only_the_owner_can_delete_the_to_do_list()
{
$user = factory(User::class)->create();
$toDoList = factory(ToDoList::class)->create();
$this->actingAs($anotherUser = factory(User::class)->create(), 'api');
$response = $this->delete('/api/to-do-lists/' . $toDoList->id);
$response->assertStatus(403);
}
}