268 lines
9.6 KiB
PHP
268 lines
9.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Http\Resources\TestResource;
|
|
use App\Models\Log;
|
|
use App\Models\Test;
|
|
use Illuminate\Http\Request;
|
|
|
|
class TestController extends Controller
|
|
{
|
|
const PAGINATION_COUNT = 20;
|
|
private const FIELD_RULES = [
|
|
'title' => 'required|string|max:255',
|
|
'description' => 'required|string',
|
|
'category_id' => 'nullable|exists:categories,id',
|
|
'questions' => 'required|array|min:1',
|
|
'questions.*' => 'exists:questions,id',
|
|
'closed_at' => 'nullable|date',
|
|
];
|
|
|
|
/**
|
|
* @OA\Get(
|
|
* path="/api/tests",
|
|
* summary="Get a list of tests (paginated)",
|
|
* description="Retrieve a paginated list of tests. Optionally filter by category_id.",
|
|
* tags={"Tests"},
|
|
* @OA\Parameter(
|
|
* name="category_id",
|
|
* in="query",
|
|
* required=false,
|
|
* description="Filter tests by category ID",
|
|
* @OA\Schema(type="integer")
|
|
* ),
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="Paginated list of tests",
|
|
* @OA\JsonContent(
|
|
* type="object",
|
|
* @OA\Property(
|
|
* property="data",
|
|
* type="array",
|
|
* @OA\Items(ref="#/components/schemas/TestResource")
|
|
* ),
|
|
* @OA\Property(
|
|
* property="links",
|
|
* type="object",
|
|
* @OA\Property(property="first", type="string", example="http://api.example.com/tests?page=1"),
|
|
* @OA\Property(property="last", type="string", example="http://api.example.com/tests?page=10"),
|
|
* @OA\Property(property="prev", type="string", nullable=true, example=null),
|
|
* @OA\Property(property="next", type="string", nullable=true, example="http://api.example.com/tests?page=2")
|
|
* ),
|
|
* @OA\Property(
|
|
* property="meta",
|
|
* type="object",
|
|
* @OA\Property(property="current_page", type="integer", example=1),
|
|
* @OA\Property(property="from", type="integer", example=1),
|
|
* @OA\Property(property="last_page", type="integer", example=10),
|
|
* @OA\Property(property="path", type="string", example="http://api.example.com/tests"),
|
|
* @OA\Property(property="per_page", type="integer", example=15),
|
|
* @OA\Property(property="to", type="integer", example=15),
|
|
* @OA\Property(property="total", type="integer", example=150)
|
|
* )
|
|
* )
|
|
* ),
|
|
* @OA\Response(
|
|
* response=401,
|
|
* description="Unauthorized"
|
|
* )
|
|
* )
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$query = Test::with(['author', 'category']);
|
|
|
|
if ($request->has('category_id')) {
|
|
$query->where('category_id', $request->query('category_id'));
|
|
}
|
|
|
|
$questions = $query->paginate(self::PAGINATION_COUNT);
|
|
|
|
return TestResource::collection($questions);
|
|
}
|
|
|
|
/**
|
|
* @OA\Post(
|
|
* path="/api/tests",
|
|
* summary="Create a new test (only admin or creator)",
|
|
* description="Store a new test in the system (only admin or creator).",
|
|
* tags={"Tests"},
|
|
* security={{"bearerAuth":{}}},
|
|
* @OA\RequestBody(
|
|
* required=true,
|
|
* @OA\JsonContent(
|
|
* required={"title","closed_at","questions"},
|
|
* @OA\Property(property="title", type="string", maxLength=255, example="Sample Test"),
|
|
* @OA\Property(property="description", type="string", nullable=true, example="Optional description"),
|
|
* @OA\Property(property="closed_at", type="string", format="date-time", nullable=true, example="2025-12-01 23:59:59"),
|
|
* @OA\Property(property="category_id", type="integer", nullable=true, example=3),
|
|
* @OA\Property(
|
|
* property="questions",
|
|
* type="array",
|
|
* description="Array of question IDs to attach to this test",
|
|
* @OA\Items(type="integer", example=1)
|
|
* )
|
|
* )
|
|
* ),
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="Test created successfully",
|
|
* @OA\JsonContent(ref="#/components/schemas/TestResource")
|
|
* ),
|
|
* @OA\Response(
|
|
* response=401,
|
|
* description="Unauthorized"
|
|
* ),
|
|
* @OA\Response(
|
|
* response=422,
|
|
* description="Validation error"
|
|
* )
|
|
* )
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
$this->authorize('create', Test::class);
|
|
$fields = $request->validate(self::FIELD_RULES);
|
|
|
|
$fields['author_id'] = $request->user()->id;
|
|
|
|
$test = Test::create($fields);
|
|
$test->questions()->sync($fields['questions']);
|
|
|
|
$test->load(['category', 'author', 'questions']);
|
|
Log::writeLog("Test '" . $test->title . "' is created by " . $request->user()->username);
|
|
return new TestResource($test);
|
|
}
|
|
|
|
/**
|
|
* @OA\Get(
|
|
* path="/api/tests/{id}",
|
|
* summary="Get a single test",
|
|
* description="Retrieve a single test with its questions, category, and author",
|
|
* tags={"Tests"},
|
|
* @OA\Parameter(
|
|
* name="id",
|
|
* in="path",
|
|
* required=true,
|
|
* description="Test ID",
|
|
* @OA\Schema(type="integer")
|
|
* ),
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="Test retrieved successfully",
|
|
* @OA\JsonContent(ref="#/components/schemas/TestResource")
|
|
* ),
|
|
* @OA\Response(
|
|
* response=404,
|
|
* description="Test not found"
|
|
* )
|
|
* )
|
|
*/
|
|
public function show(Test $test)
|
|
{
|
|
$test->load(['author', 'category', 'questions']);
|
|
return new TestResource($test);
|
|
}
|
|
|
|
/**
|
|
* @OA\Put(
|
|
* path="/api/tests/{id}",
|
|
* summary="Update a test (only admin or creator)",
|
|
* description="Update a test's data and associated questions (only admin/creator).",
|
|
* tags={"Tests"},
|
|
* security={{"bearerAuth":{}}},
|
|
* @OA\Parameter(
|
|
* name="id",
|
|
* in="path",
|
|
* required=true,
|
|
* description="Test ID",
|
|
* @OA\Schema(type="integer")
|
|
* ),
|
|
* @OA\RequestBody(
|
|
* required=true,
|
|
* @OA\JsonContent(
|
|
* required={"title","questions"},
|
|
* @OA\Property(property="title", type="string", maxLength=255, example="Updated Test Title"),
|
|
* @OA\Property(property="description", type="string", nullable=true, example="Optional description"),
|
|
* @OA\Property(property="category_id", type="integer", nullable=true, example=3),
|
|
* @OA\Property(property="closed_at", type="string", format="date-time", nullable=true, example="2025-12-01T23:59:59Z"),
|
|
* @OA\Property(
|
|
* property="questions",
|
|
* type="array",
|
|
* description="Array of question IDs to attach to this test",
|
|
* @OA\Items(type="integer", example=1)
|
|
* )
|
|
* )
|
|
* ),
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="Test updated successfully",
|
|
* @OA\JsonContent(ref="#/components/schemas/TestResource")
|
|
* ),
|
|
* @OA\Response(
|
|
* response=401,
|
|
* description="Unauthorized"
|
|
* ),
|
|
* @OA\Response(
|
|
* response=422,
|
|
* description="Validation error"
|
|
* )
|
|
* )
|
|
*/
|
|
public function update(Request $request, Test $test)
|
|
{
|
|
$this->authorize('update', $test);
|
|
$fields = $request->validate(self::FIELD_RULES);
|
|
|
|
$test->update($fields);
|
|
$test->questions()->sync($fields['questions']);
|
|
|
|
Log::writeLog("Test '" . $test->title . "' is updated by " . $request->user()->username);
|
|
|
|
$test->load(['category', 'author', 'questions']);
|
|
return new TestResource($test);
|
|
}
|
|
|
|
/**
|
|
* @OA\Delete(
|
|
* path="/api/tests/{id}",
|
|
* summary="Delete a test (only admin or creator)",
|
|
* description="Delete a test by ID (only admin or creator).",
|
|
* tags={"Tests"},
|
|
* security={{"bearerAuth":{}}},
|
|
* @OA\Parameter(
|
|
* name="id",
|
|
* in="path",
|
|
* required=true,
|
|
* description="Test ID",
|
|
* @OA\Schema(type="integer")
|
|
* ),
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="Test deleted successfully",
|
|
* @OA\JsonContent(
|
|
* type="object",
|
|
* @OA\Property(property="message", type="string", example="The test was deleted")
|
|
* )
|
|
* ),
|
|
* @OA\Response(
|
|
* response=401,
|
|
* description="Unauthorized"
|
|
* ),
|
|
* @OA\Response(
|
|
* response=404,
|
|
* description="Test not found"
|
|
* )
|
|
* )
|
|
*/
|
|
public function destroy(Request $request, Test $test)
|
|
{
|
|
$this->authorize('delete', $test);
|
|
$test->delete();
|
|
Log::writeLog("Test '" . $test->title . "' is deleted by " . $request->user()->username);
|
|
|
|
return ['message' => 'The hitcount was deleted'];
|
|
}
|
|
}
|