laravelapiproyekrestMahir4 mnt baca

Proyek Mini: Todo REST API

Bangun API todo lengkap dari nol: migration, model, resource, controller, dan route API.

Rencana project

Kita gabungkan semua modul API jadi satu project nyata: Todo REST API dengan autentikasi Sanctum. Endpoint: list, buat, lihat, update, hapus, dan toggle selesai. Ini fondasi yang dipakai hampir semua API mobile.

Langkah 1: migration dan model

bash
php artisan make:migration create_todos_table
php artisan make:model Todo
php
// database/migrations/xxxx_create_todos_table.php
Schema::create('todos', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->string('judul');
    $table->text('deskripsi')->nullable();
    $table->boolean('selesai')->default(false);
    $table->timestamps();
});
php
// app/Models/Todo.php
class Todo extends Model
{
    protected $fillable = ['judul', 'deskripsi', 'selesai'];

    protected function casts(): array
    {
        return ['selesai' => 'boolean'];
    }

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}

Langkah 2: resource dan request

bash
php artisan make:resource TodoResource
php artisan make:request StoreTodoRequest
php
// app/Http/Resources/TodoResource.php
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'judul' => $this->judul,
        'deskripsi' => $this->deskripsi,
        'selesai' => $this->selesai,
        'dibuat' => $this->created_at->diffForHumans(),
    ];
}

Langkah 3: controller dan route

php
// app/Http/Controllers/Api/TodoController.php
class TodoController extends Controller
{
    public function index(Request $request)
    {
        $todos = $request->user()->todos()->latest()->paginate(15);
        return TodoResource::collection($todos);
    }

    public function store(StoreTodoRequest $request)
    {
        $todo = $request->user()->todos()->create($request->validated());
        return (new TodoResource($todo))->response()->setStatusCode(201);
    }

    public function update(StoreTodoRequest $request, Todo $todo)
    {
        Gate::authorize('update', $todo);
        $todo->update($request->validated());
        return new TodoResource($todo);
    }

    public function destroy(Todo $todo)
    {
        Gate::authorize('delete', $todo);
        $todo->delete();
        return response()->noContent();
    }

    public function toggle(Todo $todo)
    {
        Gate::authorize('update', $todo);
        $todo->update(['selesai' => ! $todo->selesai]);
        return new TodoResource($todo);
    }
}
php
// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
    Route::apiResource('todos', TodoController::class);
    Route::patch('todos/{todo}/toggle', [TodoController::class, 'toggle']);
});

Langkah 4: uji dengan curl

bash
# Buat token dulu lewat login, lalu:
curl -H "Authorization: Bearer TOKENMU" \
     -H "Accept: application/json" \
     http://127.0.0.1:8000/api/todos

curl -X POST -H "Authorization: Bearer TOKENMU" \
     -H "Accept: application/json" -H "Content-Type: application/json" \
     -d '{"judul":"Belajar Laravel","deskripsi":"Sampai mahir"}' \
     http://127.0.0.1:8000/api/todos

Catatan teknis: Route model binding {todo} mengambil todo milik SIAPA SAJA berdasarkan ID. Tanpa Gate::authorize, user A bisa mengupdate todo milik user B hanya dengan menebak ID. Selalu pasang policy atau tambahkan scope where('user_id', auth()->id()) di binding. Keamanan tidak boleh mengandalkan "ID kan susah ditebak".