Completed
Push — master ( 498091...9beb04 )
by Tim
15s queued 12s
created

injectPersistenceManager()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 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\Service\IndexerService;
13
use HDNET\Calendarize\Utility\DateTimeUtility;
14
use HDNET\Calendarize\Utility\HelperUtility;
15
use Symfony\Component\Console\Command\Command;
16
use Symfony\Component\Console\Input\InputInterface;
17
use Symfony\Component\Console\Input\InputOption;
18
use Symfony\Component\Console\Output\OutputInterface;
19
use Symfony\Component\Console\Style\SymfonyStyle;
20
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
21
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
22
use TYPO3\CMS\Core\Utility\GeneralUtility;
23
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
24
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
25
26
/**
27
 * Cleanup the event models.
28
 */
29
class CleanupCommandController extends Command
30
{
31
    const MODUS_HIDDEN = 'hide';
32
    const MODUS_DELETED = 'delete';
33
    const DEFAULT_WAIT_PERIOD = 14;
34
    const DEFAULT_CLEANUP_REPOSITORY = \HDNET\Calendarize\Domain\Repository\EventRepository::class;
35
36
    /**
37
     * @var PersistenceManager
38
     */
39
    protected $persistenceManager;
40
41
    /**
42
     * @param PersistenceManager $persistenceManager
43
     */
44
    public function injectPersistenceManager(PersistenceManager $persistenceManager): void
45
    {
46
        $this->persistenceManager = $persistenceManager;
47
    }
48
49
    protected function configure()
50
    {
51
        $this->setDescription('Remove outdated events to keep a small footprint')
52
            ->addOption(
53
                'repositoryName',
54
                null,
55
                InputOption::VALUE_OPTIONAL,
56
                'The repository of the event to clean up',
57
                self::DEFAULT_CLEANUP_REPOSITORY
58
            )
59
            ->addOption(
60
                'modus',
61
                null,
62
                InputOption::VALUE_OPTIONAL,
63
                'What to do with cleaned Events? Set them \'hide\' or \'delete\'',
64
                self::MODUS_HIDDEN
65
            )
66
            ->addOption(
67
                'waitingPeriod',
68
                null,
69
                InputOption::VALUE_OPTIONAL,
70
                'how many days to wait after ending the Event before \'hide/delete\' it',
71
                self::DEFAULT_WAIT_PERIOD
72
            );
73
    }
74
75
    /**
76
     * Cleanup the event models.
77
     * Remove outdated events to keep a small footprint. This gain maybe a little more performance.
78
     *
79
     * @param InputInterface  $input
80
     * @param OutputInterface $output
81
     *
82
     * @return int 0 if everything went fine, or an exit code
83
     */
84
    protected function execute(InputInterface $input, OutputInterface $output)
85
    {
86
        $io = new SymfonyStyle($input, $output);
87
88
        $repositoryName = $input->getOption('repositoryName');
89
        $modus = $input->getOption('modus');
90
        $waitingPeriod = $input->getOption('waitingPeriod');
91
92
        /** @var EventRepository $repository */
93
        $repository = GeneralUtility::makeInstance($repositoryName);
94
95
        if (!($repository instanceof EventRepository)) {
96
            return 1;
97
        }
98
99
        $io->section('Reindex all events');
100
        // Index all events to start on a clean slate
101
        $this->reIndex();
102
103
        // get tablename from repository, works only with the extended EventRepository
104
        $tableName = $repository->getTableName();
105
106
        if (!$tableName) {
107
            $io->error('No tablename found on your given Repository! [' . $repositoryName . ']');
108
109
            return 2;
110
        }
111
112
        $io->text('Tablename ' . $tableName);
113
114
        $io->section('Find outdated events');
115
        // events uid, to be precise
116
        $events = $this->findOutdatedEvents($tableName, $waitingPeriod);
117
118
        $io->text('Just found ' . \count($events) . ' Events ready to process.');
119
120
        // climb thru the events and hide/delete them
121
        foreach ($events as $event) {
122
            $uid = (int)$event['foreign_uid'];
123
124
            $model = $repository->findByUid($uid);
125
126
            if (!($model instanceof Event)) {
127
                $io->error('Object with uid [' . $uid . '] is not an instance of the event base model.');
128
                continue;
129
            }
130
131
            $this->processEvent($repository, $model, $modus);
132
        }
133
134
        $this->persistenceManager->persistAll();
135
136
        $io->section('Reindex all events');
137
        // after all this deleting ... reindex!
138
        $this->reIndex();
139
140
        return 0;
141
    }
142
143
    /**
144
     * Process the found Event and delete or hide it.
145
     *
146
     * @param EventRepository $repository
147
     * @param Event           $model
148
     * @param string          $modus
149
     */
150
    protected function processEvent(EventRepository $repository, Event $model, $modus)
151
    {
152
        // define the function for the delete-modus.
153
        $delete = function ($repository, $model) {
154
            $repository->remove($model);
155
        };
156
157
        // define the function for the hide-modus.
158
        $hide = function ($repository, $model) {
159
            $model->setHidden(true);
160
            $repository->update($model);
161
        };
162
163
        if (self::MODUS_DELETED === $modus) {
164
            $function = $delete;
165
        } else {
166
            $function = $hide;
167
        }
168
169
        // dispatch variables
170
        // use function to write your own reaction
171
        $variables = [
172
            'modus' => $modus,
173
            'repository' => $repository,
174
            'model' => $model,
175
            'function' => $function,
176
        ];
177
178
        $dispatcher = GeneralUtility::makeInstance(Dispatcher::class);
179
        $variables = $dispatcher->dispatch(__CLASS__, __FUNCTION__, $variables);
180
181
        $myFunction = $variables['function'];
182
        $myFunction($repository, $model);
183
    }
184
185
    /**
186
     * Find outdated events.
187
     *
188
     * @param string $tableName
189
     * @param int    $waitingPeriod
190
     *
191
     * @throws \Exception
192
     *
193
     * @return array
194
     */
195
    protected function findOutdatedEvents($tableName, $waitingPeriod): array
196
    {
197
        // calculate the waiting time
198
        $interval = 'P' . (int)$waitingPeriod . 'D';
199
        $now = DateTimeUtility::getNow();
200
        $now->sub(new \DateInterval($interval));
201
202
        // search for outdated events
203
        $table = IndexerService::TABLE_NAME;
204
205
        $q = HelperUtility::getDatabaseConnection($table)->createQueryBuilder();
206
        $q->getRestrictions()
207
            ->removeAll()
208
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
209
            ->add(GeneralUtility::makeInstance(HiddenRestriction::class));
210
211
        $foreignUids = $q->select('foreign_uid')
212
            ->from($table)
213
            ->where($q->expr()
214
                ->gt('end_date', $q->createNamedParameter($now->format('Y-m-d'))))
215
            ->andWhere($q->expr()
216
                ->eq('foreign_table', $q->createNamedParameter($tableName)))
217
            ->execute()
218
            ->fetchAll();
219
220
        $foreignUids = array_map(function ($item) {
221
            return (int)$item['foreign_uid'];
222
        }, $foreignUids);
223
224
        $q->select('foreign_uid')
225
            ->from($table)
226
            ->where($q->expr()
227
                ->andX($q->expr()
228
                    ->lt('end_date', $q->createNamedParameter($now->format('Y-m-d'))), $q->expr()
229
                    ->eq('foreign_table', $q->createNamedParameter($tableName)), $q->expr()
230
                    ->notIn('foreign_uid', $foreignUids)));
231
232
        $rows = $q->execute()->fetchAll();
233
234
        return $rows;
235
    }
236
237
    /**
238
     * Reindex the Events.
239
     * This may take some time.
240
     */
241
    protected function reIndex()
242
    {
243
        $indexer = GeneralUtility::makeInstance(IndexerService::class);
244
        $indexer->reindexAll();
245
    }
246
}
247