Passed
Push — master ( d7dee9...483d68 )
by Jan
04:53 queued 10s
created

BaseAdminController::__construct()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 14
c 1
b 0
f 0
nc 4
nop 8
dl 0
loc 25
rs 8.8333

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4
 *
5
 * Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as published
9
 * by the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
declare(strict_types=1);
22
23
/**
24
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
25
 *
26
 * Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
27
 *
28
 * This program is free software; you can redistribute it and/or
29
 * modify it under the terms of the GNU General Public License
30
 * as published by the Free Software Foundation; either version 2
31
 * of the License, or (at your option) any later version.
32
 *
33
 * This program is distributed in the hope that it will be useful,
34
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36
 * GNU General Public License for more details.
37
 *
38
 * You should have received a copy of the GNU General Public License
39
 * along with this program; if not, write to the Free Software
40
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
41
 */
42
43
namespace App\Controller\AdminPages;
44
45
use App\DataTables\LogDataTable;
46
use App\Entity\Base\AbstractNamedDBElement;
47
use App\Entity\Base\AbstractStructuralDBElement;
48
use App\Entity\UserSystem\User;
49
use App\Exceptions\AttachmentDownloadException;
50
use App\Form\AdminPages\ImportType;
51
use App\Form\AdminPages\MassCreationForm;
52
use App\Services\Attachments\AttachmentManager;
53
use App\Services\Attachments\AttachmentSubmitHandler;
54
use App\Services\EntityExporter;
55
use App\Services\EntityImporter;
56
use App\Services\LogSystem\EventCommentHelper;
57
use App\Services\LogSystem\HistoryHelper;
58
use App\Services\LogSystem\TimeTravel;
59
use App\Services\StructuralElementRecursionHelper;
60
use Doctrine\ORM\EntityManagerInterface;
61
use InvalidArgumentException;
62
use Omines\DataTablesBundle\DataTableFactory;
63
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
64
use Symfony\Component\Form\FormInterface;
65
use Symfony\Component\HttpFoundation\File\UploadedFile;
66
use Symfony\Component\HttpFoundation\RedirectResponse;
67
use Symfony\Component\HttpFoundation\Request;
68
use Symfony\Component\HttpFoundation\Response;
69
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
70
use Symfony\Component\Validator\ConstraintViolationList;
71
use Symfony\Contracts\Translation\TranslatorInterface;
72
73
abstract class BaseAdminController extends AbstractController
74
{
75
    protected $entity_class = '';
76
    protected $form_class = '';
77
    protected $twig_template = '';
78
    protected $route_base = '';
79
    protected $attachment_class = '';
80
    protected $parameter_class = '';
81
82
    protected $passwordEncoder;
83
    protected $translator;
84
    protected $attachmentHelper;
85
    protected $attachmentSubmitHandler;
86
    protected $commentHelper;
87
88
    protected $historyHelper;
89
    protected $timeTravel;
90
    protected $dataTableFactory;
91
92
    public function __construct(TranslatorInterface $translator, UserPasswordEncoderInterface $passwordEncoder,
93
        AttachmentManager $attachmentHelper, AttachmentSubmitHandler $attachmentSubmitHandler,
94
        EventCommentHelper $commentHelper, HistoryHelper $historyHelper, TimeTravel $timeTravel,
95
        DataTableFactory $dataTableFactory)
96
    {
97
        if ('' === $this->entity_class || '' === $this->form_class || '' === $this->twig_template || '' === $this->route_base) {
0 ignored issues
show
introduced by
The condition '' === $this->twig_template is always false.
Loading history...
introduced by
The condition '' === $this->form_class is always false.
Loading history...
introduced by
The condition '' === $this->route_base is always false.
Loading history...
98
            throw new InvalidArgumentException('You have to override the $entity_class, $form_class, $route_base and $twig_template value in your subclasss!');
99
        }
100
101
        if ('' === $this->attachment_class) {
0 ignored issues
show
introduced by
The condition '' === $this->attachment_class is always false.
Loading history...
102
            throw new InvalidArgumentException('You have to override the $attachment_class value in your subclass!');
103
        }
104
105
        if ('' === $this->parameter_class) {
0 ignored issues
show
introduced by
The condition '' === $this->parameter_class is always false.
Loading history...
106
            throw new InvalidArgumentException('You have to override the $parameter_class value in your subclass!');
107
        }
108
109
        $this->translator = $translator;
110
        $this->passwordEncoder = $passwordEncoder;
111
        $this->attachmentHelper = $attachmentHelper;
112
        $this->attachmentSubmitHandler = $attachmentSubmitHandler;
113
        $this->commentHelper = $commentHelper;
114
        $this->historyHelper = $historyHelper;
115
        $this->timeTravel = $timeTravel;
116
        $this->dataTableFactory = $dataTableFactory;
117
    }
118
119
    protected function _edit(AbstractNamedDBElement $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
120
    {
121
        $this->denyAccessUnlessGranted('read', $entity);
122
123
        $timeTravel_timestamp = null;
124
        if (null !== $timestamp) {
125
            $this->denyAccessUnlessGranted('@tools.timetravel');
126
            $this->denyAccessUnlessGranted('show_history', $entity);
127
            //If the timestamp only contains numbers interpret it as unix timestamp
128
            if (ctype_digit($timestamp)) {
129
                $timeTravel_timestamp = new \DateTime();
130
                $timeTravel_timestamp->setTimestamp((int) $timestamp);
131
            } else { //Try to parse it via DateTime
132
                $timeTravel_timestamp = new \DateTime($timestamp);
133
            }
134
            $this->timeTravel->revertEntityToTimestamp($entity, $timeTravel_timestamp);
135
        }
136
137
        if ($this->isGranted('show_history', $entity)) {
138
            $table = $this->dataTableFactory->createFromType(
139
                LogDataTable::class,
140
                [
141
                    'filter_elements' => $this->historyHelper->getAssociatedElements($entity),
142
                    'mode' => 'element_history',
143
                ],
144
                ['pageLength' => 10]
145
            )
146
                ->handleRequest($request);
147
148
            if ($table->isCallback()) {
149
                return $table->getResponse();
150
            }
151
        } else {
152
            $table = null;
153
        }
154
155
        $form = $this->createForm($this->form_class, $entity, [
0 ignored issues
show
Bug introduced by
$this->form_class of type mixed is incompatible with the type string expected by parameter $type of Symfony\Bundle\Framework...ontroller::createForm(). ( Ignorable by Annotation )

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

155
        $form = $this->createForm(/** @scrutinizer ignore-type */ $this->form_class, $entity, [
Loading history...
156
            'attachment_class' => $this->attachment_class,
157
            'parameter_class' => $this->parameter_class,
158
            'disabled' => null !== $timeTravel_timestamp ? true : null,
159
        ]);
160
161
        $form->handleRequest($request);
162
        if ($form->isSubmitted() && $form->isValid()) {
163
            //Check if we editing a user and if we need to change the password of it
164
            if ($entity instanceof User && ! empty($form['new_password']->getData())) {
165
                $password = $this->passwordEncoder->encodePassword($entity, $form['new_password']->getData());
166
                $entity->setPassword($password);
167
                //By default the user must change the password afterwards
168
                $entity->setNeedPwChange(true);
169
            }
170
171
            //Upload passed files
172
            $attachments = $form['attachments'];
173
            foreach ($attachments as $attachment) {
174
                /** @var FormInterface $attachment */
175
                $options = [
176
                    'secure_attachment' => $attachment['secureFile']->getData(),
177
                    'download_url' => $attachment['downloadURL']->getData(),
178
                ];
179
180
                try {
181
                    $this->attachmentSubmitHandler->handleFormSubmit($attachment->getData(), $attachment['file']->getData(), $options);
182
                } catch (AttachmentDownloadException $attachmentDownloadException) {
183
                    $this->addFlash(
184
                        'error',
185
                        $this->translator->trans('attachment.download_failed').' '.$attachmentDownloadException->getMessage()
186
                    );
187
                }
188
            }
189
190
            $this->commentHelper->setMessage($form['log_comment']->getData());
191
192
            $em->persist($entity);
193
            $em->flush();
194
            $this->addFlash('success', 'entity.edit_flash');
195
196
            //Rebuild form, so it is based on the updated data. Important for the parent field!
197
            //We can not use dynamic form events here, because the parent entity list is build from database!
198
            $form = $this->createForm($this->form_class, $entity, [
199
                'attachment_class' => $this->attachment_class,
200
                'parameter_class' => $this->parameter_class
201
            ]);
202
        } elseif ($form->isSubmitted() && ! $form->isValid()) {
203
            $this->addFlash('error', 'entity.edit_flash.invalid');
204
        }
205
206
        return $this->render($this->twig_template, [
0 ignored issues
show
Bug introduced by
$this->twig_template of type mixed is incompatible with the type string expected by parameter $view of Symfony\Bundle\Framework...actController::render(). ( Ignorable by Annotation )

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

206
        return $this->render(/** @scrutinizer ignore-type */ $this->twig_template, [
Loading history...
207
            'entity' => $entity,
208
            'form' => $form->createView(),
209
            'attachment_helper' => $this->attachmentHelper,
210
            'route_base' => $this->route_base,
211
            'datatable' => $table,
212
            'timeTravel' => $timeTravel_timestamp,
213
        ]);
214
    }
215
216
    protected function _new(Request $request, EntityManagerInterface $em, EntityImporter $importer)
217
    {
218
        /** @var AbstractStructuralDBElement|User $new_entity */
219
        $new_entity = new $this->entity_class();
220
221
        $this->denyAccessUnlessGranted('read', $new_entity);
222
223
        //Basic edit form
224
        $form = $this->createForm($this->form_class, $new_entity, [
0 ignored issues
show
Bug introduced by
$this->form_class of type mixed is incompatible with the type string expected by parameter $type of Symfony\Bundle\Framework...ontroller::createForm(). ( Ignorable by Annotation )

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

224
        $form = $this->createForm(/** @scrutinizer ignore-type */ $this->form_class, $new_entity, [
Loading history...
225
            'attachment_class' => $this->attachment_class,
226
            'parameter_class' => $this->parameter_class,
227
        ]);
228
229
        $form->handleRequest($request);
230
231
        if ($form->isSubmitted() && $form->isValid()) {
232
            if ($new_entity instanceof User && ! empty($form['new_password']->getData())) {
233
                $password = $this->passwordEncoder->encodePassword($new_entity, $form['new_password']->getData());
234
                $new_entity->setPassword($password);
235
                //By default the user must change the password afterwards
236
                $new_entity->setNeedPwChange(true);
237
            }
238
239
            //Upload passed files
240
            $attachments = $form['attachments'];
241
            foreach ($attachments as $attachment) {
242
                /** @var FormInterface $attachment */
243
                $options = [
244
                    'secure_attachment' => $attachment['secureFile']->getData(),
245
                    'download_url' => $attachment['downloadURL']->getData(),
246
                ];
247
248
                try {
249
                    $this->attachmentSubmitHandler->handleFormSubmit($attachment->getData(), $attachment['file']->getData(), $options);
250
                } catch (AttachmentDownloadException $attachmentDownloadException) {
251
                    $this->addFlash(
252
                        'error',
253
                        $this->translator->trans('attachment.download_failed').' '.$attachmentDownloadException->getMessage()
254
                    );
255
                }
256
            }
257
258
            $this->commentHelper->setMessage($form['log_comment']->getData());
259
260
            $em->persist($new_entity);
261
            $em->flush();
262
            $this->addFlash('success', 'entity.created_flash');
263
264
            return $this->redirectToRoute($this->route_base.'_edit', ['id' => $new_entity->getID()]);
0 ignored issues
show
Bug introduced by
Are you sure $this->route_base of type mixed can be used in concatenation? ( Ignorable by Annotation )

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

264
            return $this->redirectToRoute(/** @scrutinizer ignore-type */ $this->route_base.'_edit', ['id' => $new_entity->getID()]);
Loading history...
265
        }
266
267
        if ($form->isSubmitted() && ! $form->isValid()) {
268
            $this->addFlash('error', 'entity.created_flash.invalid');
269
        }
270
271
        //Import form
272
        $import_form = $this->createForm(ImportType::class, ['entity_class' => $this->entity_class]);
273
        $import_form->handleRequest($request);
274
275
        if ($import_form->isSubmitted() && $import_form->isValid()) {
276
            /** @var UploadedFile $file */
277
            $file = $import_form['file']->getData();
278
            $data = $import_form->getData();
279
280
            $options = [
281
                'parent' => $data['parent'],
282
                'preserve_children' => $data['preserve_children'],
283
                'format' => $data['format'],
284
                'csv_separator' => $data['csv_separator'],
285
            ];
286
287
            $this->commentHelper->setMessage('Import '.$file->getClientOriginalName());
288
289
            $errors = $importer->fileToDBEntities($file, $this->entity_class, $options);
0 ignored issues
show
Bug introduced by
$this->entity_class of type mixed is incompatible with the type string expected by parameter $class_name of App\Services\EntityImporter::fileToDBEntities(). ( Ignorable by Annotation )

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

289
            $errors = $importer->fileToDBEntities($file, /** @scrutinizer ignore-type */ $this->entity_class, $options);
Loading history...
290
291
            foreach ($errors as $name => $error) {
292
                /** @var ConstraintViolationList $error */
293
                $this->addFlash('error', $name.':'.$error);
294
            }
295
        }
296
297
        //Mass creation form
298
        $mass_creation_form = $this->createForm(MassCreationForm::class, ['entity_class' => $this->entity_class]);
299
        $mass_creation_form->handleRequest($request);
300
301
        if ($mass_creation_form->isSubmitted() && $mass_creation_form->isValid()) {
302
            $data = $mass_creation_form->getData();
303
304
            //Create entries based on input
305
            $errors = [];
306
            $results = $importer->massCreation($data['lines'], $this->entity_class, $data['parent'], $errors);
0 ignored issues
show
Bug introduced by
$this->entity_class of type mixed is incompatible with the type string expected by parameter $class_name of App\Services\EntityImporter::massCreation(). ( Ignorable by Annotation )

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

306
            $results = $importer->massCreation($data['lines'], /** @scrutinizer ignore-type */ $this->entity_class, $data['parent'], $errors);
Loading history...
307
308
            //Show errors to user:
309
            foreach ($errors as $error) {
310
                $this->addFlash('error', $error['entity']->getFullPath().':'.$error['violations']);
311
            }
312
313
            //Persist valid entities to DB
314
            foreach ($results as $result) {
315
                $em->persist($result);
316
            }
317
            $em->flush();
318
        }
319
320
        return $this->render($this->twig_template, [
0 ignored issues
show
Bug introduced by
$this->twig_template of type mixed is incompatible with the type string expected by parameter $view of Symfony\Bundle\Framework...actController::render(). ( Ignorable by Annotation )

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

320
        return $this->render(/** @scrutinizer ignore-type */ $this->twig_template, [
Loading history...
321
            'entity' => $new_entity,
322
            'form' => $form->createView(),
323
            'import_form' => $import_form->createView(),
324
            'mass_creation_form' => $mass_creation_form->createView(),
325
            'attachment_helper' => $this->attachmentHelper,
326
            'route_base' => $this->route_base,
327
        ]);
328
    }
329
330
    protected function _delete(Request $request, AbstractNamedDBElement $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse
331
    {
332
        $this->denyAccessUnlessGranted('delete', $entity);
333
334
        if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
335
            $entityManager = $this->getDoctrine()->getManager();
336
337
            //Check if we need to remove recursively
338
            if ($entity instanceof AbstractStructuralDBElement && $request->get('delete_recursive', false)) {
339
                $recursionHelper->delete($entity, false);
340
            } else {
341
                if ($entity instanceof AbstractStructuralDBElement) {
342
                    $parent = $entity->getParent();
343
344
                    //Move all sub entities to the current parent
345
                    foreach ($entity->getSubelements() as $subelement) {
346
                        $subelement->setParent($parent);
347
                        $entityManager->persist($subelement);
348
                    }
349
                }
350
351
                //Remove current element
352
                $entityManager->remove($entity);
353
            }
354
355
            $this->commentHelper->setMessage($request->request->get('log_comment', null));
356
357
            //Flush changes
358
            $entityManager->flush();
359
360
            $this->addFlash('success', 'attachment_type.deleted');
361
        } else {
362
            $this->addFlash('error', 'csfr_invalid');
363
        }
364
365
        return $this->redirectToRoute($this->route_base.'_new');
0 ignored issues
show
Bug introduced by
Are you sure $this->route_base of type mixed can be used in concatenation? ( Ignorable by Annotation )

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

365
        return $this->redirectToRoute(/** @scrutinizer ignore-type */ $this->route_base.'_new');
Loading history...
366
    }
367
368
    protected function _exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response
369
    {
370
        $entity = new $this->entity_class();
371
372
        $this->denyAccessUnlessGranted('read', $entity);
373
374
        $entities = $em->getRepository($this->entity_class)->findAll();
0 ignored issues
show
Bug introduced by
$this->entity_class of type mixed is incompatible with the type string expected by parameter $className of Doctrine\Persistence\Obj...anager::getRepository(). ( Ignorable by Annotation )

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

374
        $entities = $em->getRepository(/** @scrutinizer ignore-type */ $this->entity_class)->findAll();
Loading history...
375
376
        return $exporter->exportEntityFromRequest($entities, $request);
377
    }
378
379
    protected function _exportEntity(AbstractNamedDBElement $entity, EntityExporter $exporter, Request $request): \Symfony\Component\HttpFoundation\Response
380
    {
381
        $this->denyAccessUnlessGranted('read', $entity);
382
383
        return $exporter->exportEntityFromRequest($entity, $request);
384
    }
385
}
386