Let's practice creating a CRUD with another one: Task Categories. It will be almost the same as Tasks CRUD, so for the most part, I will just show the code and specify the differences.
Task Categories: DB Model/Migration
First, we prepare the back end.
Create Model, Migration, and Pivot table for task categories:
php artisan make:model TaskCategory -mphp artisan make:migration create_task_task_category_table
Migration:
Schema::create('task_categories', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps();});
Pivot migration:
Schema::create('task_task_category', function (Blueprint $table) { $table->foreignId('task_id')->constrained(); $table->foreignId('task_category_id')->constrained();});
app/Models/Task.php:
use Illuminate\Database\Eloquent\Relations\BelongsToMany; // ... public function taskCategories(): BelongsToMany{ return $this->belongsToMany(TaskCategory::class);}
app/Models/TaskCategory.php
namespace App\Models; use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\BelongsToMany; class TaskCategory extends Model{ protected $fillable = ['name']; public function tasks(): BelongsToMany { return $this->belongsToMany(Task::class); }}
Controller and Routes
Create a Controller and Form Requests:
php artisan make:controller TaskCategoryControllerphp artisan make:request StoreTaskCategoryRequestphp artisan make:request UpdateTaskCategoryRequest
app/Http/Controllers/TaskCategoryController.php
use App\Http\Requests\StoreTaskCategoryRequest;use App\Http\Requests\UpdateTaskCategoryRequest;use App\Models\TaskCategory;use Inertia\Inertia; class TaskCategoryController extends Controller{ public function index() { return Inertia::render('TaskCategories/Index', [ 'taskCategories' => TaskCategory::query() ->withCount('tasks') ->paginate(10), ]); } public function create() { return Inertia::render('TaskCategories/Create'); } public function store(StoreTaskCategoryRequest $request) { TaskCategory::create($request->validated()); return redirect()->route('task-categories.index'); } public function edit(TaskCategory $taskCategory) { return Inertia::render('TaskCategories/Edit', [ 'taskCategory' => $taskCategory, ]); } public function update(UpdateTaskCategoryRequest $request, TaskCategory $taskCategory) { $taskCategory->update($request->validated()); return redirect()->route('task-categories.index'); } public function destroy(TaskCategory $taskCategory) { if ($taskCategory->tasks()->count() > 0) { $taskCategory->tasks()->detach(); } $taskCategory->delete(); return redirect()->route('task-categories.index'); }}
As you can see, in the destroy()
method, we check for the assigned tasks and detach them if they exist. We'll get back to this when we discuss the deleting action.
app/Http/Requests/StoreTaskCategoryRequest.php
use Illuminate\Foundation\Http\FormRequest; class StoreTaskCategoryRequest extends FormRequest{ public function rules(): array { return [ 'name' => ['required'], ]; } public function authorize(): bool { return true; }}
app/Http/Requests/UpdateTaskCategoryRequest.php
use Illuminate\Foundation\Http\FormRequest; class UpdateTaskCategoryRequest extends FormRequest{ public function rules(): array { return [ 'name' => ['required'], ]; } public function authorize(): bool { return true; }}
routes/web.php
use App\Http\Controllers\TaskCategoryController; // ... Route::middleware(['auth', 'verified'])->group(function () { // ... Route::resource('tasks', TaskController::class); Route::resource('task-categories', TaskCategoryController::class); }); // ...
TypeScript: Creating Types
Now, let's move to the front end. First, a new...