AbstractController::handleDataTablesException()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 9
c 0
b 0
f 0
rs 10
cc 2
nc 2
nop 2
1
<?php
2
3
/*
4
 * This file is part of the jquery-datatables-bundle package.
5
 *
6
 * (c) 2018 WEBEWEB
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace WBW\Bundle\JQuery\DataTablesBundle\Controller;
13
14
use Doctrine\ORM\EntityNotFoundException;
15
use Symfony\Component\HttpFoundation\JsonResponse;
16
use Symfony\Component\HttpFoundation\Request;
17
use Symfony\Component\HttpFoundation\Response;
18
use Throwable;
19
use WBW\Bundle\BootstrapBundle\Controller\AbstractController as BaseController;
20
use WBW\Bundle\JQuery\DataTablesBundle\Api\DataTablesColumnInterface;
21
use WBW\Bundle\JQuery\DataTablesBundle\Api\DataTablesWrapperInterface;
22
use WBW\Bundle\JQuery\DataTablesBundle\Event\DataTablesEvent;
23
use WBW\Bundle\JQuery\DataTablesBundle\Exception\BadDataTablesColumnException;
24
use WBW\Bundle\JQuery\DataTablesBundle\Exception\BadDataTablesCSVExporterException;
25
use WBW\Bundle\JQuery\DataTablesBundle\Exception\BadDataTablesEditorException;
26
use WBW\Bundle\JQuery\DataTablesBundle\Exception\BadDataTablesRepositoryException;
27
use WBW\Bundle\JQuery\DataTablesBundle\Exception\UnregisteredDataTablesProviderException;
28
use WBW\Bundle\JQuery\DataTablesBundle\Factory\DataTablesFactory;
29
use WBW\Bundle\JQuery\DataTablesBundle\Helper\DataTablesExportHelper;
30
use WBW\Bundle\JQuery\DataTablesBundle\Manager\DataTablesManagerTrait;
31
use WBW\Bundle\JQuery\DataTablesBundle\Model\DataTablesWrapper;
32
use WBW\Bundle\JQuery\DataTablesBundle\Provider\DataTablesCSVExporterInterface;
33
use WBW\Bundle\JQuery\DataTablesBundle\Provider\DataTablesEditorInterface;
34
use WBW\Bundle\JQuery\DataTablesBundle\Provider\DataTablesProviderInterface;
35
use WBW\Bundle\JQuery\DataTablesBundle\Provider\DataTablesRouterInterface;
36
use WBW\Bundle\JQuery\DataTablesBundle\Repository\DataTablesRepositoryInterface;
37
use WBW\Bundle\JQuery\DataTablesBundle\WBWJQueryDataTablesBundle;
38
use WBW\Library\Database\Helper\PaginateHelper;
39
use WBW\Library\Symfony\Response\SimpleJsonResponseData;
40
use WBW\Library\Symfony\Response\SimpleJsonResponseDataInterface;
41
42
/**
43
 * Abstract controller.
44
 *
45
 * @author webeweb <https://github.com/webeweb>
46
 * @package WBW\Bundle\JQuery\DataTablesBundle\Controller
47
 * @abstract
48
 */
49
abstract class AbstractController extends BaseController {
50
51
    use DataTablesManagerTrait {
52
        setDataTablesManager as public;
53
    }
54
55
    /**
56
     * Build a response.
57
     *
58
     * @param Request $request The request.
59
     * @param string $name The provider name.
60
     * @param SimpleJsonResponseDataInterface $output The output.
61
     * @return Response Returns the response.
62
     * @throws Throwable Throws an exception if an error occurs.
63
     */
64
    protected function buildDataTablesResponse(Request $request, string $name, SimpleJsonResponseDataInterface $output): Response {
65
66
        if (true === $request->isXmlHttpRequest()) {
67
            return new JsonResponse($output);
68
        }
69
70
        switch ($output->getStatus()) {
71
72
            case 200:
73
                $this->notifySuccess($output->getNotify());
0 ignored issues
show
Bug introduced by
It seems like $output->getNotify() can also be of type null; however, parameter $content of WBW\Bundle\BootstrapBund...roller::notifySuccess() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

73
                $this->notifySuccess(/** @scrutinizer ignore-type */ $output->getNotify());
Loading history...
74
                break;
75
76
            case 404:
77
                $this->notifyDanger($output->getNotify());
0 ignored issues
show
Bug introduced by
It seems like $output->getNotify() can also be of type null; however, parameter $content of WBW\Bundle\BootstrapBund...troller::notifyDanger() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

77
                $this->notifyDanger(/** @scrutinizer ignore-type */ $output->getNotify());
Loading history...
78
                break;
79
80
            case 500:
81
                $this->notifyWarning($output->getNotify());
0 ignored issues
show
Bug introduced by
It seems like $output->getNotify() can also be of type null; however, parameter $content of WBW\Bundle\BootstrapBund...roller::notifyWarning() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

81
                $this->notifyWarning(/** @scrutinizer ignore-type */ $output->getNotify());
Loading history...
82
                break;
83
        }
84
85
        return $this->redirectToRoute("wbw_jquery_datatables_index", ["name" => $name]);
86
    }
87
88
    /**
89
     * Dispatch an event.
90
     *
91
     * @param string $eventName The event name.
92
     * @param object[] $entities The entities.
93
     * @param DataTablesProviderInterface|null $provider The provider.
94
     * @return DataTablesEvent Returns the event.
95
     * @throws Throwable Throws an exception if an error occurs.
96
     */
97
    protected function dispatchDataTablesEvent(string $eventName, array $entities, ?DataTablesProviderInterface $provider = null): DataTablesEvent {
98
99
        $event = new DataTablesEvent($eventName, $entities, $provider);
100
        $this->dispatchEvent($eventName, $event);
101
102
        return $event;
103
    }
104
105
    /**
106
     * Export callback.
107
     *
108
     * @param DataTablesWrapperInterface $dtWrapper The wrapper.
109
     * @param DataTablesRepositoryInterface $repository The repository.
110
     * @param DataTablesCSVExporterInterface $dtExporter The exporter.
111
     * @param bool $windows Windows ?
112
     * @return void
113
     * @throws Throwable Throws an exception if an error occurs.
114
     */
115
    protected function exportDataTablesCallback(DataTablesWrapperInterface $dtWrapper, DataTablesRepositoryInterface $repository, DataTablesCSVExporterInterface $dtExporter, bool $windows): void {
116
117
        $stream = fopen("php://output", "w+");
118
        fputcsv($stream, DataTablesExportHelper::convert($dtExporter->exportColumns(), $windows), ";");
119
120
        // Paginates.
121
        $total = $repository->dataTablesCountExported($dtWrapper);
122
        $pages = PaginateHelper::getPagesCount($total, DataTablesRepositoryInterface::REPOSITORY_LIMIT);
123
124
        $em = $this->getEntityManager();
125
126
        for ($i = 0; $i < $pages; ++$i) {
127
128
            // Get the offset and limit.
129
            [$offset, $limit] = PaginateHelper::getPageOffsetAndLimit($i, DataTablesRepositoryInterface::REPOSITORY_LIMIT, $total);
130
131
            // Get the export query with offset and limit.
132
            $result = $repository->dataTablesExportAll($dtWrapper)
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\ORM\Query::iterate() has been deprecated. ( Ignorable by Annotation )

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

132
            $result = /** @scrutinizer ignore-deprecated */ $repository->dataTablesExportAll($dtWrapper)
Loading history...
133
                ->setFirstResult($offset)
134
                ->setMaxResults($limit)
135
                ->getQuery()
136
                ->iterate();
137
138
            while (false !== ($row = $result->next())) {
139
140
                $this->dispatchDataTablesEvent(DataTablesEvent::PRE_EXPORT, [$row[0]], $dtWrapper->getProvider());
141
142
                fputcsv($stream, DataTablesExportHelper::convert($dtExporter->exportRow($row[0]), $windows), ";");
143
144
                $this->dispatchDataTablesEvent(DataTablesEvent::POST_EXPORT, [$row[0]], $dtWrapper->getProvider());
145
            }
146
147
            $em->clear(); // Detach the entity to avoid memory consumption.
148
        }
149
150
        fclose($stream);
151
    }
152
153
    /**
154
     * Get a CSV exporter.
155
     *
156
     * @param DataTablesProviderInterface $dtProvider The provider.
157
     * @return DataTablesCSVExporterInterface Returns the CSV exporter.
158
     * @throws BadDataTablesCSVExporterException Throws a bad CSV exporter exception.
159
     * @throws Throwable Throws an exception if an error occurs.
160
     */
161
    protected function getDataTablesCSVExporter(DataTablesProviderInterface $dtProvider): DataTablesCSVExporterInterface {
162
163
        $context = [
164
            "_controller" => get_class($this),
165
            "_provider"   => get_class($dtProvider),
166
        ];
167
168
        $this->logInfo(sprintf('DataTables controller search for a CSV exporter with name "%s"', $dtProvider->getName()), $context);
169
170
        $dtExporter = $dtProvider->getCSVExporter();
171
        if (false === ($dtExporter instanceof DataTablesCSVExporterInterface)) {
172
            throw new BadDataTablesCSVExporterException($dtExporter);
173
        }
174
175
        $context["_exporter"] = get_class($dtExporter);
176
177
        $this->logInfo(sprintf('DataTables controller found a CSV exporter with name "%s"', $dtProvider->getName()), $context);
178
179
        return $dtExporter;
180
    }
181
182
    /**
183
     * Get a column.
184
     *
185
     * @param DataTablesProviderInterface $dtProvider The provider.
186
     * @param string $data The data.
187
     * @return DataTablesColumnInterface Returns the column.
188
     * @throws BadDataTablesColumnException Throws a bad column exception.
189
     * @throws Throwable Throws an exception if an error occurs.
190
     */
191
    protected function getDataTablesColumn(DataTablesProviderInterface $dtProvider, string $data): DataTablesColumnInterface {
192
193
        $dtWrapper = $this->getDataTablesWrapper($dtProvider);
194
195
        $context = [
196
            "_controller" => get_class($this),
197
            "_provider"   => get_class($dtProvider),
198
            "_wrapper"    => get_class($dtWrapper),
199
        ];
200
201
        $this->logInfo(sprintf('DataTables controller search for a column with name "%s"', $data), $context);
202
203
        $dtColumn = $dtWrapper->getColumn($data);
204
        if (null === $dtColumn) {
205
            throw new BadDataTablesColumnException($data);
206
        }
207
208
        $context["_column"] = get_class($dtColumn);
209
210
        $this->logInfo(sprintf('DataTables controller found a column with name "%s"', $data), $context);
211
212
        return $dtColumn;
213
    }
214
215
    /**
216
     * Get an editor.
217
     *
218
     * @param DataTablesProviderInterface $dtProvider The provider.
219
     * @return DataTablesEditorInterface Returns the editor.
220
     * @throws BadDataTablesEditorException Throws a bad editor exception.
221
     * @throws Throwable Throws an exception if an error occurs.
222
     */
223
    protected function getDataTablesEditor(DataTablesProviderInterface $dtProvider): DataTablesEditorInterface {
224
225
        $context = [
226
            "_controller" => get_class($this),
227
            "_provider"   => get_class($dtProvider),
228
        ];
229
230
        $this->logInfo(sprintf('DataTables controller search for an editor with name "%s"', $dtProvider->getName()), $context);
231
232
        $dtEditor = $dtProvider->getEditor();
233
        if (false === ($dtEditor instanceof DataTablesEditorInterface)) {
234
            throw new BadDataTablesEditorException($dtEditor);
235
        }
236
237
        $context["_editor"] = get_class($dtEditor);
238
239
        $this->logInfo(sprintf('DataTables controller found an editor with name "%s"', $dtProvider->getName()), $context);
240
241
        return $dtEditor;
242
    }
243
244
    /**
245
     * Get an entity by id.
246
     *
247
     * @param DataTablesProviderInterface $dtProvider The provider.
248
     * @param string $id The entity id.
249
     * @return object Returns the entity.
250
     * @throws BadDataTablesRepositoryException Throws a bad repository exception.
251
     * @throws EntityNotFoundException Throws an Entity not found exception.
252
     * @throws Throwable Throws an exception if an error occurs.
253
     */
254
    protected function getDataTablesEntityById(DataTablesProviderInterface $dtProvider, string $id) {
255
256
        $repository = $this->getDataTablesRepository($dtProvider);
257
258
        $context = [
259
            "_controller" => get_class($this),
260
            "_provider"   => get_class($dtProvider),
261
            "_repository" => get_class($repository),
262
        ];
263
264
        $this->logInfo(sprintf("DataTables controller search for an entity with id [%s]", $id), $context);
265
266
        $entity = $repository->find($id);
0 ignored issues
show
Bug introduced by
The method find() does not exist on WBW\Bundle\JQuery\DataTa...blesRepositoryInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to WBW\Bundle\JQuery\DataTa...blesRepositoryInterface. ( Ignorable by Annotation )

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

266
        /** @scrutinizer ignore-call */ 
267
        $entity = $repository->find($id);
Loading history...
267
        if (null === $entity) {
268
            throw EntityNotFoundException::fromClassNameAndIdentifier($dtProvider->getEntity(), [$id]);
269
        }
270
271
        $context["_entity"] = get_class($entity);
272
273
        $this->logInfo(sprintf("DataTables controller found an entity with id [%s]", $id), $context);
274
275
        return $entity;
276
    }
277
278
    /**
279
     * Get the provider.
280
     *
281
     * @param string $name The provider name.
282
     * @return DataTablesProviderInterface Returns the provider.
283
     * @throws Throwable Throws an exception if an error occurs.
284
     * @throws UnregisteredDataTablesProviderException Throws an unregistered provider exception.
285
     */
286
    protected function getDataTablesProvider(string $name): DataTablesProviderInterface {
287
288
        $dtManager = $this->getDataTablesManager();
289
290
        $context = [
291
            "_controller" => get_class($this),
292
            "_manager"    => get_class($dtManager),
0 ignored issues
show
Bug introduced by
It seems like $dtManager can also be of type null; however, parameter $object of get_class() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

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

292
            "_manager"    => get_class(/** @scrutinizer ignore-type */ $dtManager),
Loading history...
293
        ];
294
295
        $this->logInfo(sprintf('DataTables controller search for a provider with name "%s"', $name), $context);
296
297
        $dtProvider = $dtManager->getProvider($name);
298
299
        $context["_provider"] = get_class($dtProvider);
300
301
        $this->logInfo(sprintf('DataTables controller found a provider with name "%s"', $name), $context);
302
303
        return $dtProvider;
304
    }
305
306
    /**
307
     * Get a repository.
308
     *
309
     * @param DataTablesProviderInterface $dtProvider The provider.
310
     * @return DataTablesRepositoryInterface Returns the repository.
311
     * @throws BadDataTablesRepositoryException Throws a bad repository exception.
312
     * @throws Throwable Throws an exception if an error occurs.
313
     */
314
    protected function getDataTablesRepository(DataTablesProviderInterface $dtProvider): DataTablesRepositoryInterface {
315
316
        $em = $this->getEntityManager();
317
318
        $context = [
319
            "_controller" => get_class($this),
320
            "_provider"   => get_class($dtProvider),
321
            "_entity"     => $dtProvider->getEntity(),
322
        ];
323
324
        $this->logInfo(sprintf('DataTables controller search for a repository with name "%s"', $dtProvider->getName()), $context);
325
326
        $repository = $em->getRepository($dtProvider->getEntity());
327
        if (false === ($repository instanceof DataTablesRepositoryInterface)) {
328
            throw new BadDataTablesRepositoryException($repository);
329
        }
330
331
        $context["_repository"] = get_class($repository);
332
333
        $this->logInfo(sprintf('DataTables controller found a repository with name "%s"', $dtProvider->getName()), $context);
334
335
        /** @var DataTablesRepositoryInterface $repository */
336
        return $repository;
337
    }
338
339
    /**
340
     * Get a URL.
341
     *
342
     * @param DataTablesProviderInterface $dtProvider The provider.
343
     * @return string Returns the URL.
344
     * @throws Throwable Throws an exception if an error occurs.
345
     */
346
    protected function getDataTablesUrl(DataTablesProviderInterface $dtProvider): string {
347
348
        $context = [
349
            "_controller" => get_class($this),
350
            "_provider"   => get_class($dtProvider),
351
        ];
352
353
        $this->logInfo(sprintf('DataTables controller search for an URL with name "%s"', $dtProvider->getName()), $context);
354
355
        if (true === ($dtProvider instanceof DataTablesRouterInterface)) {
356
            $url = $dtProvider->getUrl();
357
        } else {
358
            $url = $this->getRouter()->generate("wbw_jquery_datatables_index", ["name" => $dtProvider->getName()]);
359
        }
360
361
        $context["_url"] = $url;
362
363
        $this->logInfo(sprintf('DataTables controller found for an URL with name "%s"', $dtProvider->getName()), $context);
364
365
        return $url;
366
    }
367
368
    /**
369
     * Get a wrapper.
370
     *
371
     * @param DataTablesProviderInterface $dtProvider The provider.
372
     * @return DataTablesWrapperInterface Returns the wrapper.
373
     * @throws Throwable Throws an exception if an error occurs.
374
     */
375
    protected function getDataTablesWrapper(DataTablesProviderInterface $dtProvider): DataTablesWrapperInterface {
376
377
        /** @var DataTablesWrapper $dtWrapper */
378
        $dtWrapper = DataTablesFactory::newWrapper($this->getDataTablesUrl($dtProvider), $dtProvider, $this->getKernelEventListener()->getUser());
379
380
        $context = [
381
            "_controller" => get_class($this),
382
            "_provider"   => get_class($dtProvider),
383
            "_wrapper"    => get_class($dtWrapper),
384
        ];
385
386
        foreach ($dtProvider->getColumns() as $dtColumn) {
387
388
            $this->logInfo(sprintf('DataTables controller add a column "%s" with the provider "%s"', $dtColumn->getData(), $dtProvider->getName()), $context);
389
390
            $dtWrapper->addColumn($dtColumn);
391
        }
392
393
        if (null !== $dtProvider->getOptions()) {
394
            $dtWrapper->setOptions($dtProvider->getOptions());
395
        }
396
397
        return $dtWrapper;
398
    }
399
400
    /**
401
     * Handle an exception.
402
     *
403
     * @param Throwable $ex The exception.
404
     * @param string $notificationBaseId The notification base id.
405
     * @return SimpleJsonResponseDataInterface Returns the action response.
406
     * @throws Throwable Throws an exception if an error occurs.
407
     */
408
    protected function handleDataTablesException(Throwable $ex, string $notificationBaseId): SimpleJsonResponseDataInterface {
409
410
        $this->logInfo($ex->getMessage());
411
412
        if (true === ($ex instanceof EntityNotFoundException)) {
413
            return $this->prepareActionResponse(404, $notificationBaseId . ".danger");
414
        }
415
416
        return $this->prepareActionResponse(500, $notificationBaseId . ".warning");
417
    }
418
419
    /**
420
     * Log an info.
421
     *
422
     * @param string $message The message.
423
     * @param mixed[] $context The context.
424
     * @return AbstractController Returns this controller.
425
     * @throws Throwable Throws an exception if an error occurs.
426
     */
427
    protected function logInfo(string $message, array $context = []): AbstractController {
428
        $this->getLogger()->info($message, $context);
429
        return $this;
430
    }
431
432
    /**
433
     * Prepare an action response.
434
     *
435
     * @param int $status The status.
436
     * @param string $notificationId The notification id.
437
     * @return SimpleJsonResponseDataInterface Returns the action response.
438
     * @throws Throwable Throws an exception if an error occurs.
439
     */
440
    protected function prepareActionResponse(int $status, string $notificationId): SimpleJsonResponseDataInterface {
441
442
        $notify = $this->getTranslator()->trans($notificationId, [], WBWJQueryDataTablesBundle::getTranslationDomain());
443
444
        $response = new SimpleJsonResponseData();
445
        $response->setStatus($status);
446
        $response->setNotify($notify);
447
448
        return $response;
449
    }
450
}
451