Completed
Pull Request — master (#8)
by
unknown
06:16
created

ImportCommand::import()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 74
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 8

Importance

Changes 11
Bugs 2 Features 3
Metric Value
c 11
b 2
f 3
dl 0
loc 74
ccs 31
cts 31
cp 1
rs 6.2894
cc 8
eloc 42
nc 8
nop 8
crap 8

How to fix   Long Method    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
namespace Mathielen\ImportEngineBundle\Command;
3
4
use Ddeboer\DataImport\Filter\OffsetFilter;
5
use Mathielen\DataImport\Event\ImportItemEvent;
6
use Mathielen\ImportEngine\Event\ImportConfigureEvent;
7
use Mathielen\ImportEngine\Event\ImportRequestEvent;
8
use Mathielen\ImportEngine\Exception\InvalidConfigurationException;
9
use Mathielen\ImportEngine\Import\ImportBuilder;
10
use Mathielen\ImportEngine\Import\Run\ImportRunner;
11
use Mathielen\ImportEngine\ValueObject\ImportRequest;
12
use Mathielen\ImportEngine\ValueObject\ImportRun;
13
use Mathielen\ImportEngineBundle\Utils;
14
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
15
use Symfony\Component\Console\Helper\ProgressBar;
16
use Symfony\Component\Console\Helper\Table;
17
use Symfony\Component\Console\Helper\TableSeparator;
18
use Symfony\Component\Console\Input\InputArgument;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Input\InputOption;
21
use Symfony\Component\Console\Output\OutputInterface;
22
use Symfony\Component\Validator\ConstraintViolation;
23
24
class ImportCommand extends ContainerAwareCommand
25
{
26
27
    const MAX_VIOLATION_ERRORS = 10;
28
29 4
    protected function configure()
30
    {
31 4
        $this->setName('importengine:import')
32 4
            ->setDescription('Imports data with a definied importer')
33 4
            ->addArgument('source_id', InputArgument::REQUIRED, "id of source. Different StorageProviders need different id data.\n- upload, directory: \"<path/to/file>\"\n- doctrine: \"<id of query>\"\n- service: \"<service>.<method>[?arguments_like_url_query]\"")
34 4
            ->addArgument('source_provider', InputArgument::OPTIONAL, 'id of source provider', 'default')
35 4
            ->addOption('importer', 'i', InputOption::VALUE_REQUIRED, 'id/name of importer')
36 4
            ->addOption('context', 'c', InputOption::VALUE_REQUIRED, 'Supply optional context information to import. Supply key-value data in query style: key=value&otherkey=othervalue&...')
37 4
            ->addOption('offset', 'o', InputOption::VALUE_REQUIRED, 'Offset imported rows', 0)
38 4
            ->addOption('limit', 'l', InputOption::VALUE_REQUIRED, 'Limit imported rows')
39 4
            ->addOption('dryrun', 'd', InputOption::VALUE_NONE, 'Do not import - Validation only')
40
        ;
41 4
    }
42
43 4
    protected function validateInput(InputInterface $input)
0 ignored issues
show
Unused Code introduced by
The parameter $input is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
44
    {
45 4
        if (!$this->getContainer()->has('mathielen_importengine.import.builder') ||
46 4
            !$this->getContainer()->has('mathielen_importengine.import.runner')) {
47
            throw new InvalidConfigurationException("No importengine services have been found. Did you register the bundle in AppKernel and configured at least one importer in config?");
48
        }
49 4
    }
50
51 4
    protected function execute(InputInterface $input, OutputInterface $output)
52
    {
53 4
        $this->validateInput($input);
54
55 4
        $importerId = $input->getOption('importer');
56 4
        $sourceProviderId = $input->getArgument('source_provider');
57 4
        $sourceId = $input->getArgument('source_id');
58 4
        $isDryrun = $input->getOption('dryrun');
59 4
        if ($context = $input->getOption('context')) {
60
            //parse key=value&key=value string to array
61
            if (strpos($context, '=') !== false) {
62
                parse_str($input->getOption('context'), $context);
63
            }
64
        }
65 4
        $limit = $input->getOption('limit');
66 4
        $offset = $input->getOption('offset');
67
68 4
        $this->import($output, $importerId, $sourceProviderId, $sourceId, $context, $offset, $limit, $isDryrun);
69 4
    }
70
71 4
    protected function import(OutputInterface $output, $importerId, $sourceProviderId, $sourceId, $context=null, $offset=0, $limit=null, $isDryrun=false)
72
    {
73 4
        $output->writeln("Commencing ".($isDryrun?'<comment>dry-run</comment> ':'')."import using importer ".(empty($importerId)?'<comment>unknown</comment>':"<info>$importerId</info>")." with source provider <info>$sourceProviderId</info> and source id <info>$sourceId</info>");
74
75 4
        $sourceId = Utils::parseSourceId($sourceId);
76 4
        $progress = new ProgressBar($output);
77
78
        //set limit
79 4
        if ($limit) {
80 1
            $output->writeln("Limiting import to <info>$limit</info> rows.");
81
82
            $this->getContainer()->get('event_dispatcher')->addListener(ImportConfigureEvent::AFTER_BUILD, function (ImportConfigureEvent $event) use ($offset, $limit) {
83
                $event->getImport()->importer()->filters()->add(new OffsetFilter($offset, $limit));
84 1
            });
85
        }
86
87
        //show discovered importer id
88 4
        if (empty($importerId)) {
89
            $this->getContainer()->get('event_dispatcher')->addListener(ImportRequestEvent::DISCOVERED, function (ImportRequestEvent $event) use ($output) {
90
                $importerId = $event->getImportRequest()->getImporterId();
91
                $output->writeln("Importer discovered: <info>$importerId</info>");
92 2
            });
93
        }
94
95
        /** @var ImportBuilder $importBuilder */
96 4
        $importBuilder = $this->getContainer()->get('mathielen_importengine.import.builder');
97
98 4
        $importRequest = new ImportRequest($sourceId, $sourceProviderId, $importerId, Utils::whoAmI().'@CLI', $context);
0 ignored issues
show
Unused Code introduced by
The call to ImportRequest::__construct() has too many arguments starting with $context.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
99
100 4
        $import = $importBuilder->buildFromRequest($importRequest);
101
102
        //apply context info from commandline
103 4
        $importRun = $import->getRun();
104
105
        //status callback
106 4
        $this->getContainer()->get('event_dispatcher')->addListener(ImportItemEvent::AFTER_READ, function (ImportItemEvent $event) use ($output, &$progress) {
107
            /** @var ImportRun $importRun */
108
            $importRun = $event->getContext()->getRun();
109
            $stats = $importRun->getStatistics();
110
            $processed = isset($stats['processed'])?$stats['processed']:0;
111
            $max = $importRun->getInfo()['count'];
112
113
            if ($progress->getMaxSteps() != $max) {
114
                $progress = new ProgressBar($output, $max);
115
                $progress->start();
116
            }
117
118
            $progress->setProgress($processed);
119 4
        });
120
121
        /** @var ImportRunner $importRunner */
122 4
        $importRunner = $this->getContainer()->get('mathielen_importengine.import.runner');
123 4
        if ($isDryrun) {
124 1
            $importRunner->dryRun($import);
125
        } else {
126 3
            $importRunner->run($import);
127
        }
128
129 4
        $progress->finish();
130 4
        $output->writeln('');
131 4
        $output->writeln("<info>Import done</info>");
132 4
        $output->writeln('');
133
134 4
        $this->writeStatistics($importRun->getStatistics(), new Table($output));
135
136 4
        $this->writeValidationViolations(
137
            $import
138 4
                ->importer()
139 4
                ->validation()
140 4
                ->getViolations(),
141 4
            new Table($output));
142
143 4
        $output->writeln('');
144 4
    }
145
146 4
    protected function writeValidationViolations(array $violations, Table $table)
147
    {
148 4
        if (empty($violations)) {
149 4
            return;
150
        }
151
        $violations = $violations['source'] + $violations['target'];
152
153
        $table
154
            ->setHeaders(array('Constraint', 'Occurrences (lines)'))
155
        ;
156
157
        $tree = [];
158
        foreach ($violations as $line=>$validations) {
159
            /** @var ConstraintViolation $validation */
160
            foreach ($validations as $validation) {
161
                $key = $validation->__toString();
162
                if (!isset($tree[$key])) {
163
                    $tree[$key] = [];
164
                }
165
                $tree[$key][] = $line;
166
            }
167
        }
168
169
        $i = 0;
170
        foreach ($tree as $violation=>$lines) {
171
            $table->addRow([$violation, join(', ', Utils::numbersToRangeText($lines))]);
172
            ++$i;
173
174
            if ($i === self::MAX_VIOLATION_ERRORS) {
175
                $table->addRow(new TableSeparator());
176
                $table->addRow(array(null, 'There are more errors...'));
177
178
                break;
179
            }
180
        }
181
182
        if ($i > 0) {
183
            $table->render();
184
        }
185
    }
186
187 4
    protected function writeStatistics(array $statistics, Table $table)
188
    {
189 4
        $rows = [];
190 4
        foreach ($statistics as $k=>$v) {
191
            $rows[] = [$k, $v];
192
        }
193
194
        $table
195 4
            ->setHeaders(array('Statistics'))
196 4
            ->setRows($rows)
197
        ;
198 4
        $table->render();
199 4
    }
200
201
}
202