Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — dev-extbase-fluid (#754)
by Alexander
02:55
created

DocumentRepository::fetchMetadataFromSolr()   B

Complexity

Conditions 8
Paths 6

Size

Total Lines 38
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 8
eloc 20
c 3
b 0
f 0
nc 6
nop 2
dl 0
loc 38
rs 8.4444
1
<?php
2
3
/**
4
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
5
 *
6
 * This file is part of the Kitodo and TYPO3 projects.
7
 *
8
 * @license GNU General Public License version 3 or later.
9
 * For the full copyright and license information, please read the
10
 * LICENSE.txt file that was distributed with this source code.
11
 */
12
13
namespace Kitodo\Dlf\Domain\Repository;
14
15
use Kitodo\Dlf\Common\Doc;
16
use Kitodo\Dlf\Common\Helper;
17
use Kitodo\Dlf\Common\Solr;
18
use Kitodo\Dlf\Domain\Model\Document;
19
use Kitodo\Dlf\Common\SolrSearchResult\ResultDocument;
20
use TYPO3\CMS\Core\Cache\CacheManager;
21
use TYPO3\CMS\Core\Database\ConnectionPool;
22
use TYPO3\CMS\Core\Database\Connection;
23
use TYPO3\CMS\Core\Utility\GeneralUtility;
24
use TYPO3\CMS\Core\Utility\MathUtility;
25
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
26
27
class DocumentRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
28
{
29
    /**
30
     * The controller settings passed to the repository for some special actions.
31
     *
32
     * @var array
33
     * @access protected
34
     */
35
    protected $settings;
36
37
    /**
38
     * Find one document by given parameters
39
     *
40
     * GET parameters may be:
41
     *
42
     * - 'id': the uid of the document
43
     * - 'location': the URL of the location of the XML file
44
     * - 'recordId': the record_id of the document
45
     *
46
     * @param array $parameters
47
     *
48
     * @return \Kitodo\Dlf\Domain\Model\Document|null
49
     */
50
    public function findOneByParameters($parameters)
51
    {
52
        $doc = null;
53
        $document = null;
54
55
        if (isset($parameters['id']) && MathUtility::canBeInterpretedAsInteger($parameters['id'])) {
56
57
            $document = $this->findOneByIdAndSettings($parameters['id']);
58
59
        } else if (isset($parameters['recordId'])) {
60
61
            $document = $this->findOneByRecordId($parameters['recordId']);
0 ignored issues
show
Bug introduced by
The method findOneByRecordId() does not exist on Kitodo\Dlf\Domain\Repository\DocumentRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

61
            /** @scrutinizer ignore-call */ 
62
            $document = $this->findOneByRecordId($parameters['recordId']);
Loading history...
62
63
        } else if (isset($parameters['location']) && GeneralUtility::isValidUrl($parameters['location'])) {
64
65
            $doc = Doc::getInstance($parameters['location'], [], true);
66
67
            if ($doc->recordId) {
68
                $document = $this->findOneByRecordId($doc->recordId);
69
            }
70
71
            if ($document === null) {
72
                // create new (dummy) Document object
73
                $document = GeneralUtility::makeInstance(Document::class);
74
                $document->setLocation($parameters['location']);
75
            }
76
77
        }
78
79
        if ($document !== null && $doc === null) {
80
            $doc = Doc::getInstance($document->getLocation(), [], true);
0 ignored issues
show
Bug introduced by
The method getLocation() does not exist on TYPO3\CMS\Extbase\Persistence\QueryResultInterface. ( Ignorable by Annotation )

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

80
            $doc = Doc::getInstance($document->/** @scrutinizer ignore-call */ getLocation(), [], true);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
81
        }
82
83
        if ($doc !== null) {
84
            $document->setDoc($doc);
0 ignored issues
show
Bug introduced by
The method setDoc() does not exist on TYPO3\CMS\Extbase\Persistence\QueryResultInterface. ( Ignorable by Annotation )

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

84
            $document->/** @scrutinizer ignore-call */ 
85
                       setDoc($doc);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
85
        }
86
87
        return $document;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $document also could return the type TYPO3\CMS\Extbase\Persis...y<mixed,object>|integer which is incompatible with the documented return type Kitodo\Dlf\Domain\Model\Document|null.
Loading history...
88
89
    }
90
91
92
    public function findByUidAndPartOf($uid, $partOf)
93
    {
94
        $query = $this->createQuery();
95
96
        $query->matching($query->equals('uid', $uid));
97
        $query->matching($query->equals('partof', $partOf));
98
99
        return $query->execute();
100
    }
101
102
    /**
103
     * Find the oldest document
104
     *
105
     * @return \Kitodo\Dlf\Domain\Model\Document|null
106
     */
107
    public function findOldestDocument()
108
    {
109
        $query = $this->createQuery();
110
111
        $query->setOrderings(['tstamp' => QueryInterface::ORDER_ASCENDING]);
112
        $query->setLimit(1);
113
114
        return $query->execute()->getFirst();
115
    }
116
117
    /**
118
     * @param int $partOf
119
     * @param string $structure
120
     * @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
121
     */
122
    public function getChildrenOfYearAnchor($partOf, $structure)
123
    {
124
        $query = $this->createQuery();
125
126
        $query->matching($query->equals('structure', Helper::getUidFromIndexName($structure, 'tx_dlf_structures')));
127
        $query->matching($query->equals('partof', $partOf));
128
129
        $query->setOrderings([
130
            'mets_orderlabel' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING
131
        ]);
132
133
        return $query->execute();
134
    }
135
136
    /**
137
     * Finds all documents for the given settings
138
     *
139
     * @param int $uid
140
     * @param array $settings
141
     *
142
     * @return \Kitodo\Dlf\Domain\Model\Document|null
143
     */
144
    public function findOneByIdAndSettings($uid, $settings = [])
0 ignored issues
show
Unused Code introduced by
The parameter $settings is not used and could be removed. ( Ignorable by Annotation )

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

144
    public function findOneByIdAndSettings($uid, /** @scrutinizer ignore-unused */ $settings = [])

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
145
    {
146
        $settings = ['documentSets' => $uid];
147
148
        return $this->findDocumentsBySettings($settings)->getFirst();
149
    }
150
151
    /**
152
     * Finds all documents for the given settings
153
     *
154
     * @param array $settings
155
     *
156
     * @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
157
     */
158
    public function findDocumentsBySettings($settings = [])
159
    {
160
        $query = $this->createQuery();
161
162
        $constraints = [];
163
164
        if ($settings['documentSets']) {
165
            $constraints[] = $query->in('uid', GeneralUtility::intExplode(',', $settings['documentSets']));
166
        }
167
168
        if (isset($settings['excludeOther']) && (int) $settings['excludeOther'] === 0) {
169
            $query->getQuerySettings()->setRespectStoragePage(false);
170
        }
171
172
        if (count($constraints)) {
173
            $query->matching(
174
                $query->logicalAnd($constraints)
175
            );
176
        }
177
178
        return $query->execute();
179
    }
180
181
    /**
182
     * Finds all documents for the given collections
183
     *
184
     * @param array $collections
185
     * @param int $limit
186
     *
187
     * @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
188
     */
189
    public function findAllByCollectionsLimited($collections, $limit = 50)
190
    {
191
        $query = $this->createQuery();
192
193
        // order by start_date -> start_time...
194
        $query->setOrderings(
195
            ['tstamp' => QueryInterface::ORDER_DESCENDING]
196
        );
197
198
        $constraints = [];
199
        if ($collections) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $collections 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...
200
            $constraints[] = $query->in('collections.uid', $collections);
201
        }
202
203
        if (count($constraints)) {
204
            $query->matching(
205
                $query->logicalAnd($constraints)
206
            );
207
        }
208
209
        if ($limit > 0) {
210
            $query->setLimit((int) $limit);
211
        }
212
213
        return $query->execute();
214
    }
215
216
    /**
217
     * Find all the titles
218
     *
219
     * documents with partof == 0
220
     *
221
     * @param array $settings
222
     *
223
     * @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
224
     */
225
    public function findAllTitles($settings = [])
226
    {
227
        $query = $this->createQuery();
228
229
        $constraints = [];
230
        $constraints[] = $query->equals('partof', 0);
231
232
        if ($settings['collections']) {
233
            $constraints[] = $query->in('collections.uid', GeneralUtility::intExplode(',', $settings['collections']));
234
        }
235
236
        if (count($constraints)) {
237
            $query->matching(
238
                $query->logicalAnd($constraints)
239
            );
240
        }
241
242
        return $query->execute();
243
    }
244
245
    /**
246
     * Count the titles
247
     *
248
     * documents with partof == 0
249
     *
250
     * @param array $settings
251
     *
252
     * @return int
253
     */
254
    public function countAllTitles($settings = [])
255
    {
256
        return $this->findAllTitles($settings)->count();
257
    }
258
259
    /**
260
     * Count the volumes
261
     *
262
     * documents with partof != 0
263
     *
264
     * @param array $settings
265
     *
266
     * @return int
267
     */
268
    public function countAllVolumes($settings = [])
269
    {
270
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
271
            ->getQueryBuilderForTable('tx_dlf_documents');
272
        $subQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
273
            ->getQueryBuilderForTable('tx_dlf_documents');
274
275
        $subQuery = $subQueryBuilder
276
            ->select('tx_dlf_documents.partof')
277
            ->from('tx_dlf_documents')
278
            ->where(
279
                $subQueryBuilder->expr()->neq('tx_dlf_documents.partof', 0)
280
            )
281
            ->groupBy('tx_dlf_documents.partof')
282
            ->getSQL();
283
284
        $countVolumes = $queryBuilder
285
            ->count('tx_dlf_documents.uid')
286
            ->from('tx_dlf_documents')
287
            ->where(
288
                $queryBuilder->expr()->eq('tx_dlf_documents.pid', intval($settings['pages'])),
289
                $queryBuilder->expr()->notIn('tx_dlf_documents.uid', $subQuery)
290
            )
291
            ->execute()
292
            ->fetchColumn(0);
293
294
        return $countVolumes;
295
    }
296
297
    public function getStatisticsForSelectedCollection($settings)
298
    {
299
        // Include only selected collections.
300
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
301
            ->getQueryBuilderForTable('tx_dlf_documents');
302
303
        $countTitles = $queryBuilder
304
            ->count('tx_dlf_documents.uid')
305
            ->from('tx_dlf_documents')
306
            ->innerJoin(
307
                'tx_dlf_documents',
308
                'tx_dlf_relations',
309
                'tx_dlf_relations_joins',
310
                $queryBuilder->expr()->eq(
311
                    'tx_dlf_relations_joins.uid_local',
312
                    'tx_dlf_documents.uid'
313
                )
314
            )
315
            ->innerJoin(
316
                'tx_dlf_relations_joins',
317
                'tx_dlf_collections',
318
                'tx_dlf_collections_join',
319
                $queryBuilder->expr()->eq(
320
                    'tx_dlf_relations_joins.uid_foreign',
321
                    'tx_dlf_collections_join.uid'
322
                )
323
            )
324
            ->where(
325
                $queryBuilder->expr()->eq('tx_dlf_documents.pid', intval($settings['pages'])),
326
                $queryBuilder->expr()->eq('tx_dlf_collections_join.pid', intval($settings['pages'])),
327
                $queryBuilder->expr()->eq('tx_dlf_documents.partof', 0),
328
                $queryBuilder->expr()->in('tx_dlf_collections_join.uid',
329
                    $queryBuilder->createNamedParameter(GeneralUtility::intExplode(',',
330
                        $settings['collections']), Connection::PARAM_INT_ARRAY)),
331
                $queryBuilder->expr()->eq('tx_dlf_relations_joins.ident',
332
                    $queryBuilder->createNamedParameter('docs_colls'))
333
            )
334
            ->execute()
335
            ->fetchColumn(0);
336
337
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
338
            ->getQueryBuilderForTable('tx_dlf_documents');
339
        $subQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
340
            ->getQueryBuilderForTable('tx_dlf_documents');
341
342
        $subQuery = $subQueryBuilder
343
            ->select('tx_dlf_documents.partof')
344
            ->from('tx_dlf_documents')
345
            ->where(
346
                $subQueryBuilder->expr()->neq('tx_dlf_documents.partof', 0)
347
            )
348
            ->groupBy('tx_dlf_documents.partof')
349
            ->getSQL();
350
351
        $countVolumes = $queryBuilder
352
            ->count('tx_dlf_documents.uid')
353
            ->from('tx_dlf_documents')
354
            ->innerJoin(
355
                'tx_dlf_documents',
356
                'tx_dlf_relations',
357
                'tx_dlf_relations_joins',
358
                $queryBuilder->expr()->eq(
359
                    'tx_dlf_relations_joins.uid_local',
360
                    'tx_dlf_documents.uid'
361
                )
362
            )
363
            ->innerJoin(
364
                'tx_dlf_relations_joins',
365
                'tx_dlf_collections',
366
                'tx_dlf_collections_join',
367
                $queryBuilder->expr()->eq(
368
                    'tx_dlf_relations_joins.uid_foreign',
369
                    'tx_dlf_collections_join.uid'
370
                )
371
            )
372
            ->where(
373
                $queryBuilder->expr()->eq('tx_dlf_documents.pid', intval($settings['pages'])),
374
                $queryBuilder->expr()->eq('tx_dlf_collections_join.pid', intval($settings['pages'])),
375
                $queryBuilder->expr()->notIn('tx_dlf_documents.uid', $subQuery),
376
                $queryBuilder->expr()->in('tx_dlf_collections_join.uid',
377
                    $queryBuilder->createNamedParameter(GeneralUtility::intExplode(',',
378
                        $settings['collections']), Connection::PARAM_INT_ARRAY)),
379
                $queryBuilder->expr()->eq('tx_dlf_relations_joins.ident',
380
                    $queryBuilder->createNamedParameter('docs_colls'))
381
            )
382
            ->execute()
383
            ->fetchColumn(0);
384
385
        return ['titles' => $countTitles, 'volumes' => $countVolumes];
386
    }
387
388
    public function getTableOfContentsFromDb($uid, $pid, $settings)
389
    {
390
        // Build table of contents from database.
391
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
392
            ->getQueryBuilderForTable('tx_dlf_documents');
393
394
        $excludeOtherWhere = '';
395
        if ($settings['excludeOther']) {
396
            $excludeOtherWhere = 'tx_dlf_documents.pid=' . intval($settings['pages']);
397
        }
398
        // Check if there are any metadata to suggest.
399
        $result = $queryBuilder
400
            ->select(
401
                'tx_dlf_documents.uid AS uid',
402
                'tx_dlf_documents.title AS title',
403
                'tx_dlf_documents.volume AS volume',
404
                'tx_dlf_documents.mets_label AS mets_label',
405
                'tx_dlf_documents.mets_orderlabel AS mets_orderlabel',
406
                'tx_dlf_structures_join.index_name AS type'
407
            )
408
            ->innerJoin(
409
                'tx_dlf_documents',
410
                'tx_dlf_structures',
411
                'tx_dlf_structures_join',
412
                $queryBuilder->expr()->eq(
413
                    'tx_dlf_structures_join.uid',
414
                    'tx_dlf_documents.structure'
415
                )
416
            )
417
            ->from('tx_dlf_documents')
418
            ->where(
419
                $queryBuilder->expr()->eq('tx_dlf_documents.partof', intval($uid)),
420
                $queryBuilder->expr()->eq('tx_dlf_structures_join.pid', intval($pid)),
421
                $excludeOtherWhere
422
            )
423
            ->addOrderBy('tx_dlf_documents.volume_sorting')
424
            ->addOrderBy('tx_dlf_documents.mets_orderlabel')
425
            ->execute();
426
        return $result;
427
    }
428
429
    /**
430
     * Find one document by given settings and identifier
431
     *
432
     * @param array $settings
433
     * @param array $parameters
434
     *
435
     * @return array The found document object
436
     */
437
    public function getOaiRecord($settings, $parameters)
438
    {
439
        $where = '';
440
441
        if (!$settings['show_userdefined']) {
442
            $where .= 'AND tx_dlf_collections.fe_cruser_id=0 ';
443
        }
444
445
        $connection = GeneralUtility::makeInstance(ConnectionPool::class)
446
            ->getConnectionForTable('tx_dlf_documents');
447
448
        $sql = 'SELECT `tx_dlf_documents`.*, GROUP_CONCAT(DISTINCT `tx_dlf_collections`.`oai_name` ORDER BY `tx_dlf_collections`.`oai_name` SEPARATOR " ") AS `collections` ' .
449
            'FROM `tx_dlf_documents` ' .
450
            'INNER JOIN `tx_dlf_relations` ON `tx_dlf_relations`.`uid_local` = `tx_dlf_documents`.`uid` ' .
451
            'INNER JOIN `tx_dlf_collections` ON `tx_dlf_collections`.`uid` = `tx_dlf_relations`.`uid_foreign` ' .
452
            'WHERE `tx_dlf_documents`.`record_id` = ? ' .
453
            'AND `tx_dlf_relations`.`ident`="docs_colls" ' .
454
            $where;
455
456
        $values = [
457
            $parameters['identifier']
458
        ];
459
460
        $types = [
461
            Connection::PARAM_STR
462
        ];
463
464
        // Create a prepared statement for the passed SQL query, bind the given params with their binding types and execute the query
465
        $statement = $connection->executeQuery($sql, $values, $types);
466
467
        return $statement->fetch();
468
    }
469
470
    /**
471
     * Finds all documents for the given settings
472
     *
473
     * @param array $settings
474
     * @param array $documentsToProcess
475
     *
476
     * @return array The found document objects
477
     */
478
    public function getOaiDocumentList($settings, $documentsToProcess)
0 ignored issues
show
Unused Code introduced by
The parameter $settings is not used and could be removed. ( Ignorable by Annotation )

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

478
    public function getOaiDocumentList(/** @scrutinizer ignore-unused */ $settings, $documentsToProcess)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
479
    {
480
        $connection = GeneralUtility::makeInstance(ConnectionPool::class)
481
            ->getConnectionForTable('tx_dlf_documents');
482
483
        $sql = 'SELECT `tx_dlf_documents`.*, GROUP_CONCAT(DISTINCT `tx_dlf_collections`.`oai_name` ORDER BY `tx_dlf_collections`.`oai_name` SEPARATOR " ") AS `collections` ' .
484
            'FROM `tx_dlf_documents` ' .
485
            'INNER JOIN `tx_dlf_relations` ON `tx_dlf_relations`.`uid_local` = `tx_dlf_documents`.`uid` ' .
486
            'INNER JOIN `tx_dlf_collections` ON `tx_dlf_collections`.`uid` = `tx_dlf_relations`.`uid_foreign` ' .
487
            'WHERE `tx_dlf_documents`.`uid` IN ( ? ) ' .
488
            'AND `tx_dlf_relations`.`ident`="docs_colls" ' .
489
            'AND ' . Helper::whereExpression('tx_dlf_collections') . ' ' .
490
            'GROUP BY `tx_dlf_documents`.`uid` ';
491
492
        $values = [
493
            $documentsToProcess,
494
        ];
495
496
        $types = [
497
            Connection::PARAM_INT_ARRAY,
498
        ];
499
500
        // Create a prepared statement for the passed SQL query, bind the given params with their binding types and execute the query
501
        $documents = $connection->executeQuery($sql, $values, $types);
502
503
        return $documents;
504
    }
505
506
    /**
507
     * Finds all documents with given uids
508
     *
509
     * @param array $uids
510
     *
511
     * @return array
512
     */
513
    private function findAllByUids($uids)
514
    {
515
        // get all documents from db we are talking about
516
        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
517
        $queryBuilder = $connectionPool->getQueryBuilderForTable('tx_dlf_documents');
518
        // Fetch document info for UIDs in $documentSet from DB
519
        $kitodoDocuments = $queryBuilder
520
            ->select(
521
                'tx_dlf_documents.uid AS uid',
522
                'tx_dlf_documents.title AS title',
523
                'tx_dlf_documents.structure AS structure',
524
                'tx_dlf_documents.thumbnail AS thumbnail',
525
                'tx_dlf_documents.volume_sorting AS volumeSorting',
526
                'tx_dlf_documents.mets_orderlabel AS metsOrderlabel',
527
                'tx_dlf_documents.partof AS partOf'
528
            )
529
            ->from('tx_dlf_documents')
530
            ->where(
531
                $queryBuilder->expr()->in('tx_dlf_documents.pid', $this->settings['storagePid']),
532
                $queryBuilder->expr()->in('tx_dlf_documents.uid', $uids)
533
            )
534
            ->addOrderBy('tx_dlf_documents.volume_sorting', 'asc')
535
            ->addOrderBy('tx_dlf_documents.mets_orderlabel', 'asc')
536
            ->execute();
537
538
        $allDocuments = [];
539
        $documentStructures = Helper::getDocumentStructures($this->settings['storagePid']);
540
        // Process documents in a usable array structure
541
        while ($resArray = $kitodoDocuments->fetch()) {
542
            $resArray['structure'] = $documentStructures[$resArray['structure']];
543
            $allDocuments[$resArray['uid']] = $resArray;
544
        }
545
546
        return $allDocuments;
547
    }
548
549
    /**
550
     * Find all documents with given collection from Solr
551
     *
552
     * @param \Kitodo\Dlf\Domain\Model\Collection $collection
553
     * @param array $settings
554
     * @param array $searchParams
555
     * @param \TYPO3\CMS\Extbase\Persistence\Generic\QueryResult $listedMetadata
556
     * @return array
557
     */
558
    public function findSolrByCollection($collection, $settings, $searchParams, $listedMetadata = null)
559
    {
560
        // set settings global inside this repository
561
        $this->settings = $settings;
562
563
        // Prepare query parameters.
564
        $params = [];
565
        $matches = [];
566
        $fields = Solr::getFields();
567
568
        // Set search query.
569
        if (
570
            (!empty($searchParams['fulltext']))
571
            || preg_match('/' . $fields['fulltext'] . ':\((.*)\)/', trim($searchParams['query']), $matches)
572
        ) {
573
            // If the query already is a fulltext query e.g using the facets
574
            $searchParams['query'] = empty($matches[1]) ? $searchParams['query'] : $matches[1];
575
            // Search in fulltext field if applicable. Query must not be empty!
576
            if (!empty($searchParams['query'])) {
577
                $query = $fields['fulltext'] . ':(' . Solr::escapeQuery(trim($searchParams['query'])) . ')';
578
            }
579
            $params['fulltext'] = true;
580
        } else {
581
            // Retain given search field if valid.
582
            if (!empty($searchParams['query'])) {
583
                $query = Solr::escapeQueryKeepField(trim($searchParams['query']), $settings['storagePid']);
584
            }
585
        }
586
587
        // Add filter query for faceting.
588
        if (isset($searchParams['fq']) && is_array($searchParams['fq'])) {
589
            foreach ($searchParams['fq'] as $filterQuery) {
590
                $params['filterquery'][]['query'] = $filterQuery;
591
            }
592
        }
593
594
        // Add filter query for in-document searching.
595
        if (
596
            $this->settings['searchIn'] == 'document'
597
            || $this->settings['searchIn'] == 'all'
598
        ) {
599
            if (
600
                !empty($searchParams['documentId'])
601
                && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($searchParams['documentId'])
602
            ) {
603
                // Search in document and all subordinates (valid for up to three levels of hierarchy).
604
                $params['filterquery'][]['query'] = '_query_:"{!join from='
605
                    . $fields['uid'] . ' to=' . $fields['partof'] . '}'
606
                    . $fields['uid'] . ':{!join from=' . $fields['uid'] . ' to=' . $fields['partof'] . '}'
607
                    . $fields['uid'] . ':' . $searchParams['documentId'] . '"' . ' OR {!join from='
608
                    . $fields['uid'] . ' to=' . $fields['partof'] . '}'
609
                    . $fields['uid'] . ':' . $searchParams['documentId'] . ' OR '
610
                    . $fields['uid'] . ':' . $searchParams['documentId'];
611
//                $label .= htmlspecialchars(sprintf($this->pi_getLL('in', ''), Document::getTitle($this->piVars['id'])));
612
            }
613
        }
614
615
        // if a collection is given, we prepare the collection query string
616
        if ($collection) {
0 ignored issues
show
introduced by
$collection is of type Kitodo\Dlf\Domain\Model\Collection, thus it always evaluated to true.
Loading history...
617
            $collecionsQueryString = $collection->getIndexName();
618
            $params['filterquery'][]['query'] = 'toplevel:true';
619
            $params['filterquery'][]['query'] = 'partof:0';
620
            $params['filterquery'][]['query'] = 'collection_faceting:("' . $collecionsQueryString . '")';
621
        }
622
623
        // Set some query parameters.
624
        $params['query'] = !empty($query) ? $query : '*';
625
        $params['start'] = 0;
626
        $params['rows'] = 10000;
627
628
        // order the results as given or by title as default
629
        if (!empty($searchParams['orderBy'])) {
630
            $querySort = [
631
                $searchParams['orderBy'] => $searchParams['order']
632
            ];
633
        } else {
634
            $querySort = [
635
                'year_sorting' => 'asc',
636
                'title_sorting' => 'asc'
637
            ];
638
        }
639
640
        $params['sort'] = $querySort;
641
        $params['listMetadataRecords'] = [];
642
643
        // Restrict the fields to the required ones.
644
        $params['fields'] = 'uid,id,page,title,thumbnail,partof,toplevel,type';
645
646
        if ($listedMetadata) {
647
            foreach ($listedMetadata as $metadata) {
648
                if ($metadata->getIndexStored() || $metadata->getIndexIndexed()) {
649
                    $listMetadataRecord = $metadata->getIndexName() . '_' . ($metadata->getIndexTokenized() ? 't' : 'u') . ($metadata->getIndexStored() ? 's' : 'u') . ($metadata->getIndexIndexed() ? 'i' : 'u');
650
                    $params['fields'] .= ',' . $listMetadataRecord;
651
                    $params['listMetadataRecords'][$metadata->getIndexName()] = $listMetadataRecord;
652
                }
653
            }
654
        }
655
656
        // Perform search.
657
        $result = $this->searchSolr($params, true);
658
659
        // Initialize values
660
        $numberOfToplevels = 0;
661
        $documents = [];
662
663
        if ($result['numFound'] > 0) {
664
            // flat array with uids from Solr search
665
            $documentSet = array_unique(array_column($result['documents'], 'uid'));
666
667
            if (empty($documentSet)) {
668
                // return nothing found
669
                return ['solrResults' => [], 'documents' => []];
670
            }
671
672
            // get the Extbase document objects for all uids
673
            $allDocuments = $this->findAllByUids($documentSet);
674
675
            foreach ($result['documents'] as $doc) {
676
                if (empty($documents[$doc['uid']]) && $allDocuments[$doc['uid']]) {
677
                    $documents[$doc['uid']] = $allDocuments[$doc['uid']];
678
                }
679
                if ($documents[$doc['uid']]) {
680
                    if ($doc['toplevel'] === false) {
681
                        // this maybe a chapter, article, ..., year
682
                        if ($doc['type'] === 'year') {
683
                            continue;
684
                        }
685
                        if (!empty($doc['page'])) {
686
                            // it's probably a fulltext or metadata search
687
                            $searchResult = [];
688
                            $searchResult['page'] = $doc['page'];
689
                            $searchResult['thumbnail'] = $doc['thumbnail'];
690
                            $searchResult['structure'] = $doc['type'];
691
                            $searchResult['title'] = $doc['title'];
692
                            foreach ($params['listMetadataRecords'] as $indexName => $solrField) {
693
                                if (isset($doc['metadata'][$indexName])) {
694
                                    $documents[$doc['uid']]['metadata'][$indexName] = $doc['metadata'][$indexName];
695
                                }
696
                            }
697
                            if ($searchParams['fulltext'] == '1') {
698
                                $searchResult['snippet'] = $doc['snippet'];
699
                                $searchResult['highlight'] = $doc['highlight'];
700
                                $searchResult['highlight_word'] = $searchParams['query'];
701
                            }
702
                            $documents[$doc['uid']]['searchResults'][] = $searchResult;
703
                        }
704
                    } else if ($doc['toplevel'] === true) {
705
                        $numberOfToplevels++;
706
                        foreach ($params['listMetadataRecords'] as $indexName => $solrField) {
707
                            if (isset($doc['metadata'][$indexName])) {
708
                                $documents[$doc['uid']]['metadata'][$indexName] = $doc['metadata'][$indexName];
709
                            }
710
                        }
711
                        if ($searchParams['fulltext'] != '1') {
712
                            $documents[$doc['uid']]['page'] = 1;
713
                            $documents[$doc['uid']]['children'] = $this->findByPartof($doc['uid']);
0 ignored issues
show
Bug introduced by
The method findByPartof() does not exist on Kitodo\Dlf\Domain\Repository\DocumentRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

713
                            /** @scrutinizer ignore-call */ 
714
                            $documents[$doc['uid']]['children'] = $this->findByPartof($doc['uid']);
Loading history...
714
                        }
715
                    }
716
                    if (empty($documents[$doc['uid']]['metadata'])) {
717
                        $documents[$doc['uid']]['metadata'] = $this->fetchMetadataFromSolr($doc['uid'], $listedMetadata);
718
                    }
719
                    // get title of parent if empty
720
                    if (empty($documents[$doc['uid']]['title']) && ($documents[$doc['uid']]['partOf'] > 0)) {
721
                        $parentDocument = $this->findByUid($documents[$doc['uid']]['partOf']);
722
                        if ($parentDocument) {
723
                            $documents[$doc['uid']]['title'] = $parentDocument->getTitle();
724
                        }
725
                    }
726
                }
727
            }
728
        }
729
730
        return ['solrResults' => $result, 'numberOfToplevels' => $numberOfToplevels, 'documents' => $documents];
731
    }
732
733
    /**
734
     * Find all listed metadata for given document
735
     *
736
     * @param int $uid the uid of the document
737
     * @param \TYPO3\CMS\Extbase\Persistence\Generic\QueryResult $listedMetadata
738
     * @return array
739
     */
740
    public function fetchMetadataFromSolr($uid, $listedMetadata = [])
741
    {
742
        // Prepare query parameters.
743
        $params = [];
744
        $metadataArray = [];
745
746
        // Set some query parameters.
747
        $params['query'] = 'uid:' . $uid;
748
        $params['start'] = 0;
749
        $params['rows'] = 1;
750
        $params['sort'] = ['score' => 'desc'];
751
        $params['listMetadataRecords'] = [];
752
753
        // Restrict the fields to the required ones.
754
        $params['fields'] = 'uid,toplevel';
755
756
        if ($listedMetadata) {
757
            foreach ($listedMetadata as $metadata) {
758
                if ($metadata->getIndexIndexed()) {
759
                    $listMetadataRecord = $metadata->getIndexName() . '_' . ($metadata->getIndexTokenized() ? 't' : 'u') . ($metadata->getIndexStored() ? 's' : 'u') . 'i';
760
                    $params['fields'] .= ',' . $listMetadataRecord;
761
                    $params['listMetadataRecords'][$metadata->getIndexName()] = $listMetadataRecord;
762
                }
763
            }
764
        }
765
        // Set filter query to just get toplevel documents.
766
        $params['filterquery'][] = ['query' => 'toplevel:true'];
767
768
        // Perform search.
769
        $result = $this->searchSolr($params, true);
770
771
        if ($result['numFound'] > 0) {
772
            // There is only one result found because of toplevel:true.
773
            if (isset($result['documents'][0]['metadata'])) {
774
                $metadataArray = $result['documents'][0]['metadata'];
775
            }
776
        }
777
        return $metadataArray;
778
    }
779
780
    /**
781
     * Processes a search request
782
     *
783
     * @access public
784
     *
785
     * @param array $parameters: Additional search parameters
786
     * @param boolean $enableCache: Enable caching of Solr requests
787
     *
788
     * @return array The Apache Solr Documents that were fetched
789
     */
790
    protected function searchSolr($parameters = [], $enableCache = true)
791
    {
792
        // Set additional query parameters.
793
        $parameters['start'] = 0;
794
        // Set query.
795
        $parameters['query'] = isset($parameters['query']) ? $parameters['query'] : '*';
796
        $parameters['filterquery'] = isset($parameters['filterquery']) ? $parameters['filterquery'] : [];
797
798
        // Perform Solr query.
799
        // Instantiate search object.
800
        $solr = Solr::getInstance($this->settings['solrcore']);
801
        if (!$solr->ready) {
802
            Helper::log('Apache Solr not available', LOG_SEVERITY_ERROR);
803
            return [];
804
        }
805
806
        $cacheIdentifier = '';
807
        $cache = null;
808
        // Calculate cache identifier.
809
        if ($enableCache === true) {
810
            $cacheIdentifier = Helper::digest($solr->core . print_r($parameters, true));
0 ignored issues
show
Bug introduced by
Are you sure print_r($parameters, true) of type string|true 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

810
            $cacheIdentifier = Helper::digest($solr->core . /** @scrutinizer ignore-type */ print_r($parameters, true));
Loading history...
811
            $cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('tx_dlf_solr');
812
        }
813
        $resultSet = [
814
            'documents' => [],
815
            'numFound' => 0,
816
        ];
817
        if ($enableCache === false || ($entry = $cache->get($cacheIdentifier)) === false) {
818
            $selectQuery = $solr->service->createSelect($parameters);
819
820
            if ($parameters['fulltext'] === true) {
821
                // get highlighting component and apply settings
822
                $selectQuery->getHighlighting();
823
            }
824
825
            $solrRequest = $solr->service->createRequest($selectQuery);
826
827
            if ($parameters['fulltext'] === true) {
828
                // If it is a fulltext search, enable highlighting.
829
                // field for which highlighting is going to be performed,
830
                // is required if you want to have OCR highlighting
831
                $solrRequest->addParam('hl.ocr.fl', 'fulltext');
832
                // return the coordinates of highlighted search as absolute coordinates
833
                $solrRequest->addParam('hl.ocr.absoluteHighlights', 'on');
834
                // max amount of snippets for a single page
835
                $solrRequest->addParam('hl.snippets', 20);
836
                // we store the fulltext on page level and can disable this option
837
                $solrRequest->addParam('hl.ocr.trackPages', 'off');
838
            }
839
840
            // Perform search for all documents with the same uid that either fit to the search or marked as toplevel.
841
            $response = $solr->service->executeRequest($solrRequest);
842
            $result = $solr->service->createResult($selectQuery, $response);
843
844
            /** @scrutinizer ignore-call */
845
            $resultSet['numFound'] = $result->getNumFound();
846
            $highlighting = [];
847
            if ($parameters['fulltext'] === true) {
848
                $data = $result->getData();
849
                $highlighting = $data['ocrHighlighting'];
850
            }
851
            $fields = Solr::getFields();
852
853
            foreach ($result as $record) {
854
                $resultDocument = new ResultDocument($record, $highlighting, $fields);
855
856
                $document = [
857
                    'id' => $resultDocument->getId(),
858
                    'page' => $resultDocument->getPage(),
859
                    'snippet' => $resultDocument->getSnippets(),
860
                    'thumbnail' => $resultDocument->getThumbnail(),
861
                    'title' => $resultDocument->getTitle(),
862
                    'toplevel' => $resultDocument->getToplevel(),
863
                    'type' => $resultDocument->getType(),
864
                    'uid' => !empty($resultDocument->getUid()) ? $resultDocument->getUid() : $parameters['uid'],
865
                    'highlight' => $resultDocument->getHighlightsIds(),
866
                ];
867
                foreach ($parameters['listMetadataRecords'] as $indexName => $solrField) {
868
                    if (!empty($record->$solrField)) {
869
                        $document['metadata'][$indexName] = $record->$solrField;
870
                    }
871
                }
872
                $resultSet['documents'][] = $document;
873
            }
874
875
            // Save value in cache.
876
            if (!empty($resultSet) && $enableCache === true) {
877
                $cache->set($cacheIdentifier, $resultSet);
878
            }
879
        } else {
880
            // Return cache hit.
881
            $resultSet = $entry;
882
        }
883
        return $resultSet;
884
    }
885
886
}
887