Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions API_RESPONSE_STANDARD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Standar Response API

Proyek ini menggunakan format JSON standar untuk semua endpoint API. Kami telah menyediakan helper **Trait** untuk memudahkan implementasi agar konsisten di semua Controller.

## Format Response JSON

Setiap response API akan memiliki struktur berikut:

```json
{
"success": boolean,
"message": string,
"data": mixed
}
```

- **success**: `true` jika request berhasil, `false` jika gagal/error.
- **message**: Pesan deskriptif (misal: "Data berhasil diambil" atau "Ticket not found").
- **data**: Payload utama (Object, Array, atau null).

---

## Cara Penggunaan (Implementasi)

Kami telah membuat Trait `App\Traits\ApiResponse` yang sudah di-load secara otomatis di **Base Controller**.

### 1. Response Sukses (`successResponse`)

Gunakan method `$this->successResponse($data, $message, $code)` di dalam controller.

**Contoh (Index/List):**
```php
public function index()
{
$projects = Project::all();

// Otomatis return JSON dengan success: true
return $this->successResponse(ProjectApiResource::collection($projects), 'List Data Project');
}
```

**Contoh (Detail/Show):**
```php
public function show($id)
{
$project = Project::find($id);

return $this->successResponse(new ProjectApiResource($project), 'Detail Project');
}
```

---

### 2. Response Error (`errorResponse`)

Gunakan method `$this->errorResponse($message, $code, $data)` untuk mengembalikan error secara manual (bukan Exception).

**Kapan digunakan?**
- Jika data by ID tidak ditemukan (404).
- Jika validasi logika bisnis gagal.
- Jika access denied (403).

**Contoh (Data Not Found):**
```php
public function show($id)
{
$ticket = Ticket::find($id);

if (! $ticket) {
// Return success: false, status: 404
return $this->errorResponse('Ticket not found', 404);
}

return $this->successResponse($ticket, 'Detail Ticket');
}
```

**Catatan Penting:**
Untuk endpoint **List/Index**, jika data kosong (0 record), **JANGAN** gunakan `errorResponse`. Tetap gunakan `successResponse` dengan data array kosong `[]`.

---

## Lokasi File
- Trait: `app/Traits/ApiResponse.php`
- Base Controller: `app/Http/Controllers/Controller.php`
3 changes: 2 additions & 1 deletion app/Filament/Resources/ProjectResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ public static function form(Form $form): Form
->searchable()
->options([
'kanban' => __('Kanban'),
'scrum' => __('Scrum')
'scrum' => __('Scrum'),
'layanan' => 'Layanan',
])
->reactive()
->default(fn() => 'kanban')
Expand Down
23 changes: 23 additions & 0 deletions app/Http/Controllers/Api/ProjectController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\ProjectApiResource;
use App\Models\Project;
use Illuminate\Http\Request;

class ProjectController extends Controller
{
/**
* Get Master Project List
*
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
*/
public function index()
{
$projects = Project::where('type', 'layanan')->orderBy('name', 'asc')->get();

return $this->successResponse(ProjectApiResource::collection($projects), 'List Data Master Project');
}
}
1 change: 1 addition & 0 deletions app/Http/Controllers/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
use \App\Traits\ApiResponse;
}
32 changes: 32 additions & 0 deletions app/Http/Resources/ProjectApiResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class ProjectApiResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'ticket_prefix' => $this->ticket_prefix,
'status_type' => $this->status_type,
'type' => $this->type,
'owner' => [
'id' => $this->owner_id,
'name' => $this->owner?->name,
],
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}
42 changes: 42 additions & 0 deletions app/Traits/ApiResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace App\Traits;

use Illuminate\Http\Response;

trait ApiResponse
{
/**
* Success Response
*
* @param mixed $data
* @param string $message
* @param int $code
* @return \Illuminate\Http\JsonResponse
*/
public function successResponse($data, $message = 'Success', $code = Response::HTTP_OK)
{
return response()->json([
'success' => true,
'message' => $message,
'data' => $data,
], $code);
}

/**
* Error Response
*
* @param string $message
* @param int $code
* @param mixed $data
* @return \Illuminate\Http\JsonResponse
*/
public function errorResponse($message, $code = Response::HTTP_BAD_REQUEST, $data = null)
{
return response()->json([
'success' => false,
'message' => $message,
'data' => $data,
], $code);
}
}
1 change: 1 addition & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@
// });

Route::middleware('auth:sanctum')->get('/ticket/id/{id}', [\App\Http\Controllers\Api\TicketController::class, 'show']);
Route::middleware('auth:sanctum')->get('/projects', [\App\Http\Controllers\Api\ProjectController::class, 'index']);
4 changes: 2 additions & 2 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,5 @@
Route::get('callback', [OidcAuthController::class, 'callback'])->name('callback');
});

// Route::get('/kanban/{project}', Kanban::class)->name('filament.pages.kanban');
// Route::get('/scrum/{project}', Scrum::class)->name('filament.pages.scrum');
Route::get('/kanban/{project}', Kanban::class)->name('filament.pages.kanban');
Route::get('/scrum/{project}', Scrum::class)->name('filament.pages.scrum');