Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
FeatureController
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
8 / 8
8
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 index
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 show
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 store
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 update
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 destroy
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 valueTypes
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 categories
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Http\Controllers\v2\Admin;
4
5use App\Http\Controllers\Controller;
6use App\Http\Models\Feature;
7use App\Http\Requests\v2\Feature\DestroyFeatureRequest;
8use App\Http\Requests\v2\Feature\IndexFeatureRequest;
9use App\Http\Requests\v2\Feature\ShowFeatureRequest;
10use App\Http\Requests\v2\Feature\StoreFeatureRequest;
11use App\Http\Requests\v2\Feature\UpdateFeatureRequest;
12use App\Http\Resources\v2\FeatureResource;
13use App\Http\Services\FeatureService;
14use Illuminate\Http\JsonResponse;
15use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
16
17/**
18 * Controller for managing features.
19 *
20 * Only accessible by users with the VENGRESO_ADMIN role.
21 * Provides CRUD operations for the features collection.
22 */
23class FeatureController extends Controller
24{
25    public function __construct(
26        private FeatureService $featureService
27    ) {}
28
29    /**
30     * Get all features with optional category filtering.
31     *
32     * @param  IndexFeatureRequest  $request  Validated request with optional category filter
33     *
34     * @response 200 {
35     *   "result": {
36     *     "data": [
37     *       {
38     *         "id": "...",
39     *         "key": "character_limit",
40     *         "name": "Character Limit",
41     *         "description": "Maximum characters per flycut",
42     *         "value_type": "integer",
43     *         "category": "limits",
44     *         "default_value": 1000,
45     *         "is_active": true,
46     *         "display_order": 1,
47     *         "created_at": 1642521600,
48     *         "updated_at": 1642521600
49     *       }
50     *     ]
51     *   }
52     * }
53     */
54    public function index(IndexFeatureRequest $request): AnonymousResourceCollection
55    {
56        $features = $this->featureService->getAll(
57            category: $request->input('category')
58        );
59
60        return FeatureResource::collection($features);
61    }
62
63    /**
64     * Get a single feature by ID.
65     *
66     * @param  ShowFeatureRequest  $request  Validated request (authorization only)
67     * @param  Feature  $feature  The feature to retrieve (route model binding)
68     *
69     * @response 200 {
70     *   "result": {
71     *     "data": {
72     *       "id": "507f1f77bcf86cd799439011",
73     *       "key": "bold",
74     *       "name": "Bold",
75     *       "description": "Enable bold text formatting",
76     *       "value_type": "boolean",
77     *       "category": "formatting",
78     *       "default_value": true,
79     *       "is_active": true,
80     *       "display_order": 1,
81     *       "created_at": 1642521600,
82     *       "updated_at": 1642521600
83     *     }
84     *   }
85     * }
86     */
87    public function show(ShowFeatureRequest $request, Feature $feature): FeatureResource
88    {
89        return new FeatureResource($feature);
90    }
91
92    /**
93     * Create a new feature.
94     *
95     * @param  StoreFeatureRequest  $request  Validated request with feature data
96     *
97     * @response 201 {
98     *   "result": {
99     *     "data": {
100     *       "id": "507f1f77bcf86cd799439011",
101     *       "key": "new_feature",
102     *       "name": "New Feature",
103     *       "description": "Description of the feature",
104     *       "value_type": "boolean",
105     *       "category": "general",
106     *       "default_value": false,
107     *       "is_active": true,
108     *       "display_order": 0,
109     *       "created_at": 1642521600,
110     *       "updated_at": 1642521600
111     *     }
112     *   }
113     * }
114     */
115    public function store(StoreFeatureRequest $request): JsonResponse
116    {
117        $feature = $this->featureService->create($request->validated());
118
119        return (new FeatureResource($feature))
120            ->response()
121            ->setStatusCode(201);
122    }
123
124    /**
125     * Update an existing feature.
126     *
127     * @param  UpdateFeatureRequest  $request  Validated request with update data
128     * @param  Feature  $feature  The feature to update (route model binding)
129     *
130     * @response 200 {
131     *   "result": {
132     *     "data": {
133     *       "id": "507f1f77bcf86cd799439011",
134     *       "key": "updated_feature",
135     *       "name": "Updated Feature",
136     *       "description": "Updated description",
137     *       "value_type": "boolean",
138     *       "category": "general",
139     *       "default_value": true,
140     *       "is_active": true,
141     *       "display_order": 5,
142     *       "created_at": 1642521600,
143     *       "updated_at": 1642525200
144     *     }
145     *   }
146     * }
147     */
148    public function update(UpdateFeatureRequest $request, Feature $feature): FeatureResource
149    {
150        $feature = $this->featureService->update($feature, $request->validated());
151
152        return new FeatureResource($feature);
153    }
154
155    /**
156     * Delete a feature.
157     *
158     * @param  DestroyFeatureRequest  $request  Validated request (authorization only)
159     * @param  Feature  $feature  The feature to delete (route model binding)
160     *
161     * @response 204 No content
162     */
163    public function destroy(DestroyFeatureRequest $request, Feature $feature): JsonResponse
164    {
165        $this->featureService->delete($feature);
166
167        return response()->json(null, 204);
168    }
169
170    /**
171     * Get available value types for features.
172     *
173     * @response 200 {
174     *   "result": {
175     *     "data": ["boolean", "integer", "string", "array", "object"]
176     *   }
177     * }
178     */
179    public function valueTypes(): JsonResponse
180    {
181        return response()->json([
182            'data' => $this->featureService->getValueTypes(),
183        ]);
184    }
185
186    /**
187     * Get available categories for features.
188     *
189     * @response 200 {
190     *   "result": {
191     *     "data": ["formatting", "limits", "storage", "ai", "general"]
192     *   }
193     * }
194     */
195    public function categories(): JsonResponse
196    {
197        return response()->json([
198            'data' => $this->featureService->getCategories(),
199        ]);
200    }
201}