CleanAuditLogsCommand::execute()   B
last analyzed

Complexity

Conditions 9
Paths 12

Size

Total Lines 111
Code Lines 62

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 9
eloc 62
c 2
b 0
f 0
nc 12
nop 2
dl 0
loc 111
rs 7.2735

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace DH\DoctrineAuditBundle\Command;
4
5
use DateInterval;
6
use DateTime;
7
use DH\DoctrineAuditBundle\Reader\Reader;
8
use Doctrine\DBAL\Connection;
9
use Doctrine\DBAL\Query\QueryBuilder;
10
use Exception;
11
use Symfony\Component\Console\Command\Command;
12
use Symfony\Component\Console\Command\LockableTrait;
13
use Symfony\Component\Console\Exception\RuntimeException;
14
use Symfony\Component\Console\Helper\ProgressBar;
15
use Symfony\Component\Console\Input\InputArgument;
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 Symfony\Component\DependencyInjection\ContainerAwareInterface;
21
use Symfony\Component\DependencyInjection\ContainerInterface;
22
23
class CleanAuditLogsCommand extends Command implements ContainerAwareInterface
24
{
25
    use LockableTrait;
26
27
    protected static $defaultName = 'audit:clean';
28
29
    /**
30
     * @var null|ContainerInterface
31
     */
32
    private $container;
33
34
    public function setContainer(?ContainerInterface $container = null): void
35
    {
36
        $this->container = $container;
37
    }
38
39
    public function unlock(): void
40
    {
41
        $this->release();
42
    }
43
44
    protected function configure(): void
45
    {
46
        $this
47
            ->setDescription('Cleans audit tables')
48
            ->setName(self::$defaultName)
49
            ->addOption('no-confirm', null, InputOption::VALUE_NONE, 'No interaction mode')
50
            ->addArgument('keep', InputArgument::OPTIONAL, 'Audits retention period (must be expressed as an ISO 8601 date interval, e.g. P12M to keep the last 12 months or P7D to keep the last 7 days).', 'P12M')
51
        ;
52
    }
53
54
    protected function execute(InputInterface $input, OutputInterface $output)
55
    {
56
        if (null === $this->container) {
57
            throw new RuntimeException('No container.');
58
        }
59
60
        if (!$this->lock()) {
61
            $output->writeln('The command is already running in another process.');
62
63
            return 0;
64
        }
65
66
        $io = new SymfonyStyle($input, $output);
67
68
        if (is_numeric($input->getArgument('keep'))) {
69
            $deprecationMessage = "Providing an integer value for the 'keep' argument is deprecated. Please use the ISO 8601 duration format (e.g. P12M).";
70
            @trigger_error($deprecationMessage, E_USER_DEPRECATED);
71
            $io->writeln($deprecationMessage);
72
73
            $keep = (int) $input->getArgument('keep');
74
75
            if ($keep <= 0) {
76
                $io->error("'keep' argument must be a positive number.");
77
                $this->release();
78
79
                return 0;
80
            }
81
82
            $until = new DateTime();
83
            $until->modify('-'.$keep.' month');
84
        } else {
85
            $keep = (string) ($input->getArgument('keep'));
86
87
            try {
88
                $dateInterval = new DateInterval($keep);
89
            } catch (Exception $e) {
90
                $io->error(sprintf("'keep' argument must be a valid ISO 8601 date interval. '%s' given.", $keep));
91
                $this->release();
92
93
                return 0;
94
            }
95
96
            $until = new DateTime();
97
            $until->sub($dateInterval);
98
        }
99
100
        /**
101
         * @var Reader
102
         */
103
        $reader = $this->container->get('dh_doctrine_audit.reader');
104
105
        /**
106
         * @var Connection
107
         */
108
        $connection = $reader->getConfiguration()->getEntityManager()->getConnection();
109
110
        $entities = $reader->getEntities();
111
112
        $message = sprintf(
113
            "You are about to clean audits created before <comment>%s</comment>: %d entities involved.\n Do you want to proceed?",
114
            $until->format('Y-m-d'),
115
            \count($entities)
116
        );
117
118
        $confirm = $input->getOption('no-confirm') ? true : $io->confirm($message, false);
119
120
        if ($confirm) {
121
            $progressBar = new ProgressBar($output, \count($entities));
122
            $progressBar->setBarWidth(70);
123
            $progressBar->setFormat("%message%\n".$progressBar->getFormatDefinition('debug'));
124
125
            $progressBar->setMessage('Starting...');
126
            $progressBar->start();
127
128
            foreach ($entities as $entity => $tablename) {
129
                $auditTable = implode('', [
130
                    $reader->getConfiguration()->getTablePrefix(),
131
                    $tablename,
132
                    $reader->getConfiguration()->getTableSuffix(),
133
                ]);
134
135
                /**
136
                 * @var QueryBuilder
137
                 */
138
                $queryBuilder = $connection->createQueryBuilder();
139
                $queryBuilder
140
                    ->delete($auditTable)
141
                    ->where('created_at < :until')
142
                    ->setParameter(':until', $until->format('Y-m-d'))
143
                    ->execute()
144
                ;
145
146
                $progressBar->setMessage("Cleaning audit tables... (<info>{$auditTable}</info>)");
147
                $progressBar->advance();
148
            }
149
150
            $progressBar->setMessage('Cleaning audit tables... (<info>done</info>)');
151
            $progressBar->display();
152
153
            $io->newLine(2);
154
155
            $io->success('Success.');
156
        } else {
157
            $io->success('Cancelled.');
158
        }
159
160
        // if not released explicitly, Symfony releases the lock
161
        // automatically when the execution of the command ends
162
        $this->release();
163
164
        return 0;
165
    }
166
}
167