Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
Restore
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
4 / 4
8
100.00% covered (success)
100.00%
1 / 1
 restore
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
5
 modifyRestoreQuery
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 beforeRestore
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 afterRestore
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace DevToolbelt\LaravelFastCrud\Actions;
6
7use DevToolbelt\Enums\Http\HttpStatusCode;
8use Illuminate\Database\Eloquent\Builder;
9use Illuminate\Database\Eloquent\Model;
10use Illuminate\Http\JsonResponse;
11use Illuminate\Support\Str;
12use Psr\Http\Message\ResponseInterface;
13
14/**
15 * Provides the restore action for CRUD controllers.
16 *
17 * Restores a soft deleted model instance by calling the model's restore() method.
18 * Requires the model to use Laravel's SoftDeletes trait.
19 *
20 * @method string modelClassName() Returns the Eloquent model class name
21 * @method bool hasModelAttribute(Model $model, string $attributeName) Checks if model has attribute
22 * @method JsonResponse|ResponseInterface answerInvalidUuid(HttpStatusCode $code) Returns invalid UUID error response
23 * @method JsonResponse|ResponseInterface answerRecordNotFound(HttpStatusCode $code = HttpStatusCode::NOT_FOUND)
24 *         Returns not found error response
25 * @method JsonResponse|ResponseInterface answerColumnNotFound(string $field, HttpStatusCode $code) Column not found
26 * @method JsonResponse|ResponseInterface answerSuccess(mixed $data, HttpStatusCode $code, array $meta = [])
27 *         Returns success response
28 */
29trait Restore
30{
31    /**
32     * Restores a soft deleted record by its identifier field.
33     *
34     * @param string $id The identifier value of the record to restore
35     * @param string|null $method Model serialization method (default from config or 'toArray')
36     * @return JsonResponse|ResponseInterface JSON response with the restored record or error response
37     */
38    public function restore(string $id, ?string $method = null): JsonResponse|ResponseInterface
39    {
40        $method = $method ?? config('devToolbelt.fast-crud.restore.method', 'toArray');
41        $httpStatus = HttpStatusCode::from(
42            config('devToolbelt.fast-crud.restore.http_status', HttpStatusCode::OK->value)
43        );
44
45        $validationHttpStatus = HttpStatusCode::from(
46            config('devToolbelt.fast-crud.global.validation.http_status', HttpStatusCode::BAD_REQUEST->value)
47        );
48
49        $findField = config('devToolbelt.fast-crud.restore.find_field')
50            ?? config('devToolbelt.fast-crud.global.find_field', 'id');
51
52        $isUuid = config('devToolbelt.fast-crud.restore.find_field_is_uuid')
53            ?? config('devToolbelt.fast-crud.global.find_field_is_uuid', false);
54
55        $deletedAtField = config('devToolbelt.fast-crud.soft_delete.deleted_at_field', 'deleted_at');
56
57        if ($isUuid && !Str::isUuid($id)) {
58            return $this->answerInvalidUuid($validationHttpStatus);
59        }
60
61        $modelName = $this->modelClassName();
62
63        /** @var Model $model */
64        $model = new $modelName();
65
66        if (!$this->hasModelAttribute($model, $deletedAtField)) {
67            return $this->answerColumnNotFound($deletedAtField, $validationHttpStatus);
68        }
69
70        $query = $modelName::withTrashed()
71            ->where($findField, $id)
72            ->whereNotNull($deletedAtField);
73
74        $this->modifyRestoreQuery($query);
75
76        /** @var Model|null $record */
77        $record = $query->first();
78
79        if ($record === null) {
80            return $this->answerRecordNotFound();
81        }
82
83        $this->beforeRestore($record);
84        $record->restore();
85        $this->afterRestore($record);
86
87        return $this->answerSuccess(data: $record->{$method}(), code: $httpStatus);
88    }
89
90    /**
91     * Hook to modify the restore query before fetching the record.
92     *
93     * Override this method to add additional conditions or scopes,
94     * such as ensuring the user owns the record.
95     *
96     * @param Builder $query The query builder instance
97     *
98     * @example
99     * ```php
100     * protected function modifyRestoreQuery(Builder $query): void
101     * {
102     *     $query->where('user_id', auth()->id());
103     * }
104     * ```
105     */
106    protected function modifyRestoreQuery(Builder $query): void
107    {
108    }
109
110    /**
111     * Hook called before the record is restored.
112     *
113     * Override this method to perform actions before restoration,
114     * such as logging or validation.
115     *
116     * @param Model $record The model instance about to be restored
117     */
118    protected function beforeRestore(Model $record): void
119    {
120    }
121
122    /**
123     * Hook called after the record has been restored.
124     *
125     * Override this method to perform post-restoration actions,
126     * such as clearing cache, dispatching events, or audit logging.
127     *
128     * @param Model $record The restored model instance
129     */
130    protected function afterRestore(Model $record): void
131    {
132    }
133}