CreateEditTrait::renderEditForm()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 40
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 25
c 0
b 0
f 0
nc 2
nop 3
dl 0
loc 40
rs 9.52
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
        $formVars = [
35
            'buttonID' => 'mainSubmitBtn',
36
            'buttonText' => __("x_save", 'Save :name', ['name' => __($this->label)]),
37
            'data' => $data
38
        ];
39
40
        $formHtml = $this->formService->formHtml(
41
            "/{$this->adminDirName}/create",
42
            $fields,
43
            $this->csrfManager->getToken(),
44
            $formVars
45
        );
46
        $formHtml = $this->formService->addMessages(
47
            $formHtml,
48
            $this->flashManager->getData('errors', [])
49
        );
50
51
        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

51
        return $this->/** @scrutinizer ignore-call */ renderResponse(
Loading history...
52
            $response,
53
            __("x_create", 'Create :name', ['name' => __($this->label)]),
54
            $formHtml
55
        );
56
    }
57
58
    public function renderEditForm(
59
        Request $request,
60
        Response $response,
61
        array $args
62
    ): Response {
63
        $id = $args['id'];
64
        $data = $this->model->getDataForForm('edit', $this->flashManager, $id);
65
66
        if (!$data) {
67
            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

67
            return $this->/** @scrutinizer ignore-call */ redirectResponse(
Loading history...
68
                $request,
69
                $response,
70
                "/{$this->adminDirName}/index"
71
            );
72
        }
73
74
        $fields = $this->model->getFields('edit', $data);
75
76
        $formVars = [
77
            'buttonID' => 'mainSubmitBtn',
78
            'buttonText' => __("x_save", 'Save :name', ['name' => __($this->label)]),
79
            'data' => $data
80
        ];
81
82
        $formHtml = $this->formService->formHtml(
83
            "/{$this->adminDirName}/edit/{$id}",
84
            $fields,
85
            $this->csrfManager->getToken(),
86
            $formVars,
87
        );
88
        $formHtml = $this->formService->addMessages(
89
            $formHtml,
90
            $this->flashManager->getData('errors', []),
91
            $this->flashManager->getData('success', [])
92
        );
93
94
        return $this->renderResponse(
95
            $response,
96
            __("x_edit", 'Edit :name', ['name' => __($this->label)]),
97
            $formHtml
98
        );
99
    }
100
101
    public function handleCreate(
102
        Request $request,
103
        Response $response
104
    ): Response {
105
        return $this->handleSave($request, $response, 'create');
106
    }
107
108
    public function handleEdit(
109
        Request $request,
110
        Response $response,
111
        array $args
112
    ): Response {
113
        $id = $args['id'];
114
        return $this->handleSave($request, $response, 'edit', $id);
115
    }
116
117
    private function getDefaultRedirect(string $context, ?int $id = null): string
118
    {
119
        return $context === 'create'
120
            ? "/{$this->adminDirName}/create"
121
            : "/{$this->adminDirName}/edit/{$id}";
122
    }
123
124
    private function handleSave(
125
        Request $request,
126
        Response $response,
127
        string $context,
128
        ?int $id = null
129
    ): Response {
130
        $data = $request->getParsedBody() ?? [];
131
        $this->flashManager->setData('data', $data);
132
133
        // redirect preview
134
        if (isset($data['preview']) && $data['preview'] === '1') {
135
            return $this->redirectResponse(
136
                $request,
137
                $response,
138
                "/{$this->adminDirName}/preview"
139
            );
140
        }
141
142
        $defaultRedirect = $this->getDefaultRedirect($context, $id);
143
144
        // validate csrf token
145
        $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

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