Completed
Push — master ( ed4971...ad2e84 )
by Tim
02:09
created

IndexerService::getNextEvents()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.584
c 0
b 0
f 0
cc 1
nc 1
nop 3
1
<?php
2
3
/**
4
 * Index the given events.
5
 */
6
declare(strict_types=1);
7
8
namespace HDNET\Calendarize\Service;
9
10
use HDNET\Calendarize\Register;
11
use HDNET\Calendarize\Utility\ArrayUtility;
12
use HDNET\Calendarize\Utility\DateTimeUtility;
13
use HDNET\Calendarize\Utility\HelperUtility;
14
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
15
use TYPO3\CMS\Core\Utility\GeneralUtility;
16
17
/**
18
 * Index the given events.
19
 */
20
class IndexerService extends AbstractService
21
{
22
    /**
23
     * Index table name.
24
     */
25
    const TABLE_NAME = 'tx_calendarize_domain_model_index';
26
27
    /**
28
     * Reindex all elements.
29
     */
30
    public function reindexAll()
31
    {
32
        $this->removeInvalidConfigurationIndex();
33
        $q = HelperUtility::getDatabaseConnection(self::TABLE_NAME)->createQueryBuilder();
34
35
        $q->getRestrictions()
36
            ->removeAll()
37
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
38
39
        foreach (Register::getRegister() as $key => $configuration) {
40
            $tableName = $configuration['tableName'];
41
            $this->removeInvalidRecordIndex($tableName);
42
43
            $q->resetQueryParts();
44
45
            $transPointer = $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] ?? false; // e.g. l10n_parent
46
47
            if ($transPointer) {
48
                // Note: In loclized tables, it is important, that the "default language records" are indexed first, so the
49
                // overlays can connect with l10n_paretn to the right default record.
50
                $q->select('uid')
51
                    ->from($tableName)
52
                    ->orderBy((string) $transPointer);
53
            } else {
54
                $q->select('uid')
55
                    ->from($tableName);
56
            }
57
58
            $rows = $q->execute()->fetchAll();
59
            foreach ($rows as $row) {
60
                $this->updateIndex($key, $configuration['tableName'], $row['uid']);
61
            }
62
        }
63
    }
64
65
    /**
66
     * Reindex the given element.
67
     *
68
     * @param string $configurationKey
69
     * @param string $tableName
70
     * @param int    $uid
71
     */
72
    public function reindex($configurationKey, $tableName, $uid)
73
    {
74
        $this->removeInvalidConfigurationIndex();
75
        $this->removeInvalidRecordIndex($tableName);
76
        $this->updateIndex($configurationKey, $tableName, $uid);
77
    }
78
79
    /**
80
     * Get index count.
81
     *
82
     * @param $table
83
     * @param $uid
84
     *
85
     * @return mixed
86
     */
87
    public function getIndexCount($table, $uid)
88
    {
89
        $databaseConnection = HelperUtility::getDatabaseConnection($table);
90
91
        return $databaseConnection->count('*', self::TABLE_NAME, [
92
            'foreign_table' => $table,
93
            'foreign_uid' => (int) $uid,
94
        ]);
95
    }
96
97
    /**
98
     * Get the next events.
99
     *
100
     * @param string $table
101
     * @param int    $uid
102
     * @param int    $limit
103
     *
104
     * @return array|null
105
     */
106
    public function getNextEvents($table, $uid, $limit = 5)
107
    {
108
        $q = HelperUtility::getDatabaseConnection($table)->createQueryBuilder();
109
        $now = DateTimeUtility::getNow();
110
        $now->setTime(0, 0, 0);
111
112
        $q->select('*')
113
            ->from(self::TABLE_NAME)
114
            ->where(
115
                $q->expr()->andX(
116
                    $q->expr()->gte('start_date', $now->getTimestamp()),
117
                    $q->expr()->eq('foreign_table', $q->createNamedParameter($table)),
118
                    $q->expr()->eq('foreign_uid', $q->createNamedParameter((int) $uid, \PDO::PARAM_INT))
119
                )
120
            )
121
            ->addOrderBy('start_date', 'ASC')
122
            ->addOrderBy('start_time', 'ASC')
123
            ->setMaxResults($limit);
124
125
        return $q->execute()->fetchAll();
126
    }
127
128
    /**
129
     * Build the index for one element.
130
     *
131
     * @param string $configurationKey
132
     * @param string $tableName
133
     * @param int    $uid
134
     */
135
    protected function updateIndex($configurationKey, $tableName, $uid)
136
    {
137
        /** @var $preparationService IndexPreparationService */
138
        static $preparationService = null;
139
        if (null === $preparationService) {
140
            $preparationService = GeneralUtility::makeInstance(IndexPreparationService::class);
141
        }
142
        // @todo Handle Workspace IDs?
143
        $neededItems = $preparationService->prepareIndex($configurationKey, $tableName, $uid);
144
        $this->insertAndUpdateNeededItems($neededItems, $tableName, $uid);
145
    }
146
147
    /**
148
     * Insert and/or update the needed index records.
149
     *
150
     * @param array  $neededItems
151
     * @param string $tableName
152
     * @param int    $uid
153
     *
154
     * @todo Handle Workspace IDs?
155
     */
156
    protected function insertAndUpdateNeededItems(array $neededItems, $tableName, $uid)
157
    {
158
        $databaseConnection = HelperUtility::getDatabaseConnection($tableName);
159
160
        $currentItems = $databaseConnection->select(['*'], self::TABLE_NAME, [
161
            'foreign_table' => $tableName,
162
            'foreign_uid' => $uid,
163
        ])->fetchAll();
164
165
        foreach ($neededItems as $neededKey => $neededItem) {
166
            $remove = false;
167
            foreach ($currentItems as $currentKey => $currentItem) {
168
                if (ArrayUtility::isEqualArray($neededItem, $currentItem)) {
169
                    $remove = true;
170
                    unset($neededItems[$neededKey], $currentItems[$currentKey]);
171
172
                    break;
173
                }
174
            }
175
            if ($remove) {
176
                continue;
177
            }
178
        }
179
        foreach ($currentItems as $item) {
180
            $databaseConnection->delete(self::TABLE_NAME, ['uid' => $item['uid']]);
181
        }
182
183
        $neededItems = \array_values($neededItems);
184
        if ($neededItems) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $neededItems 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...
185
            $databaseConnection->bulkInsert(self::TABLE_NAME, $neededItems, \array_keys($neededItems[0]));
186
        }
187
    }
188
189
    /**
190
     * Remove Index items of the given table of records
191
     * that are deleted or do not exists anymore.
192
     *
193
     * @param string $tableName
194
     */
195
    protected function removeInvalidRecordIndex($tableName)
196
    {
197
        $q = HelperUtility::getDatabaseConnection($tableName)->createQueryBuilder();
198
        $q->getRestrictions()
199
            ->removeAll()
200
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
201
202
        $q->select('uid')
203
            ->from($tableName);
204
205
        $rows = $q->execute()->fetchAll();
206
207
        $q->resetQueryParts()->resetRestrictions();
208
        $q->delete(self::TABLE_NAME)
209
            ->where(
210
                $q->expr()->eq('foreign_table', $q->createNamedParameter($tableName))
211
            );
212
213
        $ids = [];
214
        foreach ($rows as $row) {
215
            $ids[] = $row['uid'];
216
        }
217
        if ($ids) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ids 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...
218
            $q->andWhere(
219
                $q->expr()->notIn('foreign_uid', $ids)
220
            );
221
        }
222
223
        $q->execute();
224
    }
225
226
    /**
227
     * Remove index Items of configurations that are not valid anymore.
228
     *
229
     * @return bool
230
     */
231
    protected function removeInvalidConfigurationIndex()
232
    {
233
        $db = HelperUtility::getDatabaseConnection(self::TABLE_NAME);
234
        $q = $db->createQueryBuilder();
235
236
        $validKeys = \array_keys(Register::getRegister());
237
        if ($validKeys) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $validKeys of type array<integer|string> 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...
238
            foreach ($validKeys as $key => $value) {
239
                $validKeys[$key] = $q->createNamedParameter($value);
240
            }
241
242
            $q->delete(self::TABLE_NAME)
243
                ->where(
244
                    $q->expr()->notIn('unique_register_key', $validKeys)
245
                )->execute();
246
247
            return (bool) $q->execute();
248
        }
249
250
        return (bool) $db->truncate(self::TABLE_NAME);
251
    }
252
}
253