fix(Seed + Todo Creation):

Fixed:
- Database seeder
- Todo creation/edit form + Completed Checkbox
- Models
- Validation for views
This commit is contained in:
devoalda 2023-08-07 11:50:04 +08:00
parent 9630a250e3
commit 018e0813d2
8 changed files with 125 additions and 33 deletions

View File

@ -28,6 +28,9 @@ class ProjectTodoController extends Controller
$projects = $user->projects;
$project = $projects->find($project_id);
if (!$project || $project->user->id !== auth()->user()->id)
return back()->with('error', 'Project not found');
$todos = $project->todos;
return view('todo.index', [
@ -40,9 +43,12 @@ class ProjectTodoController extends Controller
/**
* Show the form for creating a new resource.
*/
public function create(): Factory|View|Application
public function create($project_id): Factory|View|Application
{
return view('todo.create');
$project = auth()->user()->projects->find($project_id);
return view('todo.create', [
'project' => $project,
]);
}
/**
@ -90,6 +96,9 @@ class ProjectTodoController extends Controller
$projects = $user->projects;
$project = $projects->find($project_id);
if (!$project || $project->user->id !== auth()->user()->id || $todo->user()[0]->id !== auth()->user()->id)
return back()->with('error', 'Project/Todo not found');
return view('todo.show', compact('project', 'todo'));
}
@ -102,6 +111,9 @@ class ProjectTodoController extends Controller
$projects = $user->projects;
$project = $projects->find($project_id);
if (!$project || $project->user->id !== auth()->user()->id || $todo->user()[0]->id !== auth()->user()->id)
return back()->with('error', 'Project/Todo not found');
// Check if the given todo is in the given project (Reverse find with todo's project_id)
if ($todo->project->id !== $project_id)
return back()->with('error', 'Todo not found in the given project');

View File

@ -32,8 +32,15 @@ class Project extends Model
return $this->belongsToMany(Todo::class, 'project_todo', 'project_id', 'todo_id');
}
public function user(): BelongsTo
public function user(): HasOneThrough
{
return $this->belongsTo(User::class);
return $this->hasOneThrough(
User::class,
projectUser::class,
'project_id',
'id',
'id',
'user_id'
);
}
}

View File

@ -9,6 +9,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class Todo extends Model
{
@ -34,9 +36,17 @@ class Todo extends Model
"updated_at" => "integer",
];
public function user(): BelongsTo
public function user(): Collection
{
return $this->belongsTo(User::class);
// Select User given Todo
return DB::table('users')
->join('project_user', 'users.id', '=', 'project_user.user_id')
->join('projects', 'project_user.project_id', '=', 'projects.id')
->join('project_todo', 'projects.id', '=', 'project_todo.project_id')
->join('todos', 'project_todo.todo_id', '=', 'todos.id')
->where('todos.id', '=', $this->id)
->select('users.*')
->get();
}
public function project(): HasOneThrough

View File

@ -16,7 +16,6 @@ class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, UuidTrait;
protected $dateFormat = 'U';
protected $table = 'users';
/**
@ -50,13 +49,13 @@ class User extends Authenticatable
'password' => 'hashed',
];
public function projects(): BelongsToMany
public function projects(): HasManyThrough
{
return $this->belongsToMany(Project::class, 'project_user', 'user_id', 'project_id');
return $this->hasManyThrough(Project::class, projectUser::class, 'user_id', 'id', 'id', 'project_id');
}
// public function todos(): HasManyThrough
// {
// return $this->hasManyThrough(Todo::class, projectUser::class, 'user_id', 'id', 'id', 'project_id');
// }
public function todos(): HasManyThrough
{
return $this->hasManyThrough(Todo::class, projectUser::class, 'user_id', 'id', 'id', 'project_id');
}
}

View File

@ -12,7 +12,11 @@ class DatabaseSeeder extends Seeder
*/
public function run(): void
{
// \App\Models\User::factory(10)->create();
\App\Models\User::factory(3)
->has(\App\Models\Project::factory()->count(3)
->has(\App\Models\Todo::factory()->count(10)))
->create();
// \App\Models\User::factory()->create([
// 'name' => 'Test User',

View File

@ -1,24 +1,23 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('Create Project') }}
{{ __('Create Todo') }}
</h2>
</x-slot>
<!-- Create Project Form (Name, Description) -->
<!-- Create Todo Form (title, description, due_start, due_end, completed_at(Checkbox)) -->
<div class="py-4">
<form method="POST" action="{{ route('project.store') }}" id="project-form">
<form method="POST" action="{{ route('project.todo.store', $project) }}" id="todo-form">
@csrf
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6">
<div class="text-gray-800 dark:text-gray-100">
<div class="mb-4">
<label for="name" class="block mb-2 font-semibold">Name</label>
<input type="text" name="name" id="name" placeholder="Name"
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-full p-4 @error('name') border-red-500 @enderror"
value="{{ old('name') }}">
@error('name')
<label for="title" class="block mb-2 font-semibold">Title</label>
<input type="text" name="title" id="title" placeholder="Title"
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-full p-4 @error('title') border-red-500 @enderror">
@error('title')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
</div>
@ -28,7 +27,7 @@
<div class="mb-4">
<label for="description" class="block mb-2 font-semibold">Description</label>
<textarea name="description" id="description" placeholder="Description"
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-full p-4 @error('description') border-red-500 @enderror">{{ old('description') }}</textarea>
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-full p-4 @error('description') border-red-500 @enderror"></textarea>
@error('description')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
@ -36,16 +35,62 @@
@enderror
</div>
<div>
<button type="submit"
class="bg-blue-500 hover:bg-blue-600 text-white font-semibold px-4 py-2 rounded-lg">
Create Project
</button>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="due_start" class="block mb-2 font-semibold">Due Start</label>
<input type="datetime-local" name="due_start" id="due_start" placeholder="Due Start"
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-full p-4 @error('due_start') border-red-500 @enderror">
@error('due_start')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
</div>
@enderror
</div>
<div>
<label for="due_end" class="block mb-2 font-semibold">Due End</label>
<input type="datetime-local" name="due_end" id="due_end" placeholder="Due End"
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-full p-4 @error('due_end') border-red-500 @enderror">
@error('due_end')
<div class="text-red-900 mt-2 text-sm">
{{ $message }}
</div>
@enderror
</div>
</div>
</div>
</div>
</div>
<!-- Completed at checkbox -->
<div class="mt-4">
<label for="completed_at" class="inline-flex items-center">
<input type="checkbox" name="completed_at" id="completed_at"
class="form-checkbox bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-5 h-5">
<span class="ml-2 text-gray-700 dark:text-gray-400">Completed</span>
</label>
</div>
<div class="flex justify-end mt-4 space-x-4">
<!-- Cancel Button (GET request to index route) -->
<a href="{{ route('project.todo.index', $project) }}"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-900 bg-transparent border border-gray-900 rounded-l-lg hover:bg-gray-900 hover:text-white focus:z-10 focus:ring-2 focus:ring-gray-500 focus:bg-gray-900 focus:text-white dark:border-white dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:bg-gray-700">
<svg class="w-5 h-5 mr-2" aria-hidden="true" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 19l-7-7 7-7"></path>
</svg>
Cancel
</a>
<!-- Update Button -->
<button type="submit"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-green-600 rounded-r-md hover:bg-green-800 focus:z-10 focus:ring-2 focus:ring-green-500 border border-grey-900 dark:border-white dark:text-white dark:hover:text-white dark:hover:bg-green-700 dark:focus:bg-green-700">
<svg class="w-5 h-5 mr-2" aria-hidden="true" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M5 13l4 4L19 7"></path>
</svg>
Create
</button>
</div>
</form>
</div>

View File

@ -62,6 +62,16 @@
</div>
</div>
<!-- Completed at checkbox -->
<div class="mt-4">
<label for="completed_at" class="inline-flex items-center">
<input type="checkbox" name="completed_at" id="completed_at"
class="form-checkbox bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-5 h-5"
{{ old('completed_at', $todo->completed_at) ? 'checked' : '' }}>
<span class="ml-2 text-gray-700 dark:text-gray-400">Completed</span>
</label>
</div>
<div class="flex justify-end mt-4 space-x-4">
<!-- Cancel Button (GET request to index route) -->
<a href="{{ route('project.todo.index', $project) }}"

View File

@ -12,8 +12,13 @@ class ExampleTest extends TestCase
*/
public function test_the_application_returns_a_successful_response(): void
{
$response = $this->get('/');
// Gets redirected to login page if not logged in
// Gets redirected to project page if logged in
if (auth()->check()) {
$response = $this->get(route('project.index'));
} else {
$response = $this->get(route('login'));
}
$response->assertStatus(200);
}
}