Passed
Pull Request — master (#168)
by
unknown
20:44
created

QueryBuilder   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 510
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 247
dl 0
loc 510
rs 8.4
c 2
b 0
f 0
wmc 50

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getSortScriptUniversityCollection() 0 10 1
A getSortScriptCreatorRole() 0 14 1
B buildQuery() 0 151 7
A getSettings() 0 6 1
A getSortScriptState() 0 15 3
A getSortScriptHasFiles() 0 8 1
A getSortScriptDoctype() 0 13 4
B buildSortQueryPart() 0 44 9
D buildFilterQueryPart() 0 166 23

How to fix   Complexity   

Complex Class

Complex classes like QueryBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use QueryBuilder, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace EWW\Dpf\Services\ElasticSearch;
3
4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under
8
 * the terms of the GNU General Public License, either version 2
9
 * of the License, or any later version.
10
 *
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
 * The TYPO3 project - inspiring people to share!
15
 */
16
17
use Elasticsearch\ClientBuilder;
18
use EWW\Dpf\Domain\Workflow\DocumentWorkflow;
19
use EWW\Dpf\Security\Security;
20
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
21
22
class QueryBuilder
23
{
24
    /**
25
     *
26
     * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
27
     * @inject
28
     */
29
    protected $configurationManager;
30
31
32
    /**
33
     * documentTypeRepository
34
     *
35
     * @var \EWW\Dpf\Domain\Repository\DocumentTypeRepository
36
     * @inject
37
     */
38
    protected $documentTypeRepository = null;
39
40
    /**
41
     * security
42
     *
43
     * @var \EWW\Dpf\Security\Security
44
     * @inject
45
     */
46
    protected $security = null;
47
48
49
    /**
50
     * Get typoscript settings
51
     *
52
     * @return mixed
53
     */
54
    public function getSettings()
55
    {
56
        $frameworkConfiguration = $this->configurationManager->getConfiguration(
57
            \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
58
        );
59
        return $frameworkConfiguration['settings'];
60
    }
61
62
63
    /**
64
     * Builds the document list query.
65
     *
66
     * @param int $itemsPerPage
67
     * @param array $workspaceFilter
68
     * @param int $from
69
     * @param array $bookmarkIdentifiers
70
     * @param array $filters
71
     * @param array $excludeFilters
72
     * @param string $sortField
73
     * @param string $sortOrder
74
     * @param string $queryString
75
     *
76
     * @return array
77
     */
78
    public function buildQuery(
79
        $itemsPerPage, $workspaceFilter, $from = 0, $bookmarkIdentifiers = [], $filters = [],
80
        $excludeFilters = [], $sortField = null, $sortOrder = null, $queryString = null
81
    )
82
    {
83
        // The base filter.
84
        $queryFilter = [
85
            'bool' => [
86
                'must' => [
87
                    [
88
                        'bool' => [
89
                            'should' => [
90
                                0 => $workspaceFilter
91
                            ]
92
                        ]
93
                    ]
94
                ]
95
            ]
96
        ];
97
98
        if (!($excludeFilters && array_key_exists('bookmarks', $excludeFilters))) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $excludeFilters of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
99
            // Add user document bookmarks.
100
101
            if ($bookmarkIdentifiers && is_array($bookmarkIdentifiers)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $bookmarkIdentifiers of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
102
                $queryFilter['bool']['must'][0]['bool']['should'][] = [
103
                    'terms' => [
104
                        '_id' => array_values(array_filter($bookmarkIdentifiers))
105
                    ]
106
                ];
107
            }
108
        } else {
109
            // Show only user document bookmarks.
110
            $queryFilter['bool']['must'][0] = [
111
                'terms' => [
112
                    '_id' => $bookmarkIdentifiers
113
                ]
114
            ];
115
        }
116
117
        $filterPart = $this->buildFilterQueryPart($filters, $excludeFilters);
118
119
        if ($filterPart) {
120
            $queryFilter['bool']['must'][] = $filterPart;
121
        }
122
123
        if (!is_null($queryString)) {
124
            $query = [
125
                'bool' => [
126
                    'must' => [
127
                        'query_string' => [
128
                            'query' => $queryString
129
                        ]
130
                    ],
131
                    'filter' => $queryFilter
132
                ]
133
            ];
134
        } else {
135
            $query = [
136
                'bool' => [
137
                    'must' => [
138
                        'match_all' => (object)[]
139
                    ],
140
                    'filter' => $queryFilter
141
                ]
142
            ];
143
144
        }
145
146
147
        // Put together the complete query.
148
        $fullQuery = [
149
            'body' => [
150
                'size' => $itemsPerPage,
151
                'from' => $from,
152
                'query' => $query,
153
                'sort' => $this->buildSortQueryPart($sortField, $sortOrder),
154
                'aggs' => [
155
                    'aliasState' => [
156
                        'terms' => [
157
                            'field' => 'aliasState'
158
                        ]
159
                    ],
160
                    'year' => [
161
                        'terms' => [
162
                            'field' => 'year'
163
                        ]
164
                    ],
165
                    'doctype' => [
166
                        'terms' => [
167
                            'field' => 'doctype'
168
                        ]
169
                    ],
170
                    'hasFiles' => [
171
                        'terms' => [
172
                            'field' => 'hasFiles'
173
                        ]
174
                    ],
175
                    'universityCollection' => [
176
                        'terms' => [
177
                            'script' => [
178
                                'lang' => 'painless',
179
                                'source' =>
180
                                    "for (int i = 0; i < doc['collections'].length; ++i) {".
181
                                    "    if(doc['collections'][i] =='".$this->getSettings()['universityCollection']."') {".
182
                                    "        return 'true';".
183
                                    "    }".
184
                                    "}".
185
                                    "return 'false';"
186
                            ]
187
                        ]
188
                    ],
189
                    'authorAndPublisher' => [
190
                        'terms' => [
191
                            'field' => 'authorAndPublisher'
192
                        ]
193
                    ],
194
                    'creatorRole' => [
195
                        'terms' => [
196
                            'script' => [
197
                                'lang' => 'painless',
198
                                'source' =>
199
                                    "if (".
200
                                    "    doc['creator'].size() > 0 &&".
201
                                    "    doc['creator'].value == '".$this->security->getUser()->getUid()."') {".
202
                                    "    return 'self';".
203
                                    "}".
204
                                    "if (".
205
                                    "    doc['creatorRole'].size() > 0 &&".
206
                                    "    doc['creatorRole'].value == '".Security::ROLE_LIBRARIAN."'".
207
                                    ") {".
208
                                    "    return 'librarian';".
209
                                    "}".
210
                                    "if (".
211
                                    "    doc['creatorRole'].size() > 0 &&".
212
                                    "    doc['creatorRole'].value == '".Security::ROLE_RESEARCHER."'".
213
                                    ") {".
214
                                    "    return 'user';".
215
                                    "}".
216
                                    "return 'unknown';"
217
                            ]
218
                        ]
219
                    ]
220
221
                ]
222
            ]
223
        ];
224
225
226
        //echo "<pre>"; print_r($fullQuery); echo "</pre>"; die();
227
228
        return $fullQuery;
229
230
    }
231
232
    /**
233
     * Composes the filter part based on the given filters.
234
     *
235
     * @param array $filters
236
     * @param array $excludeFilters
237
     * @return array
238
     */
239
    protected function buildFilterQueryPart($filters, $excludeFilters = []) {
240
241
        $queryFilter = [];
242
243
        // Build the column filter part.
244
        if ($filters && is_array($filters)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filters of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
245
246
            $validKeys = [
247
                'aliasState', 'authorAndPublisher', 'doctype', 'hasFiles', 'year', 'universityCollection', 'creatorRole'
248
            ];
249
250
            foreach ($filters as $key => $filterValues) {
251
                $queryFilterPart = [];
252
                if (in_array($key, $validKeys, true)) {
253
                    if ($key == 'universityCollection') {
254
                        if ($filterValues && is_array($filterValues)) {
255
                            if (in_array("true", $filterValues)) {
256
                                $filterValue = $this->getSettings()['universityCollection'];
257
                                $queryFilterPart['bool']['should'][] = [
258
                                    'term' => [
259
                                        'collections' => $filterValue
260
                                    ]
261
                                ];
262
                            } else {
263
                                $filterValue = $this->getSettings()['universityCollection'];
264
                                $queryFilterPart['bool']['should'][] = [
265
                                    'bool' => [
266
                                        'must_not' => [
267
                                            'term' => [
268
                                                'collections' => $filterValue
269
                                            ]
270
                                        ]
271
                                    ]
272
                                ];
273
                            }
274
                            $queryFilter['bool']['must'][] = $queryFilterPart;
275
                        }
276
                    } elseif ($key == 'creatorRole') {
277
                        $queryFilterPart = [];
278
                        if ($filterValues && is_array($filterValues)) {
279
                            if (in_array("librarian", $filterValues)) {
280
                                $creatorRolePart['bool']['must'] = [
281
                                    [
282
                                        'term' => [
283
                                            'creatorRole' => Security::ROLE_LIBRARIAN
284
                                        ]
285
                                    ],
286
                                    [
287
                                        'bool' => [
288
                                            'must_not' => [
289
                                                'term' => [
290
                                                    'creator' => $this->security->getUser()->getUid()
291
                                                ]
292
                                            ]
293
                                        ]
294
                                    ]
295
                                ];
296
                                $queryFilterPart['bool']['should'][] = $creatorRolePart;
297
                            } elseif (in_array("user", $filterValues)) {
298
                                $creatorRolePart['bool']['must'] = [
299
                                    [
300
                                        'term' => [
301
                                            'creatorRole' => Security::ROLE_RESEARCHER
302
                                        ]
303
                                    ],
304
                                    [
305
                                        'bool' => [
306
                                            'must_not' => [
307
                                                'term' => [
308
                                                    'creator' => $this->security->getUser()->getUid()
309
                                                ]
310
                                            ]
311
                                        ]
312
                                    ]
313
                                ];
314
                                $queryFilterPart['bool']['should'][] = $creatorRolePart;
315
                            } elseif (in_array("self", $filterValues)) {
316
                                $creatorRolePart['bool']['must'] = [
317
                                    [
318
                                        'term' => [
319
                                            'creator' =>  $this->security->getUser()->getUid()
320
                                        ]
321
                                    ]
322
                                ];
323
                                $queryFilterPart['bool']['should'][] = $creatorRolePart;
324
                            } else {
325
                                $creatorRolePart['bool']['must'] = [
326
                                    [
327
                                        'bool' => [
328
                                            'must_not' => [
329
                                                'term' => [
330
                                                    'creator' => $this->security->getUser()->getUid()
331
                                                ]
332
                                            ]
333
                                        ]
334
                                    ],
335
                                    [
336
                                        'bool' => [
337
                                            'must_not' => [
338
                                                'term' => [
339
                                                    'creatorRole' => Security::ROLE_LIBRARIAN
340
                                                ]
341
                                            ]
342
                                        ]
343
                                    ],
344
                                    [
345
                                        'bool' => [
346
                                            'must_not' => [
347
                                                'term' => [
348
                                                    'creatorRole' => Security::ROLE_RESEARCHER
349
                                                ]
350
                                            ]
351
                                        ]
352
                                    ]
353
                                ];
354
                                $queryFilterPart['bool']['should'][] = $creatorRolePart;
355
                            }
356
357
                            if ($queryFilterPart) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $queryFilterPart of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
358
                                $queryFilter['bool']['must'][] = $queryFilterPart;
359
                            }
360
                        }
361
                    } else {
362
                        if ($filterValues && is_array($filterValues)) {
363
                            foreach ($filterValues as $filterValue) {
364
                                $queryFilterPart['bool']['should'][] = [
365
                                    'term' => [
366
                                        $key => $filterValue
367
                                    ]
368
                                ];
369
                            }
370
                            $queryFilter['bool']['must'][] = $queryFilterPart;
371
                        }
372
                    }
373
                }
374
            }
375
        }
376
377
        if ($excludeFilters && array_key_exists('aliasState', $excludeFilters)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $excludeFilters of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
378
            if ($excludeFilters['aliasState']) {
379
                foreach ($excludeFilters['aliasState'] as $aliasStateExclude) {
380
                    $queryFilter['bool']['must'][] = [
381
                        'bool' => [
382
                            'must_not' => [
383
                                'bool' => [
384
                                    'must' => [
385
                                        [
386
                                            'term' => [
387
                                                'aliasState' => $aliasStateExclude
388
                                            ]
389
                                        ],
390
                                        [
391
                                            'term' => [
392
                                                'creator' => $this->security->getUser()->getUid()
393
                                            ]
394
                                        ]
395
                                    ]
396
                                ]
397
                            ]
398
                        ]
399
                    ];
400
                }
401
            }
402
        }
403
404
        return $queryFilter;
405
    }
406
407
408
    /**
409
     * Composes the sort query part based on the given sort field and order.
410
     *
411
     * @param string $sortField
412
     * @param string $sortOrder
413
     * @return array
414
     */
415
    protected function buildSortQueryPart($sortField, $sortOrder) {
416
417
        $sortField = ($sortField)? $sortField : 'title';
418
        $sortOrder = ($sortOrder)? $sortOrder : 'asc';
419
420
        // Build the sorting part.
421
        $script = "";
422
        if ($sortField == "aliasState") {
423
            $script = $this->getSortScriptState();
424
        } elseif ($sortField == "universityCollection") {
425
            $script = $this->getSortScriptUniversityCollection($this->getSettings()['universityCollection']);
426
        } elseif ($sortField == "hasFiles") {
427
            $script = $this->getSortScriptHasFiles();
428
        } elseif ($sortField == "creatorRole") {
429
            $script = $this->getSortScriptCreatorRole($this->security->getUser()->getUid());
430
        }
431
432
        if ($script) {
433
            $sort = [
434
                "_script" => [
435
                    "type" => "string",
436
                    "order" => $sortOrder,
437
                    "script" => [
438
                        "lang" => "painless",
439
                        "source" => $script
440
                    ]
441
                ],
442
                "title.keyword" => [
443
                    "order" => "asc"
444
                ]
445
            ];
446
        } else {
447
            if ($sortField == 'title') {
448
                $sortField.= ".keyword";
449
            }
450
451
            $sort = [
452
                $sortField => [
453
                    'order' => $sortOrder
454
                ]
455
            ];
456
        }
457
458
        return $sort;
459
    }
460
461
462
    protected function getSortScriptUniversityCollection($collection)
463
    {
464
        $script  = "for (int i = 0; i < doc['collections'].length; ++i) {";
465
        $script .= "    if (doc['collections'][i] == '".$collection."') {";
466
        $script .= "        return '1';";
467
        $script .= "    }";
468
        $script .= "}";
469
        $script .= "return '2'";
470
471
        return $script;
472
    }
473
474
    protected function getSortScriptHasFiles()
475
    {
476
        $script = "if (doc['hasFiles'].value == 'true') {";
477
        $script .= "    return '1';";
478
        $script .= "}";
479
        $script .= "return '2'";
480
481
        return $script;
482
    }
483
484
    protected function getSortScriptCreatorRole($feUserUid)
485
    {
486
        $script = "if (doc['creator'].value == '".$feUserUid."') {";
487
        $script .= "    return '1';";
488
        $script .= "}";
489
        $script .= "if (doc['creatorRole'].value == '".Security::ROLE_LIBRARIAN."') {";
490
        $script .= "return '2';";
491
        $script .= "}";
492
        $script .= "if (doc['creatorRole'].value == '".Security::ROLE_RESEARCHER."') {";
493
        $script .= "    return '3';";
494
        $script .= "}";
495
        $script .= "return '4';";
496
497
        return $script;
498
    }
499
500
501
    protected function getSortScriptState()
502
    {
503
        $sortStates = [];
504
        foreach (DocumentWorkflow::PLACES as $state) {
505
            if (array_key_exists($state, DocumentWorkflow::STATE_TO_ALIASSTATE_MAPPING)) {
506
                $aliasState = DocumentWorkflow::STATE_TO_ALIASSTATE_MAPPING[$state];
507
                $key = 'LLL:EXT:dpf/Resources/Private/Language/locallang.xlf:manager.documentList.state.'.$aliasState;
508
                $stateName = LocalizationUtility::translate($key, 'dpf');
509
                $sortStates[] = "if (doc['state'].value == '".$state."') return '".$stateName."';";
510
            }
511
        }
512
513
        $sortStates = implode(" ", $sortStates);
514
515
        return $sortStates." return '';";
516
    }
517
518
519
    protected function getSortScriptDoctype()
520
    {
521
        $sortDoctypes = [];
522
        foreach ($this->documentTypeRepository->findAll() as $documentType) {
523
            if ($documentType->getName() && $documentType->getDisplayname()) {
524
                $sortDoctypes[] = "if (doc['doctype'].value == '".$documentType->getName()."')"
525
                    ." return '".$documentType->getDisplayname()."';";
526
            }
527
        }
528
529
        $sortDoctypes = implode(" ", $sortDoctypes);
530
531
        return $sortDoctypes." return '';";
532
    }
533
534
}
535