You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
6.5 KiB

5 months ago
<?php
namespace App\Http\Controllers\Api\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\StoreFormSchemaDefinitionRequest;
use App\Http\Requests\Admin\UpdateFormSchemaDefinitionRequest;
use App\Models\Competition;
use App\Models\FormSchemaDefinition;
2 months ago
use App\Support\SignupApplicationFieldRegistry;
5 months ago
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class FormSchemaDefinitionController extends Controller
{
public function index(Request $request, Competition $competition): JsonResponse
{
$purpose = $request->query('purpose');
$q = FormSchemaDefinition::query()->where('competition_id', $competition->id);
1 month ago
if (in_array($purpose, [
FormSchemaDefinition::PURPOSE_SIGNUP,
FormSchemaDefinition::PURPOSE_REVIEW,
FormSchemaDefinition::PURPOSE_AUDIENCE,
], true)) {
5 months ago
$q->where('purpose', $purpose);
}
$rows = $q->orderByDesc('version')->orderByDesc('id')->get();
2 months ago
$payload = [
5 months ago
'data' => $rows->map(fn (FormSchemaDefinition $s) => $this->toBrief($s, $competition))->values(),
2 months ago
];
if ($purpose === FormSchemaDefinition::PURPOSE_SIGNUP) {
$payload['meta'] = [
'signup_field_registry' => SignupApplicationFieldRegistry::manifest(),
];
}
return response()->json($payload);
5 months ago
}
public function store(StoreFormSchemaDefinitionRequest $request, Competition $competition): JsonResponse
{
$purpose = $request->validated('purpose');
2 months ago
$schemaJson = $request->validated('schema_json');
$schemaError = $this->validateSignupSchemaIfNeeded($purpose, $schemaJson);
if ($schemaError !== null) {
return response()->json(['message' => $schemaError, 'errors' => ['schema_json' => [$schemaError]]], 422);
}
5 months ago
$nextVersion = (int) FormSchemaDefinition::query()
->where('competition_id', $competition->id)
->where('purpose', $purpose)
->max('version') + 1;
$schema = FormSchemaDefinition::query()->create([
'competition_id' => $competition->id,
'purpose' => $purpose,
'name' => $request->validated('name'),
'version' => $nextVersion,
2 months ago
'schema_json' => $schemaJson,
5 months ago
'is_published' => (bool) $request->validated('is_published', false),
]);
return response()->json($this->toDetail($schema, $competition), 201);
}
public function show(Competition $competition, FormSchemaDefinition $formSchema): JsonResponse
{
$this->assertSchemaBelongs($competition, $formSchema);
return response()->json($this->toDetail($formSchema, $competition));
}
public function update(UpdateFormSchemaDefinitionRequest $request, Competition $competition, FormSchemaDefinition $formSchema): JsonResponse
{
$this->assertSchemaBelongs($competition, $formSchema);
2 months ago
$validated = $request->validated();
if (array_key_exists('schema_json', $validated)) {
$schemaError = $this->validateSignupSchemaIfNeeded($formSchema->purpose, $validated['schema_json']);
if ($schemaError !== null) {
return response()->json(['message' => $schemaError, 'errors' => ['schema_json' => [$schemaError]]], 422);
}
}
$formSchema->fill($validated);
5 months ago
$formSchema->save();
return response()->json($this->toDetail($formSchema->fresh(), $competition));
}
public function destroy(Competition $competition, FormSchemaDefinition $formSchema): Response
{
$this->assertSchemaBelongs($competition, $formSchema);
if ((int) $competition->form_schema_id === $formSchema->id
1 month ago
|| (int) $competition->review_form_schema_id === $formSchema->id
|| (int) $competition->audience_form_schema_id === $formSchema->id) {
5 months ago
return response()->json([
'message' => '该版本正被本场赛事使用,请先更换绑定后再删除。',
], 422);
}
$formSchema->delete();
return response()->noContent();
}
private function assertSchemaBelongs(Competition $competition, FormSchemaDefinition $formSchema): void
{
if ((int) $formSchema->competition_id !== (int) $competition->id) {
abort(404);
}
}
/**
* @return array<string, mixed>
*/
private function toBrief(FormSchemaDefinition $s, Competition $competition): array
{
return [
'id' => $s->id,
'competition_id' => $s->competition_id,
'purpose' => $s->purpose,
'name' => $s->name,
'version' => $s->version,
'is_published' => $s->is_published,
'is_current_signup' => (int) $competition->form_schema_id === (int) $s->id,
'is_current_review' => (int) $competition->review_form_schema_id === (int) $s->id,
1 month ago
'is_current_audience' => (int) $competition->audience_form_schema_id === (int) $s->id,
5 months ago
'field_labels' => $this->extractFieldLabels($s->schema_json ?? []),
'created_at' => $s->created_at?->toIso8601String(),
'updated_at' => $s->updated_at?->toIso8601String(),
];
}
/**
* @return array<string, mixed>
*/
private function toDetail(FormSchemaDefinition $s, Competition $competition): array
{
$row = $this->toBrief($s, $competition);
$row['schema_json'] = $s->schema_json ?? [];
2 months ago
if ($s->purpose === FormSchemaDefinition::PURPOSE_SIGNUP) {
$row['signup_field_registry'] = SignupApplicationFieldRegistry::manifest();
}
5 months ago
return $row;
}
/**
* @param array<int, mixed> $schemaJson
* @return array<int, string>
*/
private function extractFieldLabels(array $schemaJson): array
{
$labels = [];
foreach ($schemaJson as $item) {
if (is_array($item) && isset($item['label']) && is_string($item['label']) && $item['label'] !== '') {
$labels[] = $item['label'];
}
if (count($labels) >= 12) {
break;
}
}
return $labels;
}
2 months ago
/**
* @param array<int, mixed> $schemaJson
*/
private function validateSignupSchemaIfNeeded(string $purpose, array $schemaJson): ?string
{
if ($purpose !== FormSchemaDefinition::PURPOSE_SIGNUP) {
return null;
}
return SignupApplicationFieldRegistry::validateSignupSchemaJson($schemaJson);
}
5 months ago
}