Passed
Push — main ( db60ec...ce87af )
by Nobufumi
02:23
created

CreateEditTrait::handleRenderCreateForm()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 5
rs 10
1
<?php
2
3
namespace Jidaikobo\Kontiki\Controllers\Traits;
4
5
use Psr\Http\Message\ResponseInterface as Response;
6
use Psr\Http\Message\ServerRequestInterface as Request;
7
8
trait CreateEditTrait
9
{
10
    private array $pendingMetaData;
11
12
    public function handleRenderCreateForm(
13
        Request $request,
14
        Response $response
15
    ): Response {
16
        return $this->renderCreateForm($request, $response);
17
    }
18
19
    public function handleRenderEditForm(
20
        Request $request,
21
        Response $response,
22
        array $args
23
    ): Response {
24
        return $this->renderEditForm($request, $response, $args);
25
    }
26
27
    public function renderCreateForm(
28
        Request $request,
29
        Response $response
30
    ): Response {
31
        $data = $this->model->getDataForForm('create', $this->flashManager);
32
        $fields = $this->model->getFields('create', $data);
33
34
        $formHtml = $this->formService->formHtml(
35
            "/{$this->adminDirName}/create",
36
            $fields,
37
            $this->csrfManager->getToken(),
38
            '',
39
            __("x_save", 'Save :name', ['name' => __($this->label)]),
40
        );
41
        $formHtml = $this->formService->addMessages(
42
            $formHtml,
43
            $this->flashManager->getData('errors', [])
44
        );
45
46
        return $this->renderResponse(
0 ignored issues
show
Bug introduced by
It seems like renderResponse() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

46
        return $this->/** @scrutinizer ignore-call */ renderResponse(
Loading history...
47
            $response,
48
            __("x_create", 'Create :name', ['name' => __($this->label)]),
49
            $formHtml
50
        );
51
    }
52
53
    public function renderEditForm(
54
        Request $request,
55
        Response $response,
56
        array $args
57
    ): Response {
58
        $id = $args['id'];
59
        $data = $this->model->getDataForForm('edit', $this->flashManager, $id);
60
61
        if (!$data) {
62
            return $this->redirectResponse(
0 ignored issues
show
Bug introduced by
It seems like redirectResponse() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

62
            return $this->/** @scrutinizer ignore-call */ redirectResponse(
Loading history...
63
                $request,
64
                $response,
65
                "/{$this->adminDirName}/index"
66
            );
67
        }
68
69
        $fields = $this->model->getFields('edit', $data);
70
71
        $formHtml = $this->formService->formHtml(
72
            "/{$this->adminDirName}/edit/{$id}",
73
            $fields,
74
            $this->csrfManager->getToken(),
75
            '',
76
            __("x_save", 'Save :name', ['name' => __($this->label)]),
77
        );
78
        $formHtml = $this->formService->addMessages(
79
            $formHtml,
80
            $this->flashManager->getData('errors', []),
81
            $this->flashManager->getData('success', [])
82
        );
83
84
        return $this->renderResponse(
85
            $response,
86
            __("x_edit", 'Edit :name', ['name' => __($this->label)]),
87
            $formHtml
88
        );
89
    }
90
91
    public function handleCreate(
92
        Request $request,
93
        Response $response
94
    ): Response {
95
        return $this->handleSave($request, $response, 'create');
96
    }
97
98
    public function handleEdit(
99
        Request $request,
100
        Response $response,
101
        array $args
102
    ): Response {
103
        $id = $args['id'];
104
        return $this->handleSave($request, $response, 'edit', $id);
105
    }
106
107
    private function getDefaultRedirect(string $context, ?int $id = null): string
108
    {
109
        return $context === 'create'
110
            ? "/{$this->adminDirName}/create"
111
            : "/{$this->adminDirName}/edit/{$id}";
112
    }
113
114
    private function handleSave(
115
        Request $request,
116
        Response $response,
117
        string $context,
118
        ?int $id = null
119
    ): Response {
120
        $data = $request->getParsedBody() ?? [];
121
        $this->flashManager->setData('data', $data);
122
123
        // redirect preview
124
        if (isset($data['preview']) && $data['preview'] === '1') {
125
            return $this->redirectResponse(
126
                $request,
127
                $response,
128
                "/{$this->adminDirName}/preview"
129
            );
130
        }
131
132
        $defaultRedirect = $this->getDefaultRedirect($context, $id);
133
134
        // validate csrf token
135
        $errorResponse = $this->validateCsrfToken($data, $request, $response, $defaultRedirect);
0 ignored issues
show
Bug introduced by
It seems like validateCsrfToken() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

135
        /** @scrutinizer ignore-call */ 
136
        $errorResponse = $this->validateCsrfToken($data, $request, $response, $defaultRedirect);
Loading history...
136
        if ($errorResponse) {
137
            return $errorResponse;
138
        }
139
140
        // Validate post data
141
        if (!$this->isValidData($data, $context, $id)) {
142
            return $this->redirectResponse($request, $response, $defaultRedirect);
143
        }
144
145
        return $this->processAndRedirect($request, $response, $context, $id, $data);
146
    }
147
148
    /**
149
     * Validate input data against the field definitions.
150
     */
151
    private function isValidData(array $data, string $context, ?int $id): bool
152
    {
153
        $fields = $this->model->getFields($context, $data, $id);
154
155
        $validationResult = $this->model->validateByFields($data, $fields, $id);
156
157
        if (!$validationResult['valid']) {
158
            $this->flashManager->addErrors($validationResult['errors']);
159
            return false;
160
        }
161
162
        return true;
163
    }
164
165
    /**
166
     * Process the save operation and handle redirection.
167
     */
168
    private function processAndRedirect(
169
        Request $request,
170
        Response $response,
171
        string $context,
172
        ?int $id,
173
        array $data
174
    ): Response {
175
        try {
176
            $id = $this->saveData($context, $id, $data);
177
            $this->flashManager->addMessage(
178
                'success',
179
                __(
180
                    "x_save_success_and_redirect",
181
                    ':name Saved successfully. [Go to Index](:url)',
182
                    [
183
                        'name' => __($this->label),
184
                        'url' => env('BASEPATH') . "/{$this->adminDirName}/index"
185
                    ]
186
                )
187
            );
188
            return $this->redirectResponse(
189
                $request,
190
                $response,
191
                "/{$this->adminDirName}/edit/{$id}"
192
            );
193
        } catch (\Exception $e) {
194
            $this->flashManager->addErrors([[$e->getMessage()]]);
195
            return $this->redirectResponse(
196
                $request,
197
                $response,
198
                $this->getDefaultRedirect($context, $id)
199
            );
200
        }
201
    }
202
203
    private function saveData(string $context, ?int $id, array $data): int
204
    {
205
        $data = $this->divideMetaData($data);
206
207
        if ($context === 'create') {
208
            $newId = $this->model->create($data);
209
            if ($newId === null) {
210
                throw new \RuntimeException('Failed to create record. No ID returned.');
211
            }
212
            $this->saveMetaData($newId);
213
            return $newId;
214
        }
215
216
        if ($context === 'edit' && $id !== null) {
217
            $this->model->update($id, $data);
218
            $this->saveMetaData($id);
219
            return $id;
220
        }
221
222
        throw new \InvalidArgumentException('Invalid action type or missing ID.');
223
    }
224
225
    private function divideMetaData(array $data): array
226
    {
227
        $MetaData = [];
228
        foreach ($this->model->getMetaDataFieldDefinitions() as $key => $definition) {
229
            if (isset($data[$key])) {
230
                $MetaData[$key] = $data[$key];
231
                unset($data[$key]);
232
            }
233
        }
234
235
        if (!empty($MetaData)) {
236
            $this->pendingMetaData = $MetaData;
237
        }
238
239
        return $data;
240
    }
241
242
    private function saveMetaData(int $id): void
243
    {
244
        if (empty($this->pendingMetaData)) {
245
            return;
246
        }
247
248
        foreach ($this->pendingMetaData as $key => $value) {
249
            $existing = $this->model->getMetaData($id, $key);
250
251
            if ($value === '' || $value === null) {
252
                $this->model->deleteMetaData($id, $key);
253
            } elseif ($existing !== null) {
254
                $this->model->updateMetaData($id, $key, $value);
255
            } else {
256
                $this->model->createMetaData($id, $key, $value);
257
            }
258
        }
259
        $this->pendingMetaData = [];
260
    }
261
}
262