Completed
Push — master ( ce3674...ce3674 )
by
unknown
15s queued 12s
created

DocumentRepository::findDocumentsBySettings()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 8
nop 1
dl 0
loc 21
rs 9.6111
c 0
b 0
f 0
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\Indexer;
18
use Kitodo\Dlf\Common\Solr;
19
use Kitodo\Dlf\Domain\Model\Document;
20
use Kitodo\Dlf\Common\SolrSearchResult\ResultDocument;
21
use TYPO3\CMS\Core\Cache\CacheManager;
22
use TYPO3\CMS\Core\Database\ConnectionPool;
23
use TYPO3\CMS\Core\Database\Connection;
24
use TYPO3\CMS\Core\Utility\GeneralUtility;
25
use TYPO3\CMS\Core\Utility\MathUtility;
26
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
27
28
class DocumentRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
29
{
30
    /**
31
     * The controller settings passed to the repository for some special actions.
32
     *
33
     * @var array
34
     * @access protected
35
     */
36
    protected $settings;
37
38
    /**
39
     * Find one document by given parameters
40
     *
41
     * GET parameters may be:
42
     *
43
     * - 'id': the uid of the document
44
     * - 'location': the URL of the location of the XML file
45
     * - 'recordId': the record_id of the document
46
     *
47
     * @param array $parameters
48
     *
49
     * @return \Kitodo\Dlf\Domain\Model\Document|null
50
     */
51
    public function findOneByParameters($parameters)
52
    {
53
        $doc = null;
54
        $document = null;
55
56
        if (isset($parameters['id']) && MathUtility::canBeInterpretedAsInteger($parameters['id'])) {
57
58
            $document = $this->findOneByIdAndSettings($parameters['id']);
59
60
        } else if (isset($parameters['recordId'])) {
61
62
            $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

62
            /** @scrutinizer ignore-call */ 
63
            $document = $this->findOneByRecordId($parameters['recordId']);
Loading history...
63
64
        } else if (isset($parameters['location']) && GeneralUtility::isValidUrl($parameters['location'])) {
65
66
            $doc = Doc::getInstance($parameters['location'], [], true);
67
68
            if ($doc->recordId) {
69
                $document = $this->findOneByRecordId($doc->recordId);
70
            }
71
72
            if ($document === null) {
73
                // create new (dummy) Document object
74
                $document = GeneralUtility::makeInstance(Document::class);
75
                $document->setLocation($parameters['location']);
76
            }
77
78
        }
79
80
        if ($document !== null && $doc === null) {
81
            $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

81
            $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...
82
        }
83
84
        if ($doc !== null) {
85
            $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

85
            $document->/** @scrutinizer ignore-call */ 
86
                       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...
86
        }
87
88
        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...
89
90
    }
91
92
93
    public function findByUidAndPartOf($uid, $partOf)
94
    {
95
        $query = $this->createQuery();
96
97
        $query->matching($query->equals('uid', $uid));
98
        $query->matching($query->equals('partof', $partOf));
99
100
        return $query->execute();
101
    }
102
103
    /**
104
     * Find the oldest document
105
     *
106
     * @return \Kitodo\Dlf\Domain\Model\Document|null
107
     */
108
    public function findOldestDocument()
109
    {
110
        $query = $this->createQuery();
111
112
        $query->setOrderings(['tstamp' => QueryInterface::ORDER_ASCENDING]);
113
        $query->setLimit(1);
114
115
        return $query->execute()->getFirst();
116
    }
117
118
    /**
119
     * @param int $partOf
120
     * @param  \Kitodo\Dlf\Domain\Model\Structure $structure
121
     * @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
122
     */
123
    public function getChildrenOfYearAnchor($partOf, $structure)
124
    {
125
        $query = $this->createQuery();
126
127
        $query->matching($query->equals('structure', $structure));
128
        $query->matching($query->equals('partof', $partOf));
129
130
        $query->setOrderings([
131
            'mets_orderlabel' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING
132
        ]);
133
134
        return $query->execute();
135
    }
136
137
    /**
138
     * Finds all documents for the given settings
139
     *
140
     * @param int $uid
141
     * @param array $settings
142
     *
143
     * @return \Kitodo\Dlf\Domain\Model\Document|null
144
     */
145
    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

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

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

730
                            /** @scrutinizer ignore-call */ 
731
                            $children = $this->findByPartof($doc['uid']);
Loading history...
731
                            foreach ($children as $docChild) {
732
                                // We need only a few fields from the children, but we need them as array.
733
                                $childDocument = [
734
                                    'thumbnail' => $docChild->getThumbnail(),
735
                                    'title' => $docChild->getTitle(),
736
                                    'structure' => Helper::getIndexNameFromUid($docChild->getStructure(), 'tx_dlf_structures'),
737
                                    'metsOrderlabel' => $docChild->getMetsOrderlabel(),
738
                                    'uid' => $docChild->getUid(),
739
                                    'metadata' => $this->fetchMetadataFromSolr($docChild->getUid(), $listedMetadata)
740
                                ];
741
                                $documents[$doc['uid']]['children'][$docChild->getUid()] = $childDocument;
742
                            }
743
                        }
744
                    }
745
                    if (empty($documents[$doc['uid']]['metadata'])) {
746
                        $documents[$doc['uid']]['metadata'] = $this->fetchMetadataFromSolr($doc['uid'], $listedMetadata);
747
                    }
748
                    // get title of parent if empty
749
                    if (empty($documents[$doc['uid']]['title']) && ($documents[$doc['uid']]['partOf'] > 0)) {
750
                        $parentDocument = $this->findByUid($documents[$doc['uid']]['partOf']);
751
                        if ($parentDocument) {
752
                            $documents[$doc['uid']]['title'] = '[' . $parentDocument->getTitle() . ']';
753
                        }
754
                    }
755
                }
756
            }
757
        }
758
759
        return ['solrResults' => $result, 'numberOfToplevels' => $numberOfToplevels, 'documents' => $documents];
760
    }
761
762
    /**
763
     * Find all listed metadata for given document
764
     *
765
     * @param int $uid the uid of the document
766
     * @param \TYPO3\CMS\Extbase\Persistence\Generic\QueryResult $listedMetadata
767
     * @return array
768
     */
769
    public function fetchMetadataFromSolr($uid, $listedMetadata = [])
770
    {
771
        // Prepare query parameters.
772
        $params = [];
773
        $metadataArray = [];
774
775
        // Set some query parameters.
776
        $params['query'] = 'uid:' . $uid;
777
        $params['start'] = 0;
778
        $params['rows'] = 1;
779
        $params['sort'] = ['score' => 'desc'];
780
        $params['listMetadataRecords'] = [];
781
782
        // Restrict the fields to the required ones.
783
        $params['fields'] = 'uid,toplevel';
784
785
        if ($listedMetadata) {
786
            foreach ($listedMetadata as $metadata) {
787
                if ($metadata->getIndexIndexed()) {
788
                    $listMetadataRecord = $metadata->getIndexName() . '_' . ($metadata->getIndexTokenized() ? 't' : 'u') . ($metadata->getIndexStored() ? 's' : 'u') . 'i';
789
                    $params['fields'] .= ',' . $listMetadataRecord;
790
                    $params['listMetadataRecords'][$metadata->getIndexName()] = $listMetadataRecord;
791
                }
792
            }
793
        }
794
        // Set filter query to just get toplevel documents.
795
        $params['filterquery'][] = ['query' => 'toplevel:true'];
796
797
        // Perform search.
798
        $result = $this->searchSolr($params, true);
799
800
        if ($result['numFound'] > 0) {
801
            // There is only one result found because of toplevel:true.
802
            if (isset($result['documents'][0]['metadata'])) {
803
                $metadataArray = $result['documents'][0]['metadata'];
804
            }
805
        }
806
        return $metadataArray;
807
    }
808
809
    /**
810
     * Processes a search request
811
     *
812
     * @access public
813
     *
814
     * @param array $parameters: Additional search parameters
815
     * @param boolean $enableCache: Enable caching of Solr requests
816
     *
817
     * @return array The Apache Solr Documents that were fetched
818
     */
819
    protected function searchSolr($parameters = [], $enableCache = true)
820
    {
821
        // Set additional query parameters.
822
        $parameters['start'] = 0;
823
        // Set query.
824
        $parameters['query'] = isset($parameters['query']) ? $parameters['query'] : '*';
825
        $parameters['filterquery'] = isset($parameters['filterquery']) ? $parameters['filterquery'] : [];
826
827
        // Perform Solr query.
828
        // Instantiate search object.
829
        $solr = Solr::getInstance($this->settings['solrcore']);
830
        if (!$solr->ready) {
831
            Helper::log('Apache Solr not available', LOG_SEVERITY_ERROR);
832
            return [];
833
        }
834
835
        $cacheIdentifier = '';
836
        $cache = null;
837
        // Calculate cache identifier.
838
        if ($enableCache === true) {
839
            $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

839
            $cacheIdentifier = Helper::digest($solr->core . /** @scrutinizer ignore-type */ print_r($parameters, true));
Loading history...
840
            $cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('tx_dlf_solr');
841
        }
842
        $resultSet = [
843
            'documents' => [],
844
            'numFound' => 0,
845
        ];
846
        if ($enableCache === false || ($entry = $cache->get($cacheIdentifier)) === false) {
847
            $selectQuery = $solr->service->createSelect($parameters);
848
849
            if ($parameters['fulltext'] === true) {
850
                // get highlighting component and apply settings
851
                $selectQuery->getHighlighting();
852
            }
853
854
            $solrRequest = $solr->service->createRequest($selectQuery);
855
856
            if ($parameters['fulltext'] === true) {
857
                // If it is a fulltext search, enable highlighting.
858
                // field for which highlighting is going to be performed,
859
                // is required if you want to have OCR highlighting
860
                $solrRequest->addParam('hl.ocr.fl', 'fulltext');
861
                // return the coordinates of highlighted search as absolute coordinates
862
                $solrRequest->addParam('hl.ocr.absoluteHighlights', 'on');
863
                // max amount of snippets for a single page
864
                $solrRequest->addParam('hl.snippets', 20);
865
                // we store the fulltext on page level and can disable this option
866
                $solrRequest->addParam('hl.ocr.trackPages', 'off');
867
            }
868
869
            // Perform search for all documents with the same uid that either fit to the search or marked as toplevel.
870
            $response = $solr->service->executeRequest($solrRequest);
871
            $result = $solr->service->createResult($selectQuery, $response);
872
873
            /** @scrutinizer ignore-call */
874
            $resultSet['numFound'] = $result->getNumFound();
875
            $highlighting = [];
876
            if ($parameters['fulltext'] === true) {
877
                $data = $result->getData();
878
                $highlighting = $data['ocrHighlighting'];
879
            }
880
            $fields = Solr::getFields();
881
882
            foreach ($result as $record) {
883
                $resultDocument = new ResultDocument($record, $highlighting, $fields);
884
885
                $document = [
886
                    'id' => $resultDocument->getId(),
887
                    'page' => $resultDocument->getPage(),
888
                    'snippet' => $resultDocument->getSnippets(),
889
                    'thumbnail' => $resultDocument->getThumbnail(),
890
                    'title' => $resultDocument->getTitle(),
891
                    'toplevel' => $resultDocument->getToplevel(),
892
                    'type' => $resultDocument->getType(),
893
                    'uid' => !empty($resultDocument->getUid()) ? $resultDocument->getUid() : $parameters['uid'],
894
                    'highlight' => $resultDocument->getHighlightsIds(),
895
                ];
896
                foreach ($parameters['listMetadataRecords'] as $indexName => $solrField) {
897
                    if (!empty($record->$solrField)) {
898
                        $document['metadata'][$indexName] = $record->$solrField;
899
                    }
900
                }
901
                $resultSet['documents'][] = $document;
902
            }
903
904
            // Save value in cache.
905
            if (!empty($resultSet) && $enableCache === true) {
906
                $cache->set($cacheIdentifier, $resultSet);
907
            }
908
        } else {
909
            // Return cache hit.
910
            $resultSet = $entry;
911
        }
912
        return $resultSet;
913
    }
914
915
}
916