Completed
Push — master ( 32cbdd...f2f8a7 )
by Tim
16s queued 11s
created

CalMigrationUpdate   B

Complexity

Total Complexity 9

Size/Duplication

Total Lines 1191
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 9
dl 0
loc 1191
rs 8.836
c 0
b 0
f 0

28 Methods

Rating   Name   Duplication   Size   Complexity  
A hp$0 ➔ buildExpression() 0 16 4
A getIdentifier() 0 4 1
A checkForUpdate() 0 12 4
A executeUpdate() 0 28 4
A isCalWithSysCategories() 0 15 1
B performCalEventUpdate() 0 90 5
A performExceptionEventUpdate() 0 39 2
B performLinkEventToConfigurationGroup() 0 55 3
A performSysFileReferenceUpdate() 0 46 3
A performLinkEventToCategory() 0 44 4
B performLinkEventToSysCategory() 0 63 3
A updateEventWithConfiguration() 0 30 3
A addValueToCsv() 0 14 2
A addConfigurationIdToEvent() 0 11 2
A updateEvent() 0 28 2
A findEventByImportId() 0 22 1
A findEventExcludeConfiguration() 0 30 2
B getExceptionConfigurationForExceptionGroup() 0 75 8
A mapFrequency() 0 16 2
B performSysCategoryUpdate() 0 80 3
A getSysCategoryParentUid() 0 17 1
A getCalendarizeEventUid() 0 29 1
A getCalendarizeCategoryUid() 0 24 1
A buildConfigurations() 0 52 4
B getNonMigratedCalIds() 0 58 5
A updateNecessary() 0 12 1
A getPrerequisites() 0 6 1
A getQueryBuilder() 0 34 5
1
<?php
2
3
/**
4
 * CalMigrationUpdate.
5
 */
6
declare(strict_types=1);
7
8
namespace HDNET\Calendarize\Updates;
9
10
use HDNET\Autoloader\Annotation\SignalClass;
11
use HDNET\Autoloader\Annotation\SignalName;
12
use HDNET\Calendarize\Service\IndexerService;
13
use HDNET\Calendarize\Utility\HelperUtility;
14
use TYPO3\CMS\Core\Database\Connection;
15
use TYPO3\CMS\Core\Database\ConnectionPool;
16
use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression;
17
use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
18
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
19
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
20
use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionInterface;
21
use TYPO3\CMS\Core\Utility\GeneralUtility;
22
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
23
24
/**
25
 * CalMigrationUpdate.
26
 *
27
 * If using the slots please use the m with func_get_args!
28
 * Example:
29
 * /**
30
 *  * @SignalClass("HDNET\Calendarize\Updates\CalMigrationUpdate")
31
 *  * @SignalName("getCalendarizeEventUid")
32
 *
33
 *  *@return array
34
 *  *
35
 * public function getCalendarizeEventUid()
36
 * {
37
 *    $args = func_get_args();
38
 *    list($table, $dbQueries, $call) = $args;
39
 *
40
 *    $variables = [
41
 *        'table'     => self::EVENT_TABLE,
42
 *        'dbQueries' => $dbQueries
43
 *    ];
44
 *
45
 *    return $variables;
46
 * }
47
 */
48
class CalMigrationUpdate extends AbstractUpdate
49
{
50
    /**
51
     * Import prefix.
52
     */
53
    const IMPORT_PREFIX = 'calMigration:';
54
55
    /**
56
     * Event table.
57
     */
58
    const EVENT_TABLE = 'tx_calendarize_domain_model_event';
59
60
    /**
61
     * Configuration table.
62
     */
63
    const CONFIGURATION_TABLE = 'tx_calendarize_domain_model_configuration';
64
65
    /**
66
     * ConfigurationGroup table.
67
     */
68
    const CONFIGURATION_GROUP_TABLE = 'tx_calendarize_domain_model_configurationgroup';
69
70
    /**
71
     * The human-readable title of the upgrade wizard.
72
     *
73
     * @var string
74
     */
75
    protected $title = 'Migrate cal event structures to the new calendarize event structures.
76
    Try to migrate all cal information and place the new calendarize event models in the same folder
77
    as the cal-records. Please note: the migration will be create calendarize default models.';
78
79
    public function getIdentifier(): string
80
    {
81
        return 'calendarize_calMigration';
82
    }
83
84
    /**
85
     * Checks whether updates are required.
86
     *
87
     * @param string &$description The description for the update
88
     *
89
     * @return bool Whether an update is required (TRUE) or not (FALSE)
90
     */
91
    public function checkForUpdate(&$description)
92
    {
93
        $nonMigratedCalIds = $this->getNonMigratedCalIds();
94
        $count = \count($nonMigratedCalIds);
95
        if (0 === $count) {
96
            return false;
97
        }
98
        $description = 'There ' . ($count > 1 ? 'are ' . $count : 'is ' . $count) . ' non migrated EXT:cal event
99
        ' . ($count > 1 ? 's' : '') . '. Run the update process to migrate the events to EXT:calendarize events.';
100
101
        return true;
102
    }
103
104
    /**
105
     * Performs the accordant updates.
106
     *
107
     * @param array &$dbQueries      Queries done in this update
108
     * @param mixed &$customMessages Custom messages
109
     *
110
     * @return bool Whether everything went smoothly or not
111
     */
112
    public function executeUpdate(): bool
113
    {
114
        $calIds = $this->getNonMigratedCalIds();
115
        if (empty($calIds)) {
116
            return true;
117
        }
118
        $dbQueries = [];
119
120
        /**
121
         * @var bool
122
         */
123
        $calUsesSysCategories = $this->isCalWithSysCategories();
124
125
        if (!$calUsesSysCategories) {
126
            $this->performSysCategoryUpdate($calIds, $dbQueries, $customMessages);
127
        }
128
        $this->performSysFileReferenceUpdate($calIds, $dbQueries, $customMessages);
129
        $this->performExceptionEventUpdate($calIds, $dbQueries, $customMessages);
130
        $this->performCalEventUpdate($calIds, $dbQueries, $customMessages);
131
        if ($calUsesSysCategories) {
132
            $this->performLinkEventToSysCategory($calIds, $dbQueries, $customMessages);
133
        } else {
134
            $this->performLinkEventToCategory($calIds, $dbQueries, $customMessages);
135
        }
136
        $this->performLinkEventToConfigurationGroup($calIds, $dbQueries, $customMessages);
137
138
        return true;
139
    }
140
141
    /**
142
     * Check if cal is already using sys_category instead
143
     * of tx_cal_category. This affects the conversion of
144
     * categories and category / event relations.
145
     *
146
     * @return bool
147
     */
148
    protected function isCalWithSysCategories(): bool
149
    {
150
        $table = 'sys_category_record_mm';
151
152
        $q = $this->getQueryBuilder($table);
153
        $count = (int)$q->count('*')
154
            ->from($table)
155
            ->where(
156
                $q->expr()->eq('tablenames', $q->createNamedParameter('tx_cal_event')),
157
                $q->expr()->eq('fieldname', $q->createNamedParameter('category_id'))
158
            )
159
            ->execute();
160
161
        return $count > 0;
162
    }
163
164
    /**
165
     * Perform CAL event update.
166
     *
167
     * @param       $calIds
168
     * @param array $dbQueries
169
     * @param       $customMessages
170
     *
171
     * @return bool
172
     */
173
    public function performCalEventUpdate($calIds, array &$dbQueries, &$customMessages)
174
    {
175
        $table = 'tx_cal_event';
176
        $q = $this->getQueryBuilder($table);
177
178
        $locationTable = 'tx_cal_location';
179
        $locations = $this->getQueryBuilder($locationTable)
180
            ->select('*')
181
            ->from($locationTable)
182
            ->execute()
183
            ->fetchAll();
184
        $locationByUid = [];
185
        foreach ($locations as $location) {
186
            $locationByUid[$location['uid']] = $location['name'];
187
        }
188
189
        $events = $q->select('*')
190
            ->from($table)
191
            ->where(
192
                $q->expr()->in('uid', $q->createNamedParameter($calIds, Connection::PARAM_INT_ARRAY))
193
            )
194
            ->orderBy('l18n_parent')
195
            ->execute()->fetchAll();
196
197
        foreach ($events as $event) {
198
            // Get the parent id of the event record
199
            // Note: The parent record should exist at this time, since we orderBy('l18n_parent')
200
            $parentEventUid = 0;
201
            if ($event['l18n_parent']) {
202
                $parentEventUid = (int)$this->getCalendarizeEventUid(
203
                    self::IMPORT_PREFIX . $event['l18n_parent'],
204
                    $dbQueries,
205
                    $customMessages
206
                );
207
            }
208
209
            $calendarizeEventRecord = [
210
                'pid' => $event['pid'],
211
                'import_id' => self::IMPORT_PREFIX . (int)$event['uid'],
212
                'sys_language_uid' => $event['sys_language_uid'] ?? 0,
213
                'l10n_parent' => $parentEventUid,
214
                'tstamp' => $event['tstamp'],
215
                'crdate' => $event['crdate'],
216
                'hidden' => $event['hidden'],
217
                'starttime' => $event['starttime'],
218
                'endtime' => $event['endtime'],
219
                'title' => $event['title'],
220
                'organizer' => $event['organizer'],
221
                'location' => $event['location_id'] > 0 ? $locationByUid[$event['location_id']] : $event['location'],
222
                'abstract' => $event['teaser'],
223
                'description' => $event['description'],
224
                'images' => (int)$event['image'],
225
                'downloads' => (int)$event['attachment'],
226
                'calendarize' => $this->buildConfigurations($event, $dbQueries),
227
            ];
228
229
            $variables = [
230
                'calendarizeEventRecord' => $calendarizeEventRecord,
231
                'event' => $event,
232
                'table' => self::EVENT_TABLE,
233
                'dbQueries' => $dbQueries,
234
            ];
235
236
            $dispatcher = HelperUtility::getSignalSlotDispatcher();
237
            $variables = $dispatcher->dispatch(__CLASS__, __FUNCTION__ . 'PreInsert', $variables);
238
239
            $db = HelperUtility::getDatabaseConnection($variables['table']);
240
            $q = $db->createQueryBuilder();
241
            $q->insert($variables['table'])->values($variables['calendarizeEventRecord']);
242
            $dbQueries[] = $q->getSQL();
243
244
            $q->execute();
245
246
            $variablesPostInsert = [
247
                'calendarizeEventRecord' => $calendarizeEventRecord,
248
                'event' => $event,
249
                'table' => $variables['table'],
250
                'recordId' => $db->lastInsertId($variables['table']),
251
                'dbQueries' => $dbQueries,
252
            ];
253
254
            $dispatcher = HelperUtility::getSignalSlotDispatcher();
255
            $dispatcher->dispatch(__CLASS__, __FUNCTION__ . 'PostInsert', $variablesPostInsert);
256
        }
257
258
        $indexer = GeneralUtility::makeInstance(IndexerService::class);
259
        $indexer->reindexAll();
260
261
        return true;
262
    }
263
264
    /**
265
     * Perform exception event update.
266
     *
267
     * @param       $calIds
268
     * @param array $dbQueries
269
     * @param array $customMessages
270
     *
271
     * @return bool
272
     */
273
    public function performExceptionEventUpdate($calIds, &$dbQueries, &$customMessages)
274
    {
275
        $table = 'tx_cal_exception_event_group';
276
        // ConfigurationGroup für jede ExceptionGroup
277
278
        $variables = [
279
            'table' => $table,
280
            'dbQueries' => $dbQueries,
281
            'calIds' => $calIds,
282
        ];
283
284
        $q = $this->getQueryBuilder($table);
285
286
        $q->select('*')->from($variables['table']);
287
288
        $selectResults = $q->execute()->fetchAll();
289
        $dbQueries[] = $q->getSQL();
290
291
        foreach ($selectResults as $selectResult) {
292
            $group = [
293
                'pid' => $selectResult['pid'],
294
                'tstamp' => $selectResult['tstamp'],
295
                'crdate' => $selectResult['crdate'],
296
                'cruser_id' => $selectResult['cruser_id'],
297
                'title' => $selectResult['title'],
298
                'configurations' => $this->getExceptionConfigurationForExceptionGroup($selectResult['uid'], $dbQueries),
299
                'hidden' => $selectResult['hidden'],
300
                'import_id' => self::IMPORT_PREFIX . $selectResult['uid'],
301
            ];
302
303
            $q = $this->getQueryBuilder($table);
304
            $q->insert(self::CONFIGURATION_GROUP_TABLE)->values($group);
305
            $dbQueries[] = $q->getSQL();
306
307
            $q->execute();
308
        }
309
310
        return true;
311
    }
312
313
    /**
314
     * Perform link event to configuration group.
315
     *
316
     * @param $calIds
317
     * @param $dbQueries
318
     * @param $customMessages
319
     *
320
     * @return bool
321
     */
322
    public function performLinkEventToConfigurationGroup($calIds, &$dbQueries, &$customMessages)
323
    {
324
        $q = $this->getQueryBuilder(self::CONFIGURATION_GROUP_TABLE);
325
        $now = new \DateTime();
326
327
        $variables = [
328
            'table' => self::CONFIGURATION_GROUP_TABLE,
329
            'dbQueries' => $dbQueries,
330
            'calIds' => $calIds,
331
        ];
332
333
        $selectResults = $q->select('*')->from($variables['table'])->execute()->fetchAll();
334
        $dbQueries[] = $q->getSQL();
335
336
        foreach ($selectResults as $group) {
337
            $importId = explode(':', $group['import_id']);
338
            $groupId = (int)$importId[1];
339
340
            $variables = [
341
                'table' => 'tx_cal_exception_event_mm',
342
                'dbQueries' => $dbQueries,
343
                'calIds' => $calIds,
344
            ];
345
346
            $q = $this->getQueryBuilder($variables['table']);
347
348
            $q->select('uid_local')
349
                ->from($variables['table'])
350
                ->where(
351
                    $q->expr()->andX(
352
                        $q->expr()->eq('tablenames', $q->createNamedParameter('tx_cal_exception_event_group')),
353
                        $q->expr()->eq('uid_foreign', $q->createNamedParameter((int)$groupId, \PDO::PARAM_INT))
354
                    )
355
                );
356
357
            $dbQueries[] = $q->getSQL();
358
            $selectResults = $q->execute()->fetchAll();
359
360
            foreach ($selectResults as $eventUid) {
361
                $eventImportId = self::IMPORT_PREFIX . (int)$eventUid['uid_local'];
362
                $configurationRow = [
363
                    'pid' => (int)$group['pid'],
364
                    'tstamp' => $now->getTimestamp(),
365
                    'crdate' => $now->getTimestamp(),
366
                    'type' => 'group',
367
                    'handling' => 'exclude',
368
                    'groups' => $group['uid'],
369
                ];
370
371
                $this->updateEventWithConfiguration($eventImportId, $configurationRow, $dbQueries, $customMessages);
372
            }
373
        }
374
375
        return true;
376
    }
377
378
    /**
379
     * Migrate the 'sys_file_reference' entries from 'tx_cal_event' to 'tx_calendarize_domain_model_event'.
380
     * Mark the imported entries with the import-id.
381
     *
382
     * @param       $calIds
383
     * @param array $dbQueries
384
     * @param       $customMessages
385
     */
386
    public function performSysFileReferenceUpdate($calIds, array &$dbQueries, &$customMessages)
387
    {
388
        $q = $this->getQueryBuilder('tx_cal_event');
389
390
        $variables = [
391
            'table' => 'tx_cal_event',
392
            'fieldnames' => ['image', 'attachment'],
393
            'dbQueries' => $dbQueries,
394
            'calIds' => $calIds,
395
        ];
396
397
        // select all not migrated entries
398
        $fieldnames = 'fieldname = \'' . implode('\' OR fieldname = \'', $variables['fieldnames']) . '\'';
399
        $selectWhere = 'tablenames = \'' . $variables['table'] . '\' AND (' . $fieldnames . ')';
400
        $selectWhere .= ' AND NOT EXISTS (SELECT NULL FROM sys_file_reference sfr2 WHERE sfr2.import_id = CONCAT(\'' . self::IMPORT_PREFIX . '\', sfr1.uid))';
401
402
        $q->select('*')
403
            ->from('sys_file_reference', 'sfr1')
404
            ->where($selectWhere);
405
406
        $dbQueries[] = $q->getSQL();
407
        $selectResults = $q->execute()->fetchAll();
408
409
        $variables = [
410
            'table' => self::EVENT_TABLE,
411
            'fieldnames' => $variables['fieldnames'],
412
            'dbQueries' => $dbQueries,
413
            'calIds' => $calIds,
414
            'selectResults' => $selectResults,
415
        ];
416
417
        // create new entry with import_id
418
        foreach ($variables['selectResults'] as $selectResult) {
419
            $selectResult['tablenames'] = $variables['table'];
420
            $selectResult['import_id'] = self::IMPORT_PREFIX . $selectResult['uid'];
421
            $selectResult['fieldname'] = ('image' === $selectResult['fieldname']) ? 'images' : 'downloads';
422
            unset($selectResult['uid_foreign'], $selectResult['uid']);
423
424
            $q = $this->getQueryBuilder('sys_file_reference');
425
            $q->insert('sys_file_reference')->values($selectResult);
426
427
            $dbQueries[] = $q->getSQL();
428
429
            $q->execute();
430
        }
431
    }
432
433
    /**
434
     * Link the Events to the migrated Categories.
435
     * This build up the 'sys_category_record_mm' table on base of the 'tx_cal_event_category_mm' table.
436
     *
437
     * @param       $calIds
438
     * @param array $dbQueries
439
     * @param array $customMessages
440
     */
441
    public function performLinkEventToCategory($calIds, &$dbQueries, &$customMessages)
442
    {
443
        $table = 'tx_cal_event_category_mm';
444
445
        $q = $this->getQueryBuilder($table);
446
447
        $q->select('*')->from($table);
448
        $dbQueries[] = $q->getSQL();
449
450
        $selectResults = $q->execute()->fetchAll();
451
452
        $variables = [
453
            'tablenames' => self::EVENT_TABLE,
454
            'fieldname' => 'categories',
455
            'dbQueries' => $dbQueries,
456
        ];
457
458
        $dispatcher = HelperUtility::getSignalSlotDispatcher();
459
        $variables = $dispatcher->dispatch(__CLASS__, __FUNCTION__, $variables);
460
461
        foreach ($selectResults as $mm) {
462
            $eventUid = (int)$this->getCalendarizeEventUid(self::IMPORT_PREFIX . $mm['uid_local'], $dbQueries, $customMessages);
463
            $categoryUid = (int)$this->getCalendarizeCategoryUid(
464
                self::IMPORT_PREFIX . $mm['uid_foreign'],
465
                $dbQueries,
466
                $customMessages
467
            );
468
469
            if (0 !== $eventUid && 0 !== $categoryUid) {
470
                $insertValues = [
471
                    'uid_local' => $categoryUid,
472
                    'uid_foreign' => $eventUid,
473
                    'tablenames' => $variables['tablenames'],
474
                    'fieldname' => $variables['fieldname'],
475
                ];
476
477
                $q = $this->getQueryBuilder($table);
478
                $q->insert($table)->values($insertValues);
479
                $dbQueries[] = $q->getSQL();
480
481
                $q->execute();
482
            }
483
        }
484
    }
485
486
    /**
487
     * Link the Events to the migrated categories.
488
     *
489
     * This uses the existing 'sys_category_record_mm' table which links tx_cal_event to sys_category.
490
     * The fields must be updated to use tx_calendarize_domain_model_event instead.
491
     * Additionally, the uid_foreign must be updated to point to the new event uid.
492
     *
493
     * Before: tablenames='tx_cal_event', fieldname='category_id'
494
     * After: tablenames='tx_calendarize_domain_model_event', fieldname='categories'
495
     *
496
     * @param       $calIds
497
     * @param array $dbQueries
498
     * @param array $customMessages
499
     */
500
    public function performLinkEventToSysCategory($calIds, &$dbQueries, &$customMessages)
501
    {
502
        $table = 'sys_category_record_mm';
503
504
        $q = $this->getQueryBuilder($table);
505
506
        $q->select('uid_foreign')->from($table)
507
            ->where(
508
                $q->expr()->eq('tablenames', $q->createNamedParameter('tx_cal_event')),
509
                $q->expr()->eq('fieldname', $q->createNamedParameter('category_id')),
510
                $q->expr()->neq('uid_local', $q->createNamedParameter(0, \PDO::PARAM_INT)),
511
                $q->expr()->neq('uid_foreign', $q->createNamedParameter(0, \PDO::PARAM_INT))
512
            )->groupBy('uid_foreign');
513
514
        $selectResults = $q->execute()->fetchAll();
515
516
        $variables = [
517
            'tablenames' => self::EVENT_TABLE,
518
            'fieldname' => 'categories',
519
        ];
520
521
        $dispatcher = HelperUtility::getSignalSlotDispatcher();
522
        $variables = $dispatcher->dispatch(__CLASS__, __FUNCTION__, $variables);
523
524
        foreach ($selectResults as $mm) {
525
            $eventUidOld = (int)$mm['uid_foreign'];
526
            // event id is in uid_foreign
527
            $eventUid = (int)$this->getCalendarizeEventUid(self::IMPORT_PREFIX . $eventUidOld, $dbQueries, $customMessages);
528
529
            $q = $this->getQueryBuilder($table);
530
            if (0 !== $eventUid) {
531
                $q->update($table)
532
                    ->set('tablenames', $variables['tablenames'])
533
                    ->set('fieldname', $variables['fieldname'])
534
                    ->set('uid_foreign', $eventUid)
535
                    ->where(
536
                        $q->expr()->eq('uid_foreign', $q->createNamedParameter($eventUidOld, \PDO::PARAM_INT)),
537
                        $q->expr()->eq('tablenames', $q->createNamedParameter('tx_cal_event')),
538
                        $q->expr()->eq('fieldname', $q->createNamedParameter('category_id'))
539
                    )->execute();
540
            } else {
541
                $q->delete($table)
542
                    ->where(
543
                        $q->expr()->eq('uid_foreign', $q->createNamedParameter($eventUid, \PDO::PARAM_INT)),
544
                        $q->expr()->eq('tablenames', $q->createNamedParameter('tx_cal_event')),
545
                        $q->expr()->eq('fieldname', $q->createNamedParameter('category_id'))
546
                    )
547
                    ->execute();
548
            }
549
        }
550
551
        // delete remaining entries with insufficient values (e.g. uid_foreign=0)
552
        $q = $this->getQueryBuilder($table);
553
        $q->delete($table)
554
            ->where(
555
                $q->expr()->eq('tablenames', $q->createNamedParameter('tx_cal_event')),
556
                $q->expr()->orX(
557
                    $q->expr()->eq('fieldname', $q->createNamedParameter('')),
558
                    $q->expr()->eq('uid_local', $q->createNamedParameter(0, \PDO::PARAM_INT)),
559
                    $q->expr()->eq('uid_foreign', $q->createNamedParameter(0, \PDO::PARAM_INT))
560
                )
561
            )->execute();
562
    }
563
564
    /**
565
     * Update event with configuration.
566
     *
567
     * @param $eventImportId
568
     * @param $configuration
569
     * @param $dbQueries
570
     * @param $customMessages
571
     *
572
     * @return array
573
     */
574
    protected function updateEventWithConfiguration($eventImportId, $configuration, &$dbQueries, &$customMessages)
575
    {
576
        $configurationRow = $this->findEventExcludeConfiguration($eventImportId, $dbQueries, $customMessages);
577
        if ($configurationRow) {
578
            $configurationRow['groups'] = $this->addValueToCsv($configurationRow['groups'], $configuration['groups']);
579
580
            unset($configurationRow['uid']);
581
582
            $q = $this->getQueryBuilder(self::CONFIGURATION_GROUP_TABLE);
583
            $q->update(self::CONFIGURATION_GROUP_TABLE)
584
                ->where('uid', $q->createNamedParameter((int)$configuration['uid'], \PDO::PARAM_INT));
585
            foreach ($configurationRow as $key => $value) {
0 ignored issues
show
Bug introduced by
The expression $configurationRow of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
586
                $q->set($key, $value);
587
            }
588
589
            $dbQueries[] = $q->getSQL();
590
            $results = $q->execute();
591
        } else {
592
            $db = HelperUtility::getDatabaseConnection(self::CONFIGURATION_TABLE);
593
            $q = $db->createQueryBuilder();
594
            $q->insert(self::CONFIGURATION_TABLE)->values($configuration);
595
            $dbQueries[] = $q->getSQL();
596
597
            $configurationId = $db->lastInsertId(self::CONFIGURATION_TABLE);
598
599
            $results = $this->addConfigurationIdToEvent($eventImportId, $configurationId, $dbQueries, $customMessages);
600
        }
601
602
        return $results;
603
    }
604
605
    /**
606
     * Add Value to CSV.
607
     *
608
     * @param string $csv
609
     * @param string $value
610
     *
611
     * @return string
612
     */
613
    protected function addValueToCsv($csv, $value)
614
    {
615
        $csvArray = GeneralUtility::trimExplode(',', $csv);
616
617
        // check for doubles
618
        $values = array_flip($csvArray);
619
        if (isset($values[$value])) {
620
            return $csv;
621
        }
622
        $csvArray[] = $value;
623
        $csv = implode(',', $csvArray);
624
625
        return $csv;
626
    }
627
628
    /**
629
     * Add configuration ID to event.
630
     *
631
     * @param string $eventImportId
632
     * @param int    $configurationId
633
     * @param array  $dbQueries
634
     * @param array  $customMessages
635
     *
636
     * @return array|bool
637
     */
638
    protected function addConfigurationIdToEvent($eventImportId, $configurationId, &$dbQueries, &$customMessages)
639
    {
640
        $event = $this->findEventByImportId($eventImportId, $dbQueries, $customMessages);
641
        if (!$event) {
642
            return false;
643
        }
644
645
        $event['calendarize'] = $this->addValueToCsv($event['calendarize'], $configurationId);
646
647
        return $this->updateEvent($event['uid'], $event, $dbQueries, $customMessages);
648
    }
649
650
    /**
651
     * Update event.
652
     *
653
     * @param int   $eventId
654
     * @param array $values
655
     * @param array $dbQueries
656
     * @param array $customMessages
657
     *
658
     * @return array
659
     */
660
    protected function updateEvent($eventId, $values, &$dbQueries, &$customMessages)
661
    {
662
        $q = $this->getQueryBuilder(self::EVENT_TABLE);
663
664
        $variables = [
665
            'table' => self::EVENT_TABLE,
666
            'eventId' => (int)$eventId,
667
            'values' => $values,
668
            'dbQueries' => $dbQueries,
669
        ];
670
671
        $dispatcher = HelperUtility::getSignalSlotDispatcher();
672
        $variables = $dispatcher->dispatch(__CLASS__, __FUNCTION__, $variables);
673
674
        $q->update($variables['table'])
675
            ->where(
676
                $q->expr()->eq('uid', $q->createNamedParameter((int)$eventId, \PDO::PARAM_INT))
677
            );
678
        foreach ($variables['values'] as $key => $value) {
679
            $q->set($key, $value);
680
        }
681
682
        unset($values['uid']);
683
684
        $dbQueries[] = $q->getSQL();
685
686
        return $q->execute()->fetchAll();
687
    }
688
689
    /**
690
     * Find event by import ID.
691
     *
692
     * @param $eventImportId
693
     * @param $dbQueries
694
     * @param $customMessages
695
     *
696
     * @return array|bool
697
     */
698
    protected function findEventByImportId($eventImportId, &$dbQueries, &$customMessages)
699
    {
700
        $q = $this->getQueryBuilder(self::EVENT_TABLE);
701
702
        $variables = [
703
            'table' => self::EVENT_TABLE,
704
            'dbQueries' => $dbQueries,
705
            'eventImportId' => $eventImportId,
706
        ];
707
708
        $dispatcher = HelperUtility::getSignalSlotDispatcher();
709
        $variables = $dispatcher->dispatch(__CLASS__, __FUNCTION__, $variables);
710
711
        $q->select('*')->from($variables['table'])
712
            ->where(
713
                $q->expr()->eq('import_id', $q->createNamedParameter($eventImportId))
714
            );
715
716
        $dbQueries[] = $q->getSQL();
717
718
        return $q->execute()->fetchAll();
719
    }
720
721
    /**
722
     * Find event exclude configuration.
723
     *
724
     * @param string $eventImportId
725
     * @param array  $dbQueries
726
     * @param array  $customMessages
727
     *
728
     * @return array|bool
729
     */
730
    protected function findEventExcludeConfiguration($eventImportId, &$dbQueries, &$customMessages)
731
    {
732
        $event = $this->findEventByImportId($eventImportId, $dbQueries, $customMessages);
733
734
        if (!$event) {
735
            return false;
736
        }
737
738
        $variables = [
739
            'table' => self::CONFIGURATION_TABLE,
740
            'dbQueries' => $dbQueries,
741
            'event' => $event,
742
        ];
743
744
        $q = $this->getQueryBuilder($variables['table']);
745
746
        $q->select('*')
747
            ->from($variables['table'])
748
            ->where(
749
                $q->expr()->andX(
750
                    $q->expr()->eq('type', 'group'),
751
                    $q->expr()->eq('handling', 'exclude'),
752
                    $q->expr()->in('uid', $variables['event']['calendarize'])
753
                )
754
            );
755
756
        $dbQueries[] = $q->getSQL();
757
758
        return $q->execute()->fetchAll();
759
    }
760
761
    /**
762
     * Get exception configuration for exception group.
763
     *
764
     * @param       $groupId
765
     * @param array $dbQueries
766
     *
767
     * @return string
768
     */
769
    protected function getExceptionConfigurationForExceptionGroup($groupId, &$dbQueries)
770
    {
771
        $recordIds = [];
772
        $variables = [
773
            'table' => 'tx_cal_exception_event_group_mm',
774
            'dbQueries' => $dbQueries,
775
        ];
776
777
        $q = $this->getQueryBuilder($variables['table']);
778
779
        $q->select('*')
780
            ->from($variables['table'])
781
            ->where('uid_local', $q->createNamedParameter((int)$groupId, \PDO::PARAM_INT));
782
783
        $dbQueries[] = $q->getSQL();
784
785
        $mmResults = $q->execute()->fetchAll();
786
        foreach ($mmResults as $mmResult) {
787
            $variables = [
788
                'table' => 'tx_cal_exception_event',
789
                'dbQueries' => $dbQueries,
790
            ];
791
792
            $q = $this->getQueryBuilder($variables['table']);
793
            $q->select('*')
794
                ->from($variables['table'])
795
                ->where(
796
                    $q->expr()->eq('uid', $q->createNamedParameter((int)$mmResult['uid_foreign'], \PDO::PARAM_INT))
797
                );
798
799
            $dbQueries[] = $q->getSQL();
800
801
            $selectResults = $q->execute()->fetchAll();
802
803
            foreach ($selectResults as $selectResult) {
804
                $configurationRow = [
805
                    'pid' => $selectResult['pid'],
806
                    'tstamp' => $selectResult['tstamp'],
807
                    'crdate' => $selectResult['crdate'],
808
                    'type' => 'time',
809
                    'handling' => 'include',
810
                    'start_date' => (string)$selectResult['start_date'] ?: null,
811
                    'end_date' => (string)$selectResult['end_date'] ?: null,
812
                    'start_time' => (int)$selectResult['start_time'],
813
                    'end_time' => (int)$selectResult['end_time'],
814
                    'all_day' => (null === $selectResult['start_time'] && null === $selectResult['end_time']) ? 1 : 0,
815
                    'frequency' => $this->mapFrequency($selectResult['freq']),
816
                    'till_date' => (string)$selectResult['until'] ?: null,
817
                    'counter_amount' => (int)$selectResult['cnt'],
818
                    'counter_interval' => (int)$selectResult['interval'],
819
                    'import_id' => self::IMPORT_PREFIX . $selectResult['uid'],
820
                ];
821
822
                $variables = [
823
                    'table' => self::CONFIGURATION_TABLE,
824
                    'configurationRow' => $configurationRow,
825
                ];
826
827
                $dispatcher = HelperUtility::getSignalSlotDispatcher();
828
                $variables = $dispatcher->dispatch(__CLASS__, __FUNCTION__ . 'PreInsert', $variables);
829
830
                $db = HelperUtility::getDatabaseConnection($variables['table']);
831
                $q = $db->createQueryBuilder();
832
                $q->insert($variables['table'])->values($variables['configurationRow']);
833
834
                $dbQueries[] = $q->getSQL();
835
836
                $q->execute();
837
838
                $recordIds[] = $db->lastInsertId($variables['table']);
839
            }
840
        }
841
842
        return implode(',', $recordIds);
843
    }
844
845
    /**
846
     * Map frequency.
847
     *
848
     * @param string $calFrequency
849
     *
850
     * @return string
851
     */
852
    protected function mapFrequency($calFrequency)
853
    {
854
        $freq = [
855
            'none' => null,
856
            'day' => 'daily',
857
            'week' => 'weekly',
858
            'month' => 'monthly',
859
            'year' => 'yearly',
860
        ];
861
862
        if (!isset($freq[$calFrequency])) {
863
            return '';
864
        }
865
866
        return $freq[$calFrequency];
867
    }
868
869
    /**
870
     * Migrate the 'tx_cal_category' table to the 'sys_category' table.
871
     *
872
     * @param       $calIds
873
     * @param array $dbQueries
874
     * @param       $customMessages
875
     */
876
    protected function performSysCategoryUpdate($calIds, array &$dbQueries, &$customMessages)
877
    {
878
        // first migrate from tx_cal_category to sys_category
879
        $variables = [
880
            'table' => 'tx_cal_category',
881
            'dbQueries' => $dbQueries,
882
            'calIds' => $calIds,
883
        ];
884
885
        $q = $this->getQueryBuilder($variables['table']);
886
        $q->getRestrictions()
887
            ->removeAll()
888
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
889
890
        $q->select('*')
891
            ->from($variables['table']);
892
893
        $dbQueries[] = $q->getSQL();
894
895
        $selectResults = $q->execute()->fetchAll();
896
897
        foreach ($selectResults as $category) {
898
            $sysCategoryRecord = [
899
                'pid' => $category['pid'],
900
                'tstamp' => $category['tstamp'],
901
                'crdate' => $category['crdate'],
902
                'cruser_id' => $category['cruser_id'],
903
                'deleted' => $category['deleted'],
904
                'hidden' => $category['hidden'],
905
                'starttime' => $category['starttime'],
906
                'endtime' => $category['endtime'],
907
                'sys_language_uid' => $category['sys_language_uid'],
908
                'l10n_parent' => $category['l18n_parent'],
909
                'title' => $category['title'],
910
                'parent' => (int)$category['parent_category'],
911
                'import_id' => self::IMPORT_PREFIX . (int)$category['uid'],
912
                'sorting' => $category['sorting'],
913
            ];
914
915
            $q = $this->getQueryBuilder('sys_category');
916
            $q->insert('sys_category')->values($sysCategoryRecord);
917
            $dbQueries[] = $q->getSQL();
918
919
            $q->execute();
920
        }
921
922
        // second rewrite the tree
923
        $variables = [
924
            'table' => 'sys_category',
925
            'dbQueries' => $dbQueries,
926
            'calIds' => $calIds,
927
        ];
928
929
        $q = $this->getQueryBuilder($variables['table']);
930
931
        $q->select('*')
932
            ->from($variables['table'])
933
            ->where(
934
                $q->expr()->neq('import_id', $q->createNamedParameter(''))
935
            );
936
937
        $dbQueries[] = $q->getSQL();
938
        $selectResults = $q->execute()->fetchAll();
939
940
        foreach ($selectResults as $sysCategory) {
941
            // update parent, because there are just the old uids
942
            $q = $this->getQueryBuilder('sys_category');
943
            $q->update('sys_category')
944
                ->where(
945
                    $q->expr()->eq('uid', $q->createNamedParameter((int)$sysCategory['uid'], \PDO::PARAM_INT))
946
                )->set(
947
                    'parent',
948
                    $this->getSysCategoryParentUid(self::IMPORT_PREFIX . (int)$sysCategory['parent'])
949
                );
950
951
            $dbQueries[] = $q->getSQL();
952
953
            $q->execute();
954
        }
955
    }
956
957
    /**
958
     * Return the parentUid for the 'sys_category' entry on base of the import_id.
959
     *
960
     * @param string $importId
961
     *
962
     * @return int
963
     */
964
    protected function getSysCategoryParentUid($importId)
965
    {
966
        $table = 'sys_category';
967
        $q = $this->getQueryBuilder($table);
968
969
        $q->select('uid')
970
            ->from($table)
971
            ->where(
972
                $q->expr()->eq('import_id', $q->createNamedParameter($importId))
973
            );
974
975
        $dbQueries[] = $q->getSQL();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$dbQueries was never initialized. Although not strictly required by PHP, it is generally a good practice to add $dbQueries = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
976
977
        $result = $q->execute()->fetchAll();
978
979
        return (int)$result[0]['uid'];
980
    }
981
982
    /**
983
     * Get the event uid on base of the given import_id.
984
     * The import_id is the original tx_cal_event id prefixed with the IMPORT_PREFIX.
985
     *
986
     * @param string $importId
987
     * @param array  $dbQueries
988
     * @param array  $customMessages
989
     *
990
     * @return int
991
     */
992
    protected function getCalendarizeEventUid($importId, &$dbQueries, &$customMessages)
993
    {
994
        $variables = [
995
            'table' => self::EVENT_TABLE,
996
            'dbQueries' => $dbQueries,
997
        ];
998
999
        $dispatcher = HelperUtility::getSignalSlotDispatcher();
1000
        $variables = $dispatcher->dispatch(__CLASS__, __FUNCTION__, $variables);
1001
1002
        $q = $this->getQueryBuilder($variables['table']);
1003
1004
        // also get restricted (e.g. hidden) records, otherwise retrieving event uid for
1005
        // restricted records will always return 0
1006
        $q->getRestrictions()
1007
            ->removeAll()
1008
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1009
        $q->select('uid')
1010
            ->from($variables['table'])
1011
            ->where(
1012
                $q->expr()->eq('import_id', $q->createNamedParameter($importId))
1013
            );
1014
1015
        $dbQueries[] = $q->getSQL();
1016
1017
        $result = $q->execute()->fetchAll();
1018
1019
        return (int)$result[0]['uid'];
1020
    }
1021
1022
    /**
1023
     * Get the sys_category uid on base of the given import_id.
1024
     * The import_id is the original tx_cal_category id prefixed with the IMPORT_PREFIX.
1025
     *
1026
     * @see CalMigrationUpdate::IMPORT_PREFIX
1027
     *
1028
     * @param string $importId
1029
     * @param array  $dbQueries
1030
     * @param array  $customMessages
1031
     *
1032
     * @return int
1033
     */
1034
    protected function getCalendarizeCategoryUid($importId, &$dbQueries, &$customMessages)
1035
    {
1036
        $variables = [
1037
            'table' => 'sys_category',
1038
            'dbQueries' => $dbQueries,
1039
        ];
1040
1041
        $dispatcher = HelperUtility::getSignalSlotDispatcher();
1042
        $variables = $dispatcher->dispatch(__CLASS__, __FUNCTION__, $variables);
1043
1044
        $q = $this->getQueryBuilder($variables['table']);
1045
1046
        $q->select('uid')
1047
            ->from($variables['table'])
1048
            ->where(
1049
                $q->expr()->eq('import_id', $q->createNamedParameter($importId))
1050
            );
1051
1052
        $dbQueries[] = $q->getSQL();
1053
1054
        $result = $q->execute()->fetchAll();
1055
1056
        return (int)$result[0]['uid'];
1057
    }
1058
1059
    /**
1060
     * Build configurations.
1061
     *
1062
     * @param $calEventRow
1063
     * @param $dbQueries
1064
     *
1065
     * @return int
1066
     */
1067
    protected function buildConfigurations($calEventRow, &$dbQueries)
1068
    {
1069
        $configurationRow = [
1070
            'pid' => $calEventRow['pid'],
1071
            'tstamp' => $calEventRow['tstamp'],
1072
            'crdate' => $calEventRow['crdate'],
1073
            'type' => 'time',
1074
            'handling' => 'include',
1075
            'start_date' => (string)$calEventRow['start_date'] ?: null,
1076
            'end_date' => (string)$calEventRow['end_date'] ?: null,
1077
            'start_time' => (int)$calEventRow['start_time'],
1078
            'end_time' => (int)$calEventRow['end_time'],
1079
            'all_day' => $calEventRow['allday'],
1080
            'frequency' => $this->mapFrequency($calEventRow['freq']),
1081
            'till_date' => (string)$calEventRow['until'] ?: null,
1082
            'counter_amount' => (int)$calEventRow['cnt'],
1083
            'counter_interval' => (int)$calEventRow['interval'],
1084
        ];
1085
1086
        $variables = [
1087
            'table' => self::CONFIGURATION_TABLE,
1088
            'configurationRow' => $configurationRow,
1089
            'calEventRow' => $calEventRow,
1090
            'dbQueries' => $dbQueries,
1091
        ];
1092
1093
        $db = HelperUtility::getDatabaseConnection($variables['table']);
1094
        $q = $db->createQueryBuilder();
1095
1096
        $dispatcher = HelperUtility::getSignalSlotDispatcher();
1097
        $variables = $dispatcher->dispatch(__CLASS__, __FUNCTION__ . 'PreInsert', $variables);
1098
1099
        $q->insert($variables['table'])
1100
            ->values($variables['configurationRow']);
1101
1102
        $dbQueries[] = $q->getSQL();
1103
        $q->execute();
1104
        $recordId = $db->lastInsertId($variables['table']);
1105
1106
        $variables = [
1107
            'table' => $variables['table'],
1108
            'configurationRow' => $configurationRow,
1109
            'calEventRow' => $calEventRow,
1110
            'recordId' => $recordId,
1111
            'dbQueries' => $dbQueries,
1112
        ];
1113
1114
        $dispatcher = HelperUtility::getSignalSlotDispatcher();
1115
        $variables = $dispatcher->dispatch(__CLASS__, __FUNCTION__ . 'PostInsert', $variables);
1116
1117
        return $variables['recordId'];
1118
    }
1119
1120
    /**
1121
     * Get the non migrated cal IDs.
1122
     *
1123
     * @return array
1124
     */
1125
    protected function getNonMigratedCalIds()
1126
    {
1127
        $checkImportIds = [];
1128
        $nonMigrated = [];
1129
1130
        $table = 'tx_cal_event';
1131
        $q = $this->getQueryBuilder($table);
1132
1133
        $events = $q->select('uid')
1134
            ->from($table)
1135
            ->execute()
1136
            ->fetchAll();
1137
1138
        foreach ($events as $event) {
1139
            $checkImportIds[] = '"' . self::IMPORT_PREFIX . $event['uid'] . '"';
1140
            $nonMigrated[(int)$event['uid']] = (int)$event['uid'];
1141
        }
1142
1143
        $countOriginal = \count($checkImportIds);
1144
        if (0 === $countOriginal) {
1145
            return [];
1146
        }
1147
1148
        $variables = [
1149
            'table' => self::EVENT_TABLE,
1150
        ];
1151
1152
        $dispatcher = HelperUtility::getSignalSlotDispatcher();
1153
        $variables = $dispatcher->dispatch(__CLASS__, __FUNCTION__ . 'PreSelect', $variables);
1154
1155
        $q = $this->getQueryBuilder($variables['table']);
1156
1157
        $migratedRows = $q->select('uid', 'import_id')
1158
            ->from($variables['table'])
1159
            ->where(
1160
                $q->expr()->in('import_id', $checkImportIds)
1161
            )
1162
            ->execute()
1163
            ->fetchAll();
1164
1165
        foreach ($migratedRows as $migratedRow) {
1166
            $importId = (int)str_replace(self::IMPORT_PREFIX, '', $migratedRow['import_id']);
1167
            if (isset($nonMigrated[$importId])) {
1168
                unset($nonMigrated[$importId]);
1169
            }
1170
        }
1171
1172
        $variables = [
1173
            'table' => $variables['table'],
1174
            'migratedRows' => $migratedRows,
1175
            'nonMigrated' => $nonMigrated,
1176
        ];
1177
1178
        $dispatcher = HelperUtility::getSignalSlotDispatcher();
1179
        $variables = $dispatcher->dispatch(__CLASS__, __FUNCTION__ . 'ReadyParsed', $variables);
1180
1181
        return $variables['nonMigrated'];
1182
    }
1183
1184
    public function updateNecessary(): bool
1185
    {
1186
        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
1187
        $connection = $connectionPool->getConnectionByName(ConnectionPool::DEFAULT_CONNECTION_NAME);
1188
        $dbSchema = $connection->getSchemaManager()->createSchema();
1189
1190
        $tableNames = array_map(function ($table) {
1191
            return $table->getName();
1192
        }, $dbSchema->getTables());
1193
1194
        return \in_array('tx_cal_event', $tableNames);
1195
    }
1196
1197
    public function getPrerequisites(): array
1198
    {
1199
        return [
1200
            DatabaseUpdatedPrerequisite::class,
1201
        ];
1202
    }
1203
1204
    private function getQueryBuilder(string $table): QueryBuilder
1205
    {
1206
        $queryBuilder = HelperUtility::getDatabaseConnection($table)->createQueryBuilder();
1207
1208
        // This adds a custom restriction similar to DeletedRestriction
1209
        // When 'cal' is not installed there is no TCA definition.
1210
        // This causes the default restrictions to not work.
1211
        // This adds a custom restriction to filter out deleted records.
1212
        if (str_starts_with($table, 'tx_cal_')) {
1213
            $queryBuilder
1214
                ->getRestrictions()
1215
                ->removeAll()
1216
                ->add(new class() implements QueryRestrictionInterface {
1217
                    public function buildExpression(
1218
                        array $queriedTables,
1219
                        ExpressionBuilder $expressionBuilder
1220
                    ): CompositeExpression {
1221
                        $constraints = [];
1222
                        foreach ($queriedTables as $tableAlias => $tableName) {
1223
                            if (str_starts_with($tableName, 'tx_cal_') && !str_ends_with($tableName, '_mm')) {
1224
                                $constraints[] = $expressionBuilder->eq(
1225
                                    $tableAlias . '.deleted',
1226
                                    0
1227
                                );
1228
                            }
1229
                        }
1230
1231
                        return $expressionBuilder->andX(...$constraints);
1232
                    }
1233
                });
1234
        }
1235
1236
        return $queryBuilder;
1237
    }
1238
}
1239