Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
Helpers
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
2 / 2
6
100.00% covered (success)
100.00%
1 / 1
 hasModelAttribute
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 runValidation
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2
3declare(strict_types=1);
4
5namespace DevToolbelt\LaravelFastCrud\Traits;
6
7use Illuminate\Database\Eloquent\Model;
8use Illuminate\Http\JsonResponse;
9use Illuminate\Support\Facades\Validator;
10use Psr\Http\Message\ResponseInterface;
11
12/**
13 * Provides helper methods for CRUD controllers.
14 *
15 * This trait contains utility methods used across multiple CRUD actions:
16 * - hasModelAttribute(): Check if a model has a specific attribute
17 * - runValidation(): Validate data using Laravel validation rules
18 *
19 * @method JsonResponse|ResponseInterface answerFail(array $data) Returns JSend fail response
20 */
21trait Helpers
22{
23    /**
24     * Checks if a model has a given attribute.
25     *
26     * Combines fillable, guarded, original, casts, and appends properties
27     * to determine if the attribute exists on the model.
28     *
29     * @param Model $model The model instance to check
30     * @param string $attributeName The attribute name to look for
31     * @return bool True if the attribute exists, false otherwise
32     *
33     * @example
34     * ```php
35     * $model = new Product();
36     * $this->hasModelAttribute($model, 'name'); // true if 'name' is fillable/guarded/etc.
37     * ```
38     */
39    protected function hasModelAttribute(Model $model, string $attributeName): bool
40    {
41        $attributes = array_unique([
42            ...$model->getFillable(),
43            ...$model->getGuarded(),
44            ...array_keys($model->getOriginal()),
45            ...array_keys($model->getCasts()),
46            ...$model->getAppends(),
47        ]);
48
49        return in_array($attributeName, $attributes, true);
50    }
51
52    /**
53     * Run validation on the given data using the provided rules.
54     *
55     * If validation fails, returns a JSend fail response with the following error format:
56     * ```json
57     * {
58     *     "status": "fail",
59     *     "data": [
60     *         {
61     *             "field": "email",
62     *             "error": "required",
63     *             "value": null,
64     *             "message": "The email field is required."
65     *         }
66     *     ],
67     *     "meta": []
68     * }
69     * ```
70     *
71     * @param array<string, mixed> $data The data to validate
72     * @param array<string, mixed> $rules Laravel validation rules
73     * @return JsonResponse|ResponseInterface|null Returns error response if validation fails, null if passes
74     */
75    protected function runValidation(array $data, array $rules): JsonResponse|ResponseInterface|null
76    {
77        if (empty($rules)) {
78            return null;
79        }
80
81        $validator = Validator::make($data, $rules);
82
83        if ($validator->passes()) {
84            return null;
85        }
86
87        $errors = [];
88        $messages = $validator->errors()->toArray();
89
90        foreach ($validator->failed() as $field => $failedRules) {
91            $fieldMessages = $messages[$field] ?? [];
92            $i = 0;
93
94            foreach ($failedRules as $ruleName => $params) {
95                $errors[] = [
96                    'field' => $field,
97                    'error' => strtolower($ruleName),
98                    'value' => $data[$field] ?? null,
99                    'message' => $fieldMessages[$i] ?? null,
100                ];
101                $i++;
102            }
103        }
104
105        return $this->answerFail($errors);
106    }
107}