Completed
Push — master ( b1b8c7...b37043 )
by Timo
06:43
created

Queue   C

Complexity

Total Complexity 72

Size/Duplication

Total Lines 854
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 10

Importance

Changes 4
Bugs 0 Features 1
Metric Value
wmc 72
c 4
b 0
f 1
lcom 2
cbo 10
dl 0
loc 854
rs 5

24 Methods

Rating   Name   Duplication   Size   Complexity  
B initialize() 0 41 6
A initializeIndexingConfiguration() 0 23 1
A getIndexingConfigurationByItem() 0 15 2
A getIndexingConfigurationsByItem() 0 15 2
C addItem() 0 71 12
A getPageItemChangedTime() 0 16 2
A getLocalizableItemChangedTime() 0 19 2
B deleteItemsBySite() 0 50 5
A deleteAllItems() 0 4 1
A getItem() 0 21 2
A getItems() 0 12 1
A getItemsCountBySite() 0 17 2
A getAllItemsCount() 0 11 1
A getItemsToIndex() 0 23 2
A getLastIndexTime() 0 19 2
A getLastIndexedItemId() 0 18 2
B updateItem() 0 32 4
A markItemAsFailed() 0 23 3
B getIndexQueueItemObjectsFromRecords() 0 52 7
B getItemChangedTime() 0 47 5
A containsItem() 0 12 1
A containsIndexedItem() 0 13 1
B deleteItem() 0 28 3
B deleteItemsByType() 0 29 3

How to fix   Complexity   

Complex Class

Complex classes like Queue 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Queue, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace ApacheSolrForTypo3\Solr\IndexQueue;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2009-2015 Ingo Renner <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 2 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use ApacheSolrForTypo3\Solr\Utility\DatabaseUtility;
28
use ApacheSolrForTypo3\Solr\Site;
29
use ApacheSolrForTypo3\Solr\Util;
30
use TYPO3\CMS\Backend\Utility\BackendUtility;
31
use TYPO3\CMS\Core\Utility\GeneralUtility;
32
33
/**
34
 * The Indexing Queue. It allows us to decouple from frontend indexing and
35
 * reacting to changes faster.
36
 *
37
 * @author Ingo Renner <[email protected]>
38
 * @package TYPO3
39
 * @subpackage solr
40
 */
41
class Queue
42
{
43
44
    // FIXME some of the methods should be renamed to plural forms
45
    // FIXME singular form methods should deal with exactly one item only
46
47
48
    /**
49
     * Returns the timestamp of the last indexing run.
50
     *
51
     * @param integer $rootPageId The root page uid for which to get
52
     *      the last indexed item id
53
     * @return integer Timestamp of last index run.
54
     */
55
    public function getLastIndexTime($rootPageId)
56
    {
57
        $lastIndexTime = 0;
58
59
        $lastIndexedRow = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
60
            'indexed',
61
            'tx_solr_indexqueue_item',
62
            'root = ' . (int)$rootPageId,
63
            '',
64
            'indexed DESC',
65
            1
66
        );
67
68
        if ($lastIndexedRow[0]['indexed']) {
69
            $lastIndexTime = $lastIndexedRow[0]['indexed'];
70
        }
71
72
        return $lastIndexTime;
73
    }
74
75
    /**
76
     * Returns the uid of the last indexed item in the queue
77
     *
78
     * @param integer $rootPageId The root page uid for which to get
79
     *      the last indexed item id
80
     * @return integer The last indexed item's ID.
81
     */
82
    public function getLastIndexedItemId($rootPageId)
83
    {
84
        $lastIndexedItemId = 0;
85
86
        $lastIndexedItemRow = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
87
            'uid',
88
            'tx_solr_indexqueue_item',
89
            'root = ' . (int)$rootPageId,
90
            '',
91
            'indexed DESC',
92
            1
93
        );
94
        if ($lastIndexedItemRow[0]['uid']) {
95
            $lastIndexedItemId = $lastIndexedItemRow[0]['uid'];
96
        }
97
98
        return $lastIndexedItemId;
99
    }
100
101
    /**
102
     * Truncate and rebuild the tx_solr_indexqueue_item table. This is the most
103
     * complete way to force reindexing, or to build the Index Queue for the
104
     * first time. The Index Queue initialization is site-specific.
105
     *
106
     * @param Site $site The site to initialize
107
     * @param string $indexingConfigurationName Name of a specific
108
     *      indexing configuration
109
     * @return array An array of booleans, each representing whether the
110
     *      initialization for an indexing configuration was successful
111
     */
112
    public function initialize(Site $site, $indexingConfigurationName = '')
113
    {
114
        $indexingConfigurations = array();
115
        $initializationStatus = array();
116
117
        if (empty($indexingConfigurationName)) {
118
            $solrConfiguration = $site->getSolrConfiguration();
119
            $indexingConfigurations = $solrConfiguration->getEnabledIndexQueueConfigurationNames();
120
        } else {
121
            $indexingConfigurations[] = $indexingConfigurationName;
122
        }
123
124
        foreach ($indexingConfigurations as $indexingConfigurationName) {
125
            $initializationStatus[$indexingConfigurationName] = $this->initializeIndexingConfiguration(
126
                $site,
127
                $indexingConfigurationName
128
            );
129
        }
130
131
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessIndexQueueInitialization'])) {
132
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessIndexQueueInitialization'] as $classReference) {
133
                $indexQueueInitializationPostProcessor = GeneralUtility::getUserObj($classReference);
134
135
                if ($indexQueueInitializationPostProcessor instanceof InitializationPostProcessor) {
136
                    $indexQueueInitializationPostProcessor->postProcessIndexQueueInitialization(
137
                        $site,
138
                        $indexingConfigurations,
139
                        $initializationStatus
140
                    );
141
                } else {
142
                    throw new \UnexpectedValueException(
143
                        get_class($indexQueueInitializationPostProcessor) .
144
                        ' must implement interface ApacheSolrForTypo3\Solr\IndexQueue\InitializationPostProcessor',
145
                        1345815561
146
                    );
147
                }
148
            }
149
        }
150
151
        return $initializationStatus;
152
    }
153
154
    /**
155
     * Initializes the Index Queue for a specific indexing configuration.
156
     *
157
     * @param Site $site The site to initialize
158
     * @param string $indexingConfigurationName name of a specific
159
     *      indexing configuration
160
     * @return boolean TRUE if the initialization was successful, FALSE otherwise
161
     */
162
    protected function initializeIndexingConfiguration(
163
        Site $site,
164
        $indexingConfigurationName
165
    ) {
166
        // clear queue
167
        $this->deleteItemsBySite($site, $indexingConfigurationName);
168
169
        $solrConfiguration = $site->getSolrConfiguration();
170
171
        $tableToIndex = $solrConfiguration->getIndexQueueTableNameOrFallbackToConfigurationName($indexingConfigurationName);
172
        $initializerClass = $solrConfiguration->getIndexQueueInitializerClassByConfigurationName($indexingConfigurationName);
173
174
        $initializer = GeneralUtility::makeInstance($initializerClass);
175
        /** @var $initializer \ApacheSolrForTypo3\Solr\IndexQueue\Initializer\AbstractInitializer */
176
        $initializer->setSite($site);
177
        $initializer->setType($tableToIndex);
178
        $initializer->setIndexingConfigurationName($indexingConfigurationName);
179
180
        $indexConfiguration = $solrConfiguration->getIndexQueueConfigurationByName($indexingConfigurationName);
181
        $initializer->setIndexingConfiguration($indexConfiguration);
182
183
        return $initializer->initialize();
184
    }
185
186
    /**
187
     * Gets the indexing configuration to use for an item.
188
     * Sometimes, when there are multiple configurations for a certain item type
189
     * (table) it can be hard or even impossible to find which one to use
190
     * though.
191
     * Currently selects the first indexing configuration where the name matches
192
     * the itemType or where the configured tbale is the same as the itemType.
193
     *
194
     * !!! Might return incorrect results for complex configurations !!!
195
     * Try to set the indexingConfiguration directly when using the updateItem()
196
     * method in such situations.
197
     *
198
     * @param string $itemType The item's type, usually a table name.
199
     * @param string $itemUid The item's uid, usually an integer uid, could be a
200
     *      different value for non-database-record types.
201
     * @param integer $rootPageId The configuration's page tree's root page id.
202
     *      Optional, not needed for all types.
203
     * @return string The indexing configuration's name to use when indexing
204
     * @deprecated Use getIndexingConfigurationsByItem() now, which behaves
205
     *      almost the same way but returns an array of configurations
206
     */
207
    protected function getIndexingConfigurationByItem(
208
        $itemType,
209
        $itemUid,
210
        $rootPageId = null
211
    ) {
212
        $indexingConfigurationName = '';
213
214
        $configurations = $this->getIndexingConfigurationsByItem($itemType,
215
            $itemUid, $rootPageId);
216
        if (count($configurations) > 0) {
217
            $indexingConfigurationName = $configurations[0];
218
        }
219
220
        return $indexingConfigurationName;
221
    }
222
223
    /**
224
     * Gets the indexing configurations to use for an item.
225
     * Multiple configurations for a certain item type (table) might be available.
226
     *
227
     * @param string $itemType The item's type, usually a table name.
228
     * @param string $itemUid The item's uid, usually an integer uid, could be a
229
     *      different value for non-database-record types.
230
     * @param integer $rootPageId The configuration's page tree's root page id.
231
     *      Optional, not needed for all types.
232
     * @return array<string> The indexing configurations names to use when indexing
233
     */
234
    protected function getIndexingConfigurationsByItem(
235
        $itemType,
236
        $itemUid,
0 ignored issues
show
Unused Code introduced by
The parameter $itemUid is not used and could be removed.

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

Loading history...
237
        $rootPageId = null
238
    ) {
239
        $possibleIndexingConfigurationNames = array();
240
241
        if (!is_null($rootPageId)) {
242
            // get configuration for the root's branch
243
            $solrConfiguration = Util::getSolrConfigurationFromPageId($rootPageId);
244
            $possibleIndexingConfigurationNames = $solrConfiguration->getIndexQueueConfigurationNamesByTableName($itemType);
245
        }
246
247
        return $possibleIndexingConfigurationNames;
248
    }
249
250
    /**
251
     * Marks an item as needing (re)indexing.
252
     *
253
     * Like with Solr itself, there's no add method, just a simple update method
254
     * that handles the adds, too.
255
     *
256
     * @param string $itemType The item's type, usually a table name.
257
     * @param string $itemUid The item's uid, usually an integer uid, could be a
258
     *      different value for non-database-record types.
259
     * @param string $indexingConfiguration The item's indexing configuration to use.
260
     *      Optional, overwrites existing / determined configuration.
261
     * @param int $forcedChangeTime The change time for the item if set, otherwise
262
     *          value from getItemChangedTime() is used.
263
     */
264
    public function updateItem(
265
        $itemType,
266
        $itemUid,
267
        $indexingConfiguration = null,
268
        $forcedChangeTime = 0
269
    ) {
270
        $itemInQueue = $this->containsItem($itemType, $itemUid);
271
272
        if ($itemInQueue) {
273
            // update if that item is in the queue already
274
            $changes = array(
275
                'changed' => ($forcedChangeTime > 0)
276
                                ? $forcedChangeTime
277
                                : $this->getItemChangedTime($itemType, $itemUid)
278
            );
279
280
            if (!empty($indexingConfiguration)) {
281
                $changes['indexing_configuration'] = $indexingConfiguration;
282
            }
283
284
            $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
285
                'tx_solr_indexqueue_item',
286
                'item_type = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($itemType,
287
                    'tx_solr_indexqueue_item') .
288
                ' AND item_uid = ' . (int)$itemUid,
289
                $changes
290
            );
291
        } else {
292
            // add the item since it's not in the queue yet
293
            $this->addItem($itemType, $itemUid, $indexingConfiguration);
294
        }
295
    }
296
297
    /**
298
     * Adds an item to the index queue.
299
     *
300
     * Not meant for public use.
301
     *
302
     * @param string $itemType The item's type, usually a table name.
303
     * @param string $itemUid The item's uid, usually an integer uid, could be a
304
     *      different value for non-database-record types.
305
     * @param string $indexingConfiguration The item's indexing configuration to use.
306
     *      Optional, overwrites existing / determined configuration.
307
     * @return void
308
     */
309
    private function addItem($itemType, $itemUid, $indexingConfiguration)
310
    {
311
        $additionalRecordFields = '';
312
        if ($itemType == 'pages') {
313
            $additionalRecordFields = ', doktype, uid';
314
        }
315
316
        $record = BackendUtility::getRecord($itemType, $itemUid,
317
            'pid' . $additionalRecordFields);
318
319
        if (empty($record) || ($itemType == 'pages' && !Util::isAllowedPageType($record))) {
0 ignored issues
show
Bug introduced by
It seems like $record defined by \TYPO3\CMS\Backend\Utili...additionalRecordFields) on line 316 can also be of type boolean; however, ApacheSolrForTypo3\Solr\Util::isAllowedPageType() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
320
            return;
321
        }
322
323
        if ($itemType == 'pages') {
324
            $rootPageId = Util::getRootPageId($itemUid);
325
        } else {
326
            $rootPageId = Util::getRootPageId($record['pid'], true);
327
        }
328
329
        if (Util::isRootPage($rootPageId)) {
330
            $item = array(
331
                'root' => $rootPageId,
332
                'item_type' => $itemType,
333
                'item_uid' => $itemUid,
334
                'changed' => $this->getItemChangedTime($itemType, $itemUid),
335
                'errors' => ''
336
            );
337
338
            if (!empty($indexingConfiguration)) {
339
                $indexingConfigurationList = array($indexingConfiguration);
340
            } else {
341
                $indexingConfigurationList = $this->getIndexingConfigurationsByItem(
342
                    $itemType, $itemUid, $rootPageId
343
                );
344
            }
345
346
            $solrConfiguration = Util::getSolrConfigurationFromPageId($rootPageId);
347
348
            // make a backup of the current item
349
            $baseItem = $item;
350
            foreach ($indexingConfigurationList as $indexingConfigurationCurrent) {
351
                $item = $baseItem;
352
                $item['indexing_configuration'] = $indexingConfigurationCurrent;
353
354
                $addItemToQueue = true;
355
                // Ensure additionalWhereClause is applied.
356
                $additionalWhere = $solrConfiguration->getIndexQueueAdditionalWhereClauseByConfigurationName($item['indexing_configuration']);
357
                if ($additionalWhere !== '') {
358
                    $indexingConfigurationCheckRecord = BackendUtility::getRecord(
359
                        $itemType,
360
                        $itemUid,
361
                        'pid' . $additionalRecordFields,
362
                        $additionalWhere
363
                    );
364
365
                    if (empty($indexingConfigurationCheckRecord)) {
366
                        // item does not match the indexing configuration's additionalWhereClause
367
                        $addItemToQueue = false;
368
                    }
369
                }
370
371
                if ($addItemToQueue) {
372
                    $GLOBALS['TYPO3_DB']->exec_INSERTquery(
373
                        'tx_solr_indexqueue_item',
374
                        $item
375
                    );
376
                }
377
            }
378
        }
379
    }
380
381
    /**
382
     * Determines the time for when an item should be indexed. This timestamp
383
     * is then stored in the changed column in the Index Queue.
384
     *
385
     * The changed timestamp usually is now - time(). For records which are set
386
     * to published at a later time, this timestamp is the start time. So if a
387
     * future start time has been set, that will be used to delay indexing
388
     * of an item.
389
     *
390
     * @param string $itemType The item's table name.
391
     * @param string $itemUid The item's uid, usually an integer uid, could be a
392
     *      different value for non-database-record types.
393
     * @return integer Timestamp of the item's changed time or future start time
394
     */
395
    protected function getItemChangedTime($itemType, $itemUid)
396
    {
397
        $itemTypeHasStartTimeColumn = false;
398
        $changedTimeColumns = $GLOBALS['TCA'][$itemType]['ctrl']['tstamp'];
399
        $startTime = 0;
400
        $pageChangedTime = 0;
401
402
        if (!empty($GLOBALS['TCA'][$itemType]['ctrl']['enablecolumns']['starttime'])) {
403
            $itemTypeHasStartTimeColumn = true;
404
            $changedTimeColumns .= ', ' . $GLOBALS['TCA'][$itemType]['ctrl']['enablecolumns']['starttime'];
405
        }
406
        if ($itemType == 'pages') {
407
            // does not carry time information directly, but needed to support
408
            // canonical pages
409
            $changedTimeColumns .= ', content_from_pid';
410
        }
411
412
        $record = BackendUtility::getRecord($itemType, $itemUid,
413
            $changedTimeColumns);
414
        $itemChangedTime = $record[$GLOBALS['TCA'][$itemType]['ctrl']['tstamp']];
415
416
        if ($itemTypeHasStartTimeColumn) {
417
            $startTime = $record[$GLOBALS['TCA'][$itemType]['ctrl']['enablecolumns']['starttime']];
418
        }
419
420
        if ($itemType == 'pages') {
421
            $record['uid'] = $itemUid;
422
            // overrule the page's last changed time with the most recent
423
            //content element change
424
            $pageChangedTime = $this->getPageItemChangedTime($record);
0 ignored issues
show
Bug introduced by
It seems like $record defined by \TYPO3\CMS\Backend\Utili...d, $changedTimeColumns) on line 412 can also be of type boolean; however, ApacheSolrForTypo3\Solr\...etPageItemChangedTime() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
425
        }
426
427
        $localizationsChangedTime = $this->getLocalizableItemChangedTime($itemType,
428
            $itemUid);
429
430
        // if start time exists and start time is higher than last changed timestamp
431
        // then set changed to the future start time to make the item
432
        // indexed at a later time
433
        $changedTime = max(
434
            $itemChangedTime,
435
            $pageChangedTime,
436
            $localizationsChangedTime,
437
            $startTime
438
        );
439
440
        return $changedTime;
441
    }
442
443
    /**
444
     * Gets the most recent changed time of a page's content elements
445
     *
446
     * @param array $page Partial page record
447
     * @return integer Timestamp of the most recent content element change
448
     */
449
    protected function getPageItemChangedTime(array $page)
450
    {
451
        if (!empty($page['content_from_pid'])) {
452
            // canonical page, get the original page's last changed time
453
            $pageContentLastChangedTime = $this->getPageItemChangedTime(array('uid' => $page['content_from_pid']));
454
        } else {
455
            $pageContentLastChangedTime = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
456
                'MAX(tstamp) AS changed_time',
457
                'tt_content',
458
                'pid = ' . (int)$page['uid']
459
            );
460
            $pageContentLastChangedTime = $pageContentLastChangedTime['changed_time'];
461
        }
462
463
        return $pageContentLastChangedTime;
464
    }
465
466
    /**
467
     * Gets the most recent changed time for an item taking into account
468
     * localized records.
469
     *
470
     * @param string $itemType The item's type, usually a table name.
471
     * @param string $itemUid The item's uid, usually an integer uid, could be a
472
     *      different value for non-database-record types.
473
     * @return integer Timestamp of the most recent content element change
474
     */
475
    protected function getLocalizableItemChangedTime($itemType, $itemUid)
476
    {
477
        $localizedChangedTime = 0;
478
479
        if (isset($GLOBALS['TCA'][$itemType]['ctrl']['transOrigPointerField'])) {
480
            // table is localizable
481
            $translationOriginalPointerField = $GLOBALS['TCA'][$itemType]['ctrl']['transOrigPointerField'];
482
483
            $itemUid = intval($itemUid);
484
            $localizedChangedTime = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
485
                'MAX(tstamp) AS changed_time',
486
                $itemType,
487
                "uid = $itemUid OR $translationOriginalPointerField = $itemUid"
488
            );
489
            $localizedChangedTime = $localizedChangedTime['changed_time'];
490
        }
491
492
        return $localizedChangedTime;
493
    }
494
495
    /**
496
     * Checks whether the Index Queue contains a specific item.
497
     *
498
     * @param string $itemType The item's type, usually a table name.
499
     * @param string $itemUid The item's uid, usually an integer uid, could be a
500
     *      different value for non-database-record types.
501
     * @return boolean TRUE if the item is found in the queue, FALSE otherwise
502
     */
503
    public function containsItem($itemType, $itemUid)
504
    {
505
        $itemIsInQueue = (boolean)$GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
506
            'uid',
507
            'tx_solr_indexqueue_item',
508
            'item_type = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($itemType,
509
                'tx_solr_indexqueue_item') .
510
            ' AND item_uid = ' . (int)$itemUid
511
        );
512
513
        return $itemIsInQueue;
514
    }
515
516
    /**
517
     * Checks whether the Index Queue contains a specific item that has been
518
     * marked as indexed.
519
     *
520
     * @param string $itemType The item's type, usually a table name.
521
     * @param string $itemUid The item's uid, usually an integer uid, could be a
522
     *      different value for non-database-record types.
523
     * @return boolean TRUE if the item is found in the queue and marked as
524
     *      indexed, FALSE otherwise
525
     */
526
    public function containsIndexedItem($itemType, $itemUid)
527
    {
528
        $itemIsInQueue = (boolean)$GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
529
            'uid',
530
            'tx_solr_indexqueue_item',
531
            'item_type = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($itemType,
532
                'tx_solr_indexqueue_item') .
533
            ' AND item_uid = ' . (int)$itemUid .
534
            ' AND indexed > 0'
535
        );
536
537
        return $itemIsInQueue;
538
    }
539
540
    /**
541
     * Removes an item from the Index Queue.
542
     *
543
     * @param string $itemType The type of the item to remove, usually a table name.
544
     * @param integer $itemUid The uid of the item to remove
545
     */
546
    public function deleteItem($itemType, $itemUid)
547
    {
548
        $uidList = array();
549
550
        // get the item uids to use them in the deletes afterwards
551
        $items = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
552
            'uid',
553
            'tx_solr_indexqueue_item',
554
            'item_type = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($itemType,
555
                'tx_solr_indexqueue_item') .
556
            ' AND item_uid = ' . intval($itemUid)
557
        );
558
559
        if (count($items)) {
560
            foreach ($items as $item) {
561
                $uidList[] = $item['uid'];
562
            }
563
564
            $GLOBALS['TYPO3_DB']->exec_DELETEquery(
565
                'tx_solr_indexqueue_item',
566
                'uid IN(' . implode(',', $uidList) . ')'
567
            );
568
            $GLOBALS['TYPO3_DB']->exec_DELETEquery(
569
                'tx_solr_indexqueue_indexing_property',
570
                'item_id IN(' . implode(',', $uidList) . ')'
571
            );
572
        }
573
    }
574
575
    /**
576
     * Removes all items of a certain type from the Index Queue.
577
     *
578
     * @param string $itemType The type of items to remove, usually a table name.
579
     */
580
    public function deleteItemsByType($itemType)
581
    {
582
        $uidList = array();
583
584
        // get the item uids to use them in the deletes afterwards
585
        $items = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
586
            'uid',
587
            'tx_solr_indexqueue_item',
588
            'item_type = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr(
589
                $itemType,
590
                'tx_solr_indexqueue_item'
591
            )
592
        );
593
594
        if (count($items)) {
595
            foreach ($items as $item) {
596
                $uidList[] = $item['uid'];
597
            }
598
599
            $GLOBALS['TYPO3_DB']->exec_DELETEquery(
600
                'tx_solr_indexqueue_item',
601
                'uid IN(' . implode(',', $uidList) . ')'
602
            );
603
            $GLOBALS['TYPO3_DB']->exec_DELETEquery(
604
                'tx_solr_indexqueue_indexing_property',
605
                'item_id IN(' . implode(',', $uidList) . ')'
606
            );
607
        }
608
    }
609
610
    /**
611
     * Removes all items of a certain site from the Index Queue. Accepts an
612
     * optional parameter to limit the deleted items by indexing configuration.
613
     *
614
     * @param Site $site The site to remove items for.
615
     * @param string $indexingConfigurationName Name of a specific indexing
616
     *      configuration
617
     */
618
    public function deleteItemsBySite(
619
        Site $site,
620
        $indexingConfigurationName = ''
621
    ) {
622
        $rootPageConstraint = 'tx_solr_indexqueue_item.root = ' . $site->getRootPageId();
623
624
        $indexingConfigurationConstraint = '';
625
        if (!empty($indexingConfigurationName)) {
626
            $indexingConfigurationConstraint =
627
                ' AND tx_solr_indexqueue_item.indexing_configuration = \'' .
628
                $indexingConfigurationName . '\'';
629
        }
630
631
        DatabaseUtility::transactionStart();
632
        try {
633
            // reset Index Queue
634
            $result = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
635
                'tx_solr_indexqueue_item',
636
                $rootPageConstraint . $indexingConfigurationConstraint
637
            );
638
            if (!$result) {
639
                throw new \RuntimeException(
640
                    'Failed to reset Index Queue for site ' . $site->getLabel(),
641
                    1412986560
642
                );
643
            }
644
645
            // reset Index Queue Properties
646
            $indexQueuePropertyResetQuery = '
647
				DELETE tx_solr_indexqueue_indexing_property.*
648
				FROM tx_solr_indexqueue_indexing_property
649
				INNER JOIN tx_solr_indexqueue_item
650
					ON tx_solr_indexqueue_item.uid = tx_solr_indexqueue_indexing_property.item_id
651
					AND ' .
652
                $rootPageConstraint .
653
                $indexingConfigurationConstraint;
654
655
            $result = $GLOBALS['TYPO3_DB']->sql_query($indexQueuePropertyResetQuery);
656
            if (!$result) {
657
                throw new \RuntimeException(
658
                    'Failed to reset Index Queue properties for site ' . $site->getLabel(),
659
                    1412986604
660
                );
661
            }
662
663
            DatabaseUtility::transactionCommit();
664
        } catch (\RuntimeException $e) {
665
            DatabaseUtility::transactionRollback();
666
        }
667
    }
668
669
    /**
670
     * Removes all items from the Index Queue.
671
     *
672
     */
673
    public function deleteAllItems()
674
    {
675
        $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('tx_solr_indexqueue_item', '');
676
    }
677
678
    /**
679
     * Gets a single Index Queue item by its uid.
680
     *
681
     * @param integer $itemId Index Queue item uid
682
     * @return Item The request Index Queue item or NULL
683
     *      if no item with $itemId was found
684
     */
685
    public function getItem($itemId)
686
    {
687
        $item = null;
688
689
        $indexQueueItemRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
690
            '*',
691
            'tx_solr_indexqueue_item',
692
            'uid = ' . intval($itemId)
693
        );
694
695
        if (count($indexQueueItemRecord) == 1) {
696
            $indexQueueItemRecord = $indexQueueItemRecord[0];
697
698
            $item = GeneralUtility::makeInstance(
699
                'ApacheSolrForTypo3\\Solr\\IndexQueue\\Item',
700
                $indexQueueItemRecord
701
            );
702
        }
703
704
        return $item;
705
    }
706
707
    /**
708
     * Gets Index Queue items by type and uid.
709
     *
710
     * @param string $itemType item type, ususally  the table name
711
     * @param integer $itemUid item uid
712
     * @return array An array of items matching $itemType and $itemUid
713
     */
714
    public function getItems($itemType, $itemUid)
715
    {
716
        $indexQueueItemRecords = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
717
            '*',
718
            'tx_solr_indexqueue_item',
719
            'item_type = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($itemType,
720
                'tx_solr_indexqueue_item') .
721
            ' AND item_uid = ' . intval($itemUid)
722
        );
723
724
        return $this->getIndexQueueItemObjectsFromRecords($indexQueueItemRecords);
725
    }
726
727
    /**
728
     * Gets number of Index Queue items for a specific site / indexing configuration
729
     * optional parameter to limit the deleted items by indexing configuration.
730
     *
731
     * @param Site $site The site to search for.
732
     * @param string $indexingConfigurationName name of a specific indexing
733
     *      configuration
734
     * @return mixed Number of items (integer) or FALSE if something went
735
     *      wrong (boolean)
736
     */
737
    public function getItemsCountBySite(
738
        Site $site,
739
        $indexingConfigurationName = ''
740
    ) {
741
        $indexingConfigurationConstraint = '';
742
        if (!empty($indexingConfigurationName)) {
743
            $indexingConfigurationConstraint = ' AND indexing_configuration = \'' . $indexingConfigurationName . '\'';
744
        }
745
746
        $itemCount = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
747
            'uid',
748
            'tx_solr_indexqueue_item',
749
            'root = ' . $site->getRootPageId() . $indexingConfigurationConstraint
750
        );
751
752
        return $itemCount;
753
    }
754
755
    /**
756
     * Returns the number of items for all queues.
757
     *
758
     * @return integer
759
     */
760
    public function getAllItemsCount()
761
    {
762
        $db = $GLOBALS['TYPO3_DB'];
763
        /**  @var $db \TYPO3\CMS\Core\Database\DatabaseConnection */
764
        $itemCount = $db->exec_SELECTcountRows(
765
            'uid',
766
            'tx_solr_indexqueue_item'
767
        );
768
769
        return (int)$itemCount;
770
    }
771
772
    /**
773
     * Gets $limit number of items to index for a particular $site.
774
     *
775
     * @param Site $site TYPO3 site
776
     * @param integer $limit Number of items to get from the queue
777
     * @return Item[] Items to index to the given solr server
778
     */
779
    public function getItemsToIndex(Site $site, $limit = 50)
780
    {
781
        $itemsToIndex = array();
782
783
        // determine which items to index with this run
784
        $indexQueueItemRecords = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
785
            '*',
786
            'tx_solr_indexqueue_item',
787
            'root = ' . $site->getRootPageId() .
788
            ' AND changed > indexed' .
789
            ' AND changed <= ' . time() .
790
            ' AND errors = \'\'',
791
            '',
792
            'indexing_priority DESC, changed DESC, uid DESC',
793
            intval($limit)
794
        );
795
        if (!empty($indexQueueItemRecords)) {
796
            // convert queued records to index queue item objects
797
            $itemsToIndex = $this->getIndexQueueItemObjectsFromRecords($indexQueueItemRecords);
798
        }
799
800
        return $itemsToIndex;
801
    }
802
803
    /**
804
     * Creates an array of ApacheSolrForTypo3\Solr\IndexQueue\Item objects from an array of
805
     * index queue records.
806
     *
807
     * @param array $indexQueueItemRecords Array of plain index queue records
808
     * @return array Array of ApacheSolrForTypo3\Solr\IndexQueue\Item objects
809
     */
810
    protected function getIndexQueueItemObjectsFromRecords(
811
        array $indexQueueItemRecords
812
    ) {
813
        $indexQueueItems = array();
814
        $tableUids = array();
815
        $tableRecords = array();
816
817
        // grouping records by table
818
        foreach ($indexQueueItemRecords as $indexQueueItemRecord) {
819
            $tableUids[$indexQueueItemRecord['item_type']][] = $indexQueueItemRecord['item_uid'];
820
        }
821
822
        // fetching records by table, saves us a lot of single queries
823
        foreach ($tableUids as $table => $uids) {
824
            $uidList = implode(',', $uids);
825
            $records = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
826
                '*',
827
                $table,
828
                'uid IN(' . $uidList . ')',
829
                '', '', '', // group, order, limit
830
                'uid'
831
            );
832
            $tableRecords[$table] = $records;
833
834
            if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessFetchRecordsForIndexQueueItem'])) {
835
                $params = ['table' => $table, 'uids' => $uids, 'tableRecords' => &$tableRecords];
836
                foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessFetchRecordsForIndexQueueItem'] as $reference) {
837
                    GeneralUtility::callUserFunction($reference, $params, $this);
838
                }
839
                unset($params);
840
            }
841
        }
842
843
        // creating index queue item objects and assigning / mapping
844
        // records to index queue items
845
        foreach ($indexQueueItemRecords as $indexQueueItemRecord) {
846
            if (isset($tableRecords[$indexQueueItemRecord['item_type']][$indexQueueItemRecord['item_uid']])) {
847
                $indexQueueItems[] = GeneralUtility::makeInstance(
848
                    'ApacheSolrForTypo3\\Solr\\IndexQueue\\Item',
849
                    $indexQueueItemRecord,
850
                    $tableRecords[$indexQueueItemRecord['item_type']][$indexQueueItemRecord['item_uid']]
851
                );
852
            } else {
853
                GeneralUtility::devLog('Record missing for Index Queue item. Item removed.',
854
                    'solr', 3, array($indexQueueItemRecord));
855
                $this->deleteItem($indexQueueItemRecord['item_type'],
856
                    $indexQueueItemRecord['item_uid']);
857
            }
858
        }
859
860
        return $indexQueueItems;
861
    }
862
863
    /**
864
     * Marks an item as failed and causes the indexer to skip the item in the
865
     * next run.
866
     *
867
     * @param int|Item $item Either the item's Index Queue
868
     *      uid or the complete item
869
     * @param string $errorMessage Error message
870
     */
871
    public function markItemAsFailed($item, $errorMessage = '')
872
    {
873
        $itemUid = 0;
0 ignored issues
show
Unused Code introduced by
$itemUid is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
874
875
        if ($item instanceof Item) {
876
            $itemUid = $item->getIndexQueueUid();
877
        } else {
878
            $itemUid = (int)$item;
879
        }
880
881
        if (empty($errorMessage)) {
882
            // simply set to "TRUE"
883
            $errorMessage = '1';
884
        }
885
886
        $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
887
            'tx_solr_indexqueue_item',
888
            'uid = ' . $itemUid,
889
            array(
890
                'errors' => $errorMessage
891
            )
892
        );
893
    }
894
}
895