These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | use BZIon\Form\Creator\ModelFormCreator; |
||
4 | use Symfony\Component\Form\Form; |
||
5 | use Symfony\Component\HttpFoundation\RedirectResponse; |
||
6 | use Symfony\Component\HttpFoundation\Response; |
||
7 | |||
8 | /** |
||
9 | * A controller with actions for creating, reading, updating and deleting models |
||
10 | * @package BZiON\Controllers |
||
11 | */ |
||
12 | abstract class CRUDController extends JSONController |
||
13 | { |
||
14 | /** |
||
15 | * Make sure that the data of a form is valid, only called when creating a |
||
16 | * new object |
||
17 | * @param Form $form The submitted form |
||
18 | * @return void |
||
19 | */ |
||
20 | 1 | protected function validateNew($form) |
|
21 | { |
||
22 | 1 | } |
|
23 | |||
24 | /** |
||
25 | * Make sure that the data of a form is valid, only called when editing an |
||
26 | * existing object |
||
27 | * @param Form $form The submitted form |
||
28 | * @param PermissionModel $model The model being edited |
||
29 | * @return void |
||
30 | */ |
||
31 | protected function validateEdit($form, $model) |
||
32 | { |
||
33 | } |
||
34 | |||
35 | /** |
||
36 | * Make sure that the data of a form is valid |
||
37 | * @param Form $form The submitted form |
||
38 | * @return void |
||
39 | */ |
||
40 | protected function validate($form) |
||
41 | { |
||
42 | } |
||
43 | |||
44 | /** |
||
45 | * Delete a model |
||
46 | * @param PermissionModel $model The model we want to delete |
||
47 | * @param Player $me The user who wants to delete the model |
||
48 | * @param Closure|null $onSuccess Something to do when the model is deleted |
||
49 | * @throws ForbiddenException |
||
50 | * @return mixed The response to show to the user |
||
51 | */ |
||
52 | 1 | protected function delete(PermissionModel $model, Player $me, $onSuccess = null) |
|
53 | { |
||
54 | 1 | if ($model->isDeleted()) { |
|
55 | // We will have to hard delete the model |
||
56 | $hard = true; |
||
57 | $message = 'hardDelete'; |
||
58 | $action = 'Erase forever'; |
||
59 | } else { |
||
60 | 1 | $hard = false; |
|
61 | 1 | $message = 'softDelete'; |
|
62 | 1 | $action = 'Delete'; |
|
63 | } |
||
64 | |||
65 | 1 | if (!$this->canDelete($me, $model, $hard)) { |
|
66 | throw new ForbiddenException($this->getMessage($model, $message, 'forbidden')); |
||
67 | } |
||
68 | |||
69 | 1 | $successMessage = $this->getMessage($model, $message, 'success'); |
|
70 | 1 | $redirection = $this->redirectToList($model); |
|
71 | |||
72 | 1 | return $this->showConfirmationForm( |
|
73 | 1 | View Code Duplication | function () use ($model, $hard, $redirection, $onSuccess) { |
0 ignored issues
–
show
|
|||
74 | if ($hard) { |
||
75 | $model->wipe(); |
||
76 | 1 | } else { |
|
77 | $model->delete(); |
||
78 | } |
||
79 | 1 | ||
80 | 1 | if ($onSuccess) { |
|
81 | 1 | $response = $onSuccess(); |
|
82 | if ($response instanceof Response) { |
||
83 | return $response; |
||
84 | } |
||
85 | } |
||
86 | 1 | ||
87 | 1 | return $redirection; |
|
88 | }, |
||
89 | $this->getMessage($model, $message, 'confirm'), |
||
90 | $successMessage, |
||
91 | $action, |
||
92 | null, |
||
93 | 'confirmation.html.twig', |
||
94 | false, |
||
95 | true |
||
96 | ); |
||
97 | } |
||
98 | |||
99 | protected function restore(PermissionModel $model, Player $me, $onSuccess) |
||
100 | { |
||
101 | 1 | if (!$this->canDelete($me, $model)) { |
|
102 | throw new ForbiddenException($this->getMessage($model, 'restore', 'forbidden')); |
||
103 | 1 | } |
|
104 | 1 | ||
105 | if (!$model->isDeleted()) { |
||
106 | throw new LogicException('You cannot restore an object that is not marked as deleted.'); |
||
107 | 1 | } |
|
108 | 1 | ||
109 | $successMessage = $this->getMessage($model, 'restore', 'success'); |
||
110 | 1 | ||
111 | 1 | View Code Duplication | return $this->showConfirmationForm(function () use ($model, $successMessage, $onSuccess) { |
112 | 1 | $model->restore(); |
|
113 | 1 | ||
114 | 1 | if ($onSuccess) { |
|
115 | 1 | $response = $onSuccess(); |
|
116 | 1 | if ($response instanceof Response) { |
|
117 | return $response; |
||
118 | 1 | } |
|
119 | 1 | } |
|
120 | 1 | ||
121 | return $this->redirectTo($model); |
||
122 | }, $this->getMessage($model, 'restore', 'confirm'), $successMessage, 'Restore'); |
||
123 | } |
||
124 | |||
125 | 1 | /** |
|
126 | * Create a model |
||
127 | * |
||
128 | * This method requires that you have implemented enter() and a form creator |
||
129 | 1 | * for the model |
|
130 | * |
||
131 | * @param Player $me The user who wants to create the model |
||
132 | * @param Closure|null $onSuccess The function to call on success |
||
133 | * @throws ForbiddenException |
||
134 | * @return mixed The response to show to the user |
||
135 | */ |
||
136 | protected function create(Player $me, $onSuccess = null) |
||
137 | { |
||
138 | if (!$this->canCreate($me)) { |
||
139 | throw new ForbiddenException($this->getMessage($this->getName(), 'create', 'forbidden')); |
||
140 | } |
||
141 | |||
142 | $creator = $this->getFormCreator(); |
||
143 | $form = $creator->create()->handleRequest($this->getRequest()); |
||
144 | |||
145 | if ($form->isSubmitted()) { |
||
146 | $this->validate($form); |
||
147 | $this->validateNew($form); |
||
148 | if ($form->isValid()) { |
||
149 | $model = $creator->enter($form); |
||
150 | $this->getFlashBag()->add("success", |
||
151 | $this->getMessage($model, 'create', 'success')); |
||
152 | |||
153 | if ($onSuccess) { |
||
154 | $response = $onSuccess($model); |
||
155 | if ($response instanceof Response) { |
||
156 | return $response; |
||
157 | } |
||
158 | } |
||
159 | |||
160 | return $this->redirectTo($model); |
||
161 | } |
||
162 | } |
||
163 | |||
164 | return array("form" => $form->createView()); |
||
165 | } |
||
166 | |||
167 | /** |
||
168 | * Edit a model |
||
169 | * |
||
170 | * This method requires that you have implemented update() and a form creator |
||
171 | * for the model |
||
172 | * |
||
173 | * @param PermissionModel $model The model we want to edit |
||
174 | * @param Player $me The user who wants to edit the model |
||
175 | * @param string $type The name of the variable to pass to the view |
||
176 | 1 | * @throws ForbiddenException |
|
177 | * @return mixed The response to show to the user |
||
178 | 1 | */ |
|
179 | protected function edit(PermissionModel $model, Player $me, $type) |
||
180 | { |
||
181 | if (!$this->canEdit($me, $model)) { |
||
182 | throw new ForbiddenException($this->getMessage($model, 'edit', 'forbidden')); |
||
183 | } |
||
184 | |||
185 | $creator = $this->getFormCreator($model); |
||
186 | $form = $creator->create()->handleRequest($this->getRequest()); |
||
187 | 1 | ||
188 | if ($form->isSubmitted()) { |
||
189 | 1 | $this->validate($form); |
|
190 | $this->validateEdit($form, $model); |
||
191 | 1 | if ($form->isValid()) { |
|
192 | $creator->update($form, $model); |
||
193 | $this->getFlashBag()->add("success", |
||
194 | $this->getMessage($model, 'edit', 'success')); |
||
195 | |||
196 | return $this->redirectTo($model); |
||
197 | } |
||
198 | } |
||
199 | |||
200 | return array("form" => $form->createView(), $type => $model); |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * Find whether a player can delete a model |
||
205 | * |
||
206 | * @param Player $player The player who wants to delete the model |
||
207 | * @param PermissionModel $model The model that will be deleted |
||
208 | * @param bool $hard Whether to hard-delete the model instead of soft-deleting it |
||
209 | * @return bool |
||
210 | */ |
||
211 | protected function canDelete($player, $model, $hard = false) |
||
212 | { |
||
213 | return $player->canDelete($model, $hard); |
||
214 | } |
||
215 | 1 | ||
216 | /** |
||
217 | 1 | * Find whether a player can create a model |
|
218 | 1 | * |
|
219 | * @param Player $player The player who wants to create a model |
||
220 | * @return bool |
||
221 | */ |
||
222 | protected function canCreate($player) |
||
223 | { |
||
224 | $modelName = $this->getName(); |
||
225 | |||
226 | return $player->canCreate($modelName); |
||
227 | } |
||
228 | |||
229 | /** |
||
230 | 1 | * Find whether a player can edit a model |
|
231 | * |
||
232 | 1 | * @param Player $player The player who wants to delete the model |
|
233 | 1 | * @param PermissionModel $model The model which will be edited |
|
234 | * @return bool |
||
235 | 1 | */ |
|
236 | protected function canEdit($player, $model) |
||
237 | { |
||
238 | return $player->canEdit($model); |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * Get a redirection response to a model |
||
243 | * |
||
244 | 1 | * Goes to a list of models of the same type if the provided model does not |
|
245 | * have a URL |
||
246 | 1 | * |
|
247 | 1 | * @param ModelInterface $model The model to redirect to |
|
248 | * @return Response |
||
249 | 1 | */ |
|
250 | 1 | protected function redirectTo($model) |
|
251 | { |
||
252 | 1 | if ($model instanceof UrlModel) { |
|
253 | return new RedirectResponse($model->getUrl()); |
||
254 | } else { |
||
255 | return $this->redirectToList($model); |
||
256 | } |
||
257 | } |
||
258 | |||
259 | /** |
||
260 | * Get a redirection response to a list of models |
||
261 | * |
||
262 | * @param ModelInterface $model The model to whose list we should redirect |
||
263 | 1 | * @return Response |
|
264 | */ |
||
265 | 1 | protected function redirectToList($model) |
|
266 | 1 | { |
|
267 | $route = $model->getRouteName('list'); |
||
268 | 1 | $url = Service::getGenerator()->generate($route); |
|
269 | |||
270 | 1 | return new RedirectResponse($url); |
|
271 | 1 | } |
|
272 | 1 | ||
273 | /** |
||
274 | * Dynamically get the form to show to the user |
||
275 | 1 | * |
|
276 | * @param \Model|null $model The model being edited, `null` if we're creating one |
||
277 | 1 | * @return ModelFormCreator |
|
278 | */ |
||
279 | private function getFormCreator($model = null) |
||
280 | { |
||
281 | $type = ($model instanceof Model) ? $model->getType() : $this->getName(); |
||
282 | $type = ucfirst($type); |
||
283 | |||
284 | 1 | $creatorClass = "\\BZIon\\Form\\Creator\\{$type}FormCreator"; |
|
285 | $creator = new $creatorClass($model, $this->getMe(), $this); |
||
286 | 1 | ||
287 | return $creator; |
||
288 | } |
||
289 | |||
290 | /** |
||
291 | * Get a message to show to the user |
||
292 | * @todo Use the $escape parameter |
||
293 | * @param \ModelInterface|string $model The model (or type) to show a message for |
||
294 | * @param string $action The action that will be performed (softDelete, hardDelete, create or edit) |
||
295 | * @param string $status The message's status (confirm, error or success) |
||
296 | 1 | * @return string |
|
297 | */ |
||
298 | private function getMessage($model, $action, $status, $escape = true) |
||
299 | { |
||
300 | if ($model instanceof Model) { |
||
301 | $type = strtolower($model->getTypeForHumans()); |
||
302 | 1 | ||
303 | if ($model instanceof NamedModel) { |
||
304 | 1 | // Twig will not escape the message on confirmation forms |
|
305 | $name = $model->getName(); |
||
306 | if ($status == 'confirm') { |
||
307 | $name = Model::escape($name); |
||
308 | 1 | } |
|
309 | |||
310 | 1 | $messages = $this->getMessages($type, $name); |
|
311 | |||
312 | return $messages[$action][$status]['named']; |
||
313 | } else { |
||
314 | 1 | $messages = $this->getMessages($type); |
|
315 | 1 | ||
316 | return $messages[$action][$status]['unnamed']; |
||
317 | } |
||
318 | 1 | } else { |
|
319 | 1 | $messages = $this->getMessages(strtolower($model)); |
|
320 | |||
321 | return $messages[$action][$status]; |
||
322 | } |
||
323 | } |
||
324 | 1 | ||
325 | 1 | /** |
|
326 | * Get a list of messages to show to the user |
||
327 | * @param string $type The type of the model that the message refers to |
||
328 | 1 | * @param string $name The name of the model |
|
329 | 1 | * @return array |
|
330 | */ |
||
331 | protected function getMessages($type, $name = '') |
||
332 | 1 | { |
|
333 | 1 | return array( |
|
334 | 'hardDelete' => array( |
||
335 | 'confirm' => array( |
||
336 | 'named' => <<<"WARNING" |
||
337 | Are you sure you want to wipe <strong>$name</strong>?<br /> |
||
338 | 1 | <strong><em>DANGER</em></strong>: This action will <strong>permanently</strong> |
|
339 | 1 | erase the $type from the database, including any objects directly related to it! |
|
340 | WARNING |
||
341 | , |
||
342 | 1 | 'unnamed' => <<<"WARNING" |
|
343 | 1 | Are you sure you want to wipe this $type?<br /> |
|
344 | <strong><em>DANGER</em></strong>: This action will <strong>permanently</strong> |
||
345 | erase the $type from the database, including any objects directly related to it! |
||
346 | WARNING |
||
347 | 1 | ), |
|
348 | 'forbidden' => array( |
||
349 | 1 | 'named' => "You are not allowed to delete the $type $name", |
|
350 | 1 | 'unnamed' => "You are not allowed to delete this $type", |
|
351 | ), |
||
352 | 'success' => array( |
||
353 | 'named' => "The $type $name was permanently erased from the database", |
||
354 | 'unnamed' => "The $type has been permanently erased from the database", |
||
355 | ), |
||
356 | ), |
||
357 | 'softDelete' => array( |
||
358 | 'confirm' => array( |
||
359 | 'named' => "Are you sure you want to delete <strong>$name</strong>?", |
||
360 | 'unnamed' => "Are you sure you want to delete this $type?", |
||
361 | ), |
||
362 | 'forbidden' => array( |
||
363 | 'named' => "You are not allowed to delete the $type $name", |
||
364 | 'unnamed' => "You are not allowed to delete this $type", |
||
365 | ), |
||
366 | 'success' => array( |
||
367 | 'named' => "The $type $name was deleted successfully", |
||
368 | 'unnamed' => "The $type was deleted successfully", |
||
369 | ), |
||
370 | ), |
||
371 | 'restore' => array( |
||
372 | 'confirm' => array( |
||
373 | 'named' => "Are you sure you want to restore <strong>$name</strong>?", |
||
374 | 'unnamed' => "Are you sure you want to restore this $type?", |
||
375 | ), |
||
376 | 'forbidden' => array( |
||
377 | 'named' => "You are not allowed to restore the $type $name", |
||
378 | 'unnamed' => "You are not allowed to restore this $type", |
||
379 | ), |
||
380 | 'success' => array( |
||
381 | 'named' => "The $type $name has been restored successfully", |
||
382 | 'unnamed' => "The $type has been restored successfully", |
||
383 | ), |
||
384 | ), |
||
385 | 'edit' => array( |
||
386 | 'forbidden' => array( |
||
387 | 'named' => "You are not allowed to edit the $type $name", |
||
388 | 'unnamed' => "You are not allowed to edit this $type", |
||
389 | ), |
||
390 | 'success' => array( |
||
391 | 'named' => "The $type $name has been successfully updated", |
||
392 | 'unnamed' => "The $type was updated successfully", |
||
393 | ), |
||
394 | ), |
||
395 | 'create' => array( |
||
396 | 'forbidden' => "You are not allowed to create a new $type", |
||
397 | 'success' => array( |
||
398 | 'named' => "The $type $name was created successfully", |
||
399 | 'unnamed' => "The $type was created successfully", |
||
400 | ), |
||
401 | ), |
||
402 | ); |
||
403 | } |
||
404 | } |
||
405 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.