Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
Restore
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
4 / 4
9
100.00% covered (success)
100.00%
1 / 1
 restore
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
1 / 1
6
 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 Illuminate\Database\Eloquent\Builder;
8use Illuminate\Database\Eloquent\Model;
9use Illuminate\Http\JsonResponse;
10use Illuminate\Support\Str;
11use Psr\Http\Message\ResponseInterface;
12
13/**
14 * Provides the restore action for CRUD controllers.
15 *
16 * Restores a soft deleted model instance by clearing the deleted_at and deleted_by fields.
17 * Uses the same field configuration as the SoftDelete action.
18 *
19 * @method string modelClassName() Returns the Eloquent model class name
20 * @method bool hasModelAttribute(Model $model, string $attributeName) Checks if model has attribute
21 * @method JsonResponse|ResponseInterface answerInvalidUuid() Returns invalid UUID error response
22 * @method JsonResponse|ResponseInterface answerRecordNotFound() Returns not found error response
23 * @method JsonResponse|ResponseInterface answerColumnNotFound(string $field) Returns column not found error response
24 * @method JsonResponse|ResponseInterface answerSuccess(array $data, array $meta = []) Returns success response
25 */
26trait Restore
27{
28    /**
29     * Restores a soft deleted record by its identifier field.
30     *
31     * @param string $id The identifier value of the record to restore
32     * @param string|null $method Model serialization method (default from config or 'toArray')
33     * @return JsonResponse|ResponseInterface JSON response with the restored record or error response
34     */
35    public function restore(string $id, ?string $method = null): JsonResponse|ResponseInterface
36    {
37        $method = $method ?? config('devToolbelt.fast-crud.restore.method', 'toArray');
38        $findField = config('devToolbelt.fast-crud.restore.find_field')
39            ?? config('devToolbelt.fast-crud.global.find_field', 'id');
40
41        $isUuid = config('devToolbelt.fast-crud.restore.find_field_is_uuid')
42            ?? config('devToolbelt.fast-crud.global.find_field_is_uuid', false);
43
44        $deletedAtField = config('devToolbelt.fast-crud.soft_delete.deleted_at_field', 'deleted_at');
45        $deletedByField = config('devToolbelt.fast-crud.soft_delete.deleted_by_field', 'deleted_by');
46
47        if ($isUuid && !Str::isUuid($id)) {
48            return $this->answerInvalidUuid();
49        }
50
51        $modelName = $this->modelClassName();
52
53        /** @var Model $model */
54        $model = new $modelName();
55
56        if (!$this->hasModelAttribute($model, $deletedAtField)) {
57            return $this->answerColumnNotFound($deletedAtField);
58        }
59
60        if (!$this->hasModelAttribute($model, $deletedByField)) {
61            return $this->answerColumnNotFound($deletedByField);
62        }
63
64        $query = $modelName::query()
65            ->where($findField, $id)
66            ->whereNotNull($deletedAtField)
67            ->whereNotNull($deletedByField);
68
69        $this->modifyRestoreQuery($query);
70
71        /** @var Model|null $record */
72        $record = $query->first();
73
74        if ($record === null) {
75            return $this->answerRecordNotFound();
76        }
77
78        $this->beforeRestore($record);
79
80        $record->update([
81            $deletedAtField => null,
82            $deletedByField => null,
83        ]);
84
85        $this->afterRestore($record);
86
87        return $this->answerSuccess($record->{$method}());
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}