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
|
|
|
use TYPO3\CMS\Core\Utility\MathUtility; |
17
|
|
|
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Index the given events. |
21
|
|
|
*/ |
22
|
|
|
class IndexerService extends AbstractService |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* Index table name. |
26
|
|
|
*/ |
27
|
|
|
const TABLE_NAME = 'tx_calendarize_domain_model_index'; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @var Dispatcher |
31
|
|
|
*/ |
32
|
|
|
protected $signalSlot; |
33
|
|
|
|
34
|
|
|
public function __construct() |
35
|
|
|
{ |
36
|
|
|
$this->signalSlot = GeneralUtility::makeInstance(Dispatcher::class); |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Reindex all elements. |
42
|
|
|
*/ |
43
|
|
|
public function reindexAll() |
44
|
|
|
{ |
45
|
|
|
$this->signalSlot->dispatch(__CLASS__, __FUNCTION__ . 'Pre', [$this]); |
46
|
|
|
|
47
|
|
|
$this->removeInvalidConfigurationIndex(); |
48
|
|
|
$q = HelperUtility::getDatabaseConnection(self::TABLE_NAME)->createQueryBuilder(); |
49
|
|
|
|
50
|
|
|
$q->getRestrictions() |
51
|
|
|
->removeAll() |
52
|
|
|
->add(GeneralUtility::makeInstance(DeletedRestriction::class)); |
53
|
|
|
|
54
|
|
|
foreach (Register::getRegister() as $key => $configuration) { |
55
|
|
|
$tableName = $configuration['tableName']; |
56
|
|
|
$this->removeInvalidRecordIndex($tableName); |
57
|
|
|
|
58
|
|
|
$q->resetQueryParts(); |
59
|
|
|
|
60
|
|
|
$transPointer = $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] ?? false; // e.g. l10n_parent |
61
|
|
|
|
62
|
|
|
if ($transPointer) { |
63
|
|
|
// Note: In loclized tables, it is important, that the "default language records" are indexed first, so the |
64
|
|
|
// overlays can connect with l10n_paretn to the right default record. |
65
|
|
|
$q->select('uid') |
66
|
|
|
->from($tableName) |
67
|
|
|
->orderBy((string) $transPointer); |
68
|
|
|
} else { |
69
|
|
|
$q->select('uid') |
70
|
|
|
->from($tableName); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
$rows = $q->execute()->fetchAll(); |
74
|
|
|
foreach ($rows as $row) { |
75
|
|
|
$this->updateIndex($key, $configuration['tableName'], $row['uid']); |
76
|
|
|
} |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
$this->signalSlot->dispatch(__CLASS__, __FUNCTION__ . 'Post', [$this]); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Reindex the given element. |
84
|
|
|
* |
85
|
|
|
* @param string $configurationKey |
86
|
|
|
* @param string $tableName |
87
|
|
|
* @param int $uid |
88
|
|
|
*/ |
89
|
|
|
public function reindex($configurationKey, $tableName, $uid) |
90
|
|
|
{ |
91
|
|
|
$dispatcher = GeneralUtility::makeInstance(Dispatcher::class); |
92
|
|
|
$dispatcher->dispatch(__CLASS__, __FUNCTION__ . 'Pre', [$configurationKey, $tableName, $uid, $this]); |
93
|
|
|
|
94
|
|
|
$this->removeInvalidConfigurationIndex(); |
95
|
|
|
$this->removeInvalidRecordIndex($tableName); |
96
|
|
|
$this->updateIndex($configurationKey, $tableName, $uid); |
97
|
|
|
|
98
|
|
|
$dispatcher->dispatch(__CLASS__, __FUNCTION__ . 'Post', [$configurationKey, $tableName, $uid, $this]); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Get index count. |
103
|
|
|
* |
104
|
|
|
* @param string $tableName |
105
|
|
|
* @param int $uid |
106
|
|
|
* |
107
|
|
|
* @return int |
108
|
|
|
*/ |
109
|
|
|
public function getIndexCount(string $tableName, $uid): int |
110
|
|
|
{ |
111
|
|
|
// Note: "uid" could be e.g. NEW6273482 in DataHandler process |
112
|
|
|
if (MathUtility::canBeInterpretedAsInteger($uid)) { |
113
|
|
|
return (int) $this->getCurrentItems($tableName, $uid)->rowCount(); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
return 0; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* Get the next events. |
121
|
|
|
* |
122
|
|
|
* @param string $table |
123
|
|
|
* @param int $uid |
124
|
|
|
* @param int $limit |
125
|
|
|
* |
126
|
|
|
* @return array|null |
127
|
|
|
*/ |
128
|
|
|
public function getNextEvents($table, $uid, $limit = 5) |
129
|
|
|
{ |
130
|
|
|
$q = HelperUtility::getDatabaseConnection($table)->createQueryBuilder(); |
131
|
|
|
$now = DateTimeUtility::getNow(); |
132
|
|
|
$now->setTime(0, 0, 0); |
133
|
|
|
|
134
|
|
|
$q->getRestrictions() |
135
|
|
|
->removeAll() |
136
|
|
|
->add(GeneralUtility::makeInstance(DeletedRestriction::class)); |
137
|
|
|
|
138
|
|
|
$q->select('*') |
139
|
|
|
->from(self::TABLE_NAME) |
140
|
|
|
->where( |
141
|
|
|
$q->expr()->andX( |
142
|
|
|
$q->expr()->gte('start_date', $now->getTimestamp()), |
143
|
|
|
$q->expr()->eq('foreign_table', $q->createNamedParameter($table)), |
144
|
|
|
$q->expr()->eq('foreign_uid', $q->createNamedParameter((int) $uid, \PDO::PARAM_INT)) |
145
|
|
|
) |
146
|
|
|
) |
147
|
|
|
->addOrderBy('start_date', 'ASC') |
148
|
|
|
->addOrderBy('start_time', 'ASC') |
149
|
|
|
->setMaxResults($limit); |
150
|
|
|
|
151
|
|
|
return $q->execute()->fetchAll(); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Build the index for one element. |
156
|
|
|
* |
157
|
|
|
* @param string $configurationKey |
158
|
|
|
* @param string $tableName |
159
|
|
|
* @param int $uid |
160
|
|
|
*/ |
161
|
|
|
protected function updateIndex($configurationKey, $tableName, $uid) |
162
|
|
|
{ |
163
|
|
|
/** @var $preparationService IndexPreparationService */ |
164
|
|
|
static $preparationService = null; |
165
|
|
|
if (null === $preparationService) { |
166
|
|
|
$preparationService = GeneralUtility::makeInstance(IndexPreparationService::class); |
167
|
|
|
} |
168
|
|
|
$neededItems = $preparationService->prepareIndex($configurationKey, $tableName, $uid); |
169
|
|
|
$this->insertAndUpdateNeededItems($neededItems, $tableName, $uid); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Get the current items (ignore enable fields). |
174
|
|
|
* |
175
|
|
|
* @param string $tableName |
176
|
|
|
* @param int $uid |
177
|
|
|
* |
178
|
|
|
* @return \Doctrine\DBAL\Driver\Statement|int |
179
|
|
|
*/ |
180
|
|
|
protected function getCurrentItems(string $tableName, int $uid) |
181
|
|
|
{ |
182
|
|
|
$q = HelperUtility::getDatabaseConnection(self::TABLE_NAME)->createQueryBuilder(); |
183
|
|
|
$q->getRestrictions() |
184
|
|
|
->removeAll() |
185
|
|
|
->add(GeneralUtility::makeInstance(DeletedRestriction::class)); |
186
|
|
|
$q->resetQueryParts(); |
187
|
|
|
$q->select('*') |
188
|
|
|
->from(self::TABLE_NAME) |
189
|
|
|
->where($q->expr()->eq('foreign_table', $q->quote($tableName)), $q->expr()->eq('foreign_uid', $uid)); |
190
|
|
|
|
191
|
|
|
return $q->execute(); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* Insert and/or update the needed index records. |
196
|
|
|
* |
197
|
|
|
* @param array $neededItems |
198
|
|
|
* @param string $tableName |
199
|
|
|
* @param int $uid |
200
|
|
|
*/ |
201
|
|
|
protected function insertAndUpdateNeededItems(array $neededItems, $tableName, $uid) |
202
|
|
|
{ |
203
|
|
|
$databaseConnection = HelperUtility::getDatabaseConnection($tableName); |
204
|
|
|
$currentItems = $this->getCurrentItems($tableName, $uid)->fetchAll(); |
205
|
|
|
|
206
|
|
|
$this->signalSlot->dispatch(__CLASS__, __FUNCTION__ . 'Pre', [$neededItems, $tableName, $uid]); |
207
|
|
|
|
208
|
|
|
foreach ($neededItems as $neededKey => $neededItem) { |
209
|
|
|
$remove = false; |
210
|
|
|
foreach ($currentItems as $currentKey => $currentItem) { |
211
|
|
|
if (ArrayUtility::isEqualArray($neededItem, $currentItem)) { |
212
|
|
|
$remove = true; |
213
|
|
|
unset($neededItems[$neededKey], $currentItems[$currentKey]); |
214
|
|
|
|
215
|
|
|
break; |
216
|
|
|
} |
217
|
|
|
} |
218
|
|
|
if ($remove) { |
219
|
|
|
continue; |
220
|
|
|
} |
221
|
|
|
} |
222
|
|
|
foreach ($currentItems as $item) { |
223
|
|
|
$databaseConnection->delete(self::TABLE_NAME, ['uid' => $item['uid']]); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
$neededItems = \array_values($neededItems); |
227
|
|
|
if ($neededItems) { |
|
|
|
|
228
|
|
|
$databaseConnection->bulkInsert(self::TABLE_NAME, $neededItems, \array_keys($neededItems[0])); |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Remove Index items of the given table of records |
234
|
|
|
* that are deleted or do not exists anymore. |
235
|
|
|
* |
236
|
|
|
* @param string $tableName |
237
|
|
|
*/ |
238
|
|
|
protected function removeInvalidRecordIndex($tableName) |
239
|
|
|
{ |
240
|
|
|
$q = HelperUtility::getDatabaseConnection($tableName)->createQueryBuilder(); |
241
|
|
|
$q->getRestrictions() |
242
|
|
|
->removeAll() |
243
|
|
|
->add(GeneralUtility::makeInstance(DeletedRestriction::class)); |
244
|
|
|
|
245
|
|
|
$q->select('uid') |
246
|
|
|
->from($tableName); |
247
|
|
|
|
248
|
|
|
$rows = $q->execute()->fetchAll(); |
249
|
|
|
|
250
|
|
|
$q->resetQueryParts()->resetRestrictions(); |
251
|
|
|
$q->delete(self::TABLE_NAME) |
252
|
|
|
->where( |
253
|
|
|
$q->expr()->eq('foreign_table', $q->createNamedParameter($tableName)) |
254
|
|
|
); |
255
|
|
|
|
256
|
|
|
$ids = []; |
257
|
|
|
foreach ($rows as $row) { |
258
|
|
|
$ids[] = $row['uid']; |
259
|
|
|
} |
260
|
|
|
if ($ids) { |
|
|
|
|
261
|
|
|
$q->andWhere( |
262
|
|
|
$q->expr()->notIn('foreign_uid', $ids) |
263
|
|
|
); |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
$q->execute(); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Remove index Items of configurations that are not valid anymore. |
271
|
|
|
* |
272
|
|
|
* @return bool |
273
|
|
|
*/ |
274
|
|
|
protected function removeInvalidConfigurationIndex() |
275
|
|
|
{ |
276
|
|
|
$db = HelperUtility::getDatabaseConnection(self::TABLE_NAME); |
277
|
|
|
$q = $db->createQueryBuilder(); |
278
|
|
|
|
279
|
|
|
$validKeys = \array_keys(Register::getRegister()); |
280
|
|
|
if ($validKeys) { |
|
|
|
|
281
|
|
|
foreach ($validKeys as $key => $value) { |
282
|
|
|
$validKeys[$key] = $q->createNamedParameter($value); |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
$q->delete(self::TABLE_NAME) |
286
|
|
|
->where( |
287
|
|
|
$q->expr()->notIn('unique_register_key', $validKeys) |
288
|
|
|
)->execute(); |
289
|
|
|
|
290
|
|
|
return (bool) $q->execute(); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
return (bool) $db->truncate(self::TABLE_NAME); |
294
|
|
|
} |
295
|
|
|
} |
296
|
|
|
|
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.