Completed
Push — master ( 7b9104...2433c1 )
by Tim
02:44
created

CleanupCommandController   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 219
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 0
Metric Value
wmc 12
lcom 1
cbo 12
dl 0
loc 219
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A injectPersistenceManager() 0 4 1
A injectEventDispatcher() 0 4 1
A configure() 0 22 1
B execute() 0 58 5
A processEvent() 0 25 2
A findOutdatedEvents() 0 41 1
A reIndex() 0 5 1
1
<?php
2
3
/**
4
 * Cleanup the event models.
5
 */
6
declare(strict_types=1);
7
8
namespace HDNET\Calendarize\Command;
9
10
use HDNET\Calendarize\Domain\Model\Event;
11
use HDNET\Calendarize\Domain\Repository\EventRepository;
12
use HDNET\Calendarize\Event\CleanupEvent;
13
use HDNET\Calendarize\Service\IndexerService;
14
use HDNET\Calendarize\Utility\DateTimeUtility;
15
use HDNET\Calendarize\Utility\HelperUtility;
16
use Psr\EventDispatcher\EventDispatcherInterface;
17
use Symfony\Component\Console\Command\Command;
18
use Symfony\Component\Console\Input\InputArgument;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Output\OutputInterface;
21
use Symfony\Component\Console\Style\SymfonyStyle;
22
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
23
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
24
use TYPO3\CMS\Core\Utility\GeneralUtility;
25
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
26
27
/**
28
 * Cleanup the event models.
29
 */
30
class CleanupCommandController extends Command
31
{
32
    const MODUS_HIDDEN = 'hide';
33
    const MODUS_DELETED = 'delete';
34
    const DEFAULT_WAIT_PERIOD = 14;
35
    const DEFAULT_CLEANUP_REPOSITORY = \HDNET\Calendarize\Domain\Repository\EventRepository::class;
36
37
    /**
38
     * @var PersistenceManager
39
     */
40
    protected $persistenceManager;
41
42
    /**
43
     * @var EventDispatcherInterface
44
     */
45
    protected $eventDispatcher;
46
47
    /**
48
     * @param PersistenceManager $persistenceManager
49
     */
50
    public function injectPersistenceManager(PersistenceManager $persistenceManager): void
51
    {
52
        $this->persistenceManager = $persistenceManager;
53
    }
54
55
    /**
56
     * @param EventDispatcherInterface $eventDispatcher
57
     */
58
    public function injectEventDispatcher(EventDispatcherInterface $eventDispatcher): void
59
    {
60
        $this->eventDispatcher = $eventDispatcher;
61
    }
62
63
    protected function configure()
64
    {
65
        $this->setDescription('Remove outdated events to keep a small footprint')
66
            ->addArgument(
67
                'repositoryName',
68
                InputArgument::OPTIONAL,
69
                'The repository of the event to clean up',
70
                self::DEFAULT_CLEANUP_REPOSITORY
71
            )
72
            ->addArgument(
73
                'modus',
74
                InputArgument::OPTIONAL,
75
                'What to do with cleaned Events? Set them \'hide\' or \'delete\'',
76
                self::MODUS_HIDDEN
77
            )
78
            ->addArgument(
79
                'waitingPeriod',
80
                InputArgument::OPTIONAL,
81
                'How many days to wait after ending the Event before \'hide/delete\' it',
82
                self::DEFAULT_WAIT_PERIOD
83
            );
84
    }
85
86
    /**
87
     * Cleanup the event models.
88
     * Remove outdated events to keep a small footprint. This gain maybe a little more performance.
89
     *
90
     * @param InputInterface  $input
91
     * @param OutputInterface $output
92
     *
93
     * @return int 0 if everything went fine, or an exit code
94
     */
95
    protected function execute(InputInterface $input, OutputInterface $output)
96
    {
97
        $io = new SymfonyStyle($input, $output);
98
99
        $repositoryName = $input->getOption('repositoryName');
100
        $modus = $input->getOption('modus');
101
        $waitingPeriod = $input->getOption('waitingPeriod');
102
103
        /** @var EventRepository $repository */
104
        $repository = GeneralUtility::makeInstance($repositoryName);
105
106
        if (!($repository instanceof EventRepository)) {
107
            return 1;
108
        }
109
110
        $io->section('Reindex all events');
111
        // Index all events to start on a clean slate
112
        $this->reIndex();
113
114
        // get tablename from repository, works only with the extended EventRepository
115
        $tableName = $repository->getTableName();
116
117
        if (!$tableName) {
118
            $io->error('No tablename found on your given Repository! [' . $repositoryName . ']');
119
120
            return 2;
121
        }
122
123
        $io->text('Tablename ' . $tableName);
124
125
        $io->section('Find outdated events');
126
        // events uid, to be precise
127
        $events = $this->findOutdatedEvents($tableName, $waitingPeriod);
128
129
        $io->text('Just found ' . \count($events) . ' Events ready to process.');
130
131
        // climb thru the events and hide/delete them
132
        foreach ($events as $event) {
133
            $uid = (int)$event['foreign_uid'];
134
135
            $model = $repository->findByUid($uid);
136
137
            if (!($model instanceof Event)) {
138
                $io->error('Object with uid [' . $uid . '] is not an instance of the event base model.');
139
                continue;
140
            }
141
142
            $this->processEvent($repository, $model, $modus);
143
        }
144
145
        $this->persistenceManager->persistAll();
146
147
        $io->section('Reindex all events');
148
        // after all this deleting ... reindex!
149
        $this->reIndex();
150
151
        return 0;
152
    }
153
154
    /**
155
     * Process the found Event and delete or hide it.
156
     *
157
     * @param EventRepository $repository
158
     * @param Event           $model
159
     * @param string          $modus
160
     */
161
    protected function processEvent(EventRepository $repository, Event $model, $modus)
162
    {
163
        // define the function for the delete-modus.
164
        $delete = function ($repository, $model) {
165
            $repository->remove($model);
166
        };
167
168
        // define the function for the hide-modus.
169
        $hide = function ($repository, $model) {
170
            $model->setHidden(true);
171
            $repository->update($model);
172
        };
173
174
        if (self::MODUS_DELETED === $modus) {
175
            $function = $delete;
176
        } else {
177
            $function = $hide;
178
        }
179
180
        $event = new CleanupEvent($modus, $repository, $model, $function);
181
        $this->eventDispatcher->dispatch($event);
0 ignored issues
show
Documentation introduced by
$event is of type object<HDNET\Calendarize\Event\CleanupEvent>, but the function expects a object<Psr\EventDispatcher\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
182
183
        $myFunction = $event->getFunction();
184
        $myFunction($repository, $model);
185
    }
186
187
    /**
188
     * Find outdated events.
189
     *
190
     * @param string $tableName
191
     * @param int    $waitingPeriod
192
     *
193
     * @throws \Exception
194
     *
195
     * @return array
196
     */
197
    protected function findOutdatedEvents($tableName, $waitingPeriod): array
198
    {
199
        // calculate the waiting time
200
        $interval = 'P' . (int)$waitingPeriod . 'D';
201
        $now = DateTimeUtility::getNow();
202
        $now->sub(new \DateInterval($interval));
203
204
        // search for outdated events
205
        $table = IndexerService::TABLE_NAME;
206
207
        $q = HelperUtility::getDatabaseConnection($table)->createQueryBuilder();
208
        $q->getRestrictions()
209
            ->removeAll()
210
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
211
            ->add(GeneralUtility::makeInstance(HiddenRestriction::class));
212
213
        $foreignUids = $q->select('foreign_uid')
214
            ->from($table)
215
            ->where($q->expr()
216
                ->gt('end_date', $q->createNamedParameter($now->format('Y-m-d'))))
217
            ->andWhere($q->expr()
218
                ->eq('foreign_table', $q->createNamedParameter($tableName)))
219
            ->execute()
220
            ->fetchAll();
221
222
        $foreignUids = array_map(function ($item) {
223
            return (int)$item['foreign_uid'];
224
        }, $foreignUids);
225
226
        $q->select('foreign_uid')
227
            ->from($table)
228
            ->where($q->expr()
229
                ->andX($q->expr()
230
                    ->lt('end_date', $q->createNamedParameter($now->format('Y-m-d'))), $q->expr()
231
                    ->eq('foreign_table', $q->createNamedParameter($tableName)), $q->expr()
232
                    ->notIn('foreign_uid', $foreignUids)));
233
234
        $rows = $q->execute()->fetchAll();
235
236
        return $rows;
237
    }
238
239
    /**
240
     * Reindex the Events.
241
     * This may take some time.
242
     */
243
    protected function reIndex()
244
    {
245
        $indexer = GeneralUtility::makeInstance(IndexerService::class);
246
        $indexer->reindexAll();
247
    }
248
}
249