ImportUrlAliasesCommand   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 194
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 113
c 5
b 0
f 0
dl 0
loc 194
ccs 0
cts 122
cp 0
rs 10
wmc 16

3 Methods

Rating   Name   Duplication   Size   Complexity  
B execute() 0 64 11
A parseInputToMapping() 0 17 4
A configure() 0 60 1
1
<?php
2
/**
3
 * @author Boudewijn Schoon <[email protected]>
4
 * @copyright Zicht Online <http://zicht.nl>
5
 */
6
7
namespace Zicht\Bundle\UrlBundle\Command;
8
9
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
10
use Symfony\Component\Console\Input\InputArgument;
11
use Symfony\Component\Console\Input\InputInterface;
12
use Symfony\Component\Console\Input\InputOption;
13
use Symfony\Component\Console\Output\OutputInterface;
14
use Zicht\Bundle\UrlBundle\Aliasing\Aliasing;
15
use Zicht\Bundle\UrlBundle\Entity\UrlAlias;
16
17
/**
18
 * Class ImportUrlAliasesCommand
19
 *
20
 * @package Zicht\Bundle\UrlBundle\Command
21
 */
22
class ImportUrlAliasesCommand extends ContainerAwareCommand
23
{
24
    /**
25
     * Defines the possible input values for the command and their associated (i.e. mapped) behavior
26
     */
27
    const REDIRECT_TYPE_MAPPING = [
28
        '0' => UrlAlias::REWRITE,
29
        '301' => UrlAlias::MOVE,
30
        '302' => UrlAlias::ALIAS,
31
        'rewrite' => UrlAlias::REWRITE,
32
        'move' => UrlAlias::MOVE,
33
        'alias' => UrlAlias::ALIAS,
34
    ];
35
36
    /**
37
     * Defines the possible input values for the command and their associated (i.e. mapped) behavior
38
     */
39
    const DEFAULT_CONFLICTING_PUBLIC_URL_STRATEGY_MAPPING = [
40
        'keep' => Aliasing::STRATEGY_KEEP,
41
        'overwrite' => Aliasing::STRATEGY_OVERWRITE,
42
        'suffix' => Aliasing::STRATEGY_SUFFIX,
43
    ];
44
45
    /**
46
     * Defines the possible input values for the command and their associated (i.e. mapped) behavior
47
     */
48
    const DEFAULT_CONFLICTING_INTERNAL_URL_STRATEGY_MAPPING = [
49
        'ignore' => Aliasing::STRATEGY_IGNORE,
50
        'move-new-to-previous' => Aliasing::STRATEGY_MOVE_NEW_TO_PREVIOUS,
51
        'move-previous-to-new' => Aliasing::STRATEGY_MOVE_PREVIOUS_TO_NEW,
52
    ];
53
54
    /**
55
     * {@inheritDoc}
56
     */
57
    protected function configure()
58
    {
59
        $this
60
            ->setName('zicht:url:import-aliases')
61
            ->setDescription("Import multiple url aliases from a source file")
62
            ->setHelp(
63
                'This command can parse csv files that follow the following syntax:
64
65
    PUBLICURL, INTERNALURL, TYPE, CONFLICTINGPUBLICURLSTRATEGY, CONFLICTINGINTERNALURLSTRATEGY
66
    /home, /nl/page/1
67
    /also-home, /nl/page/1
68
69
Note that the first line can be ignored using "--skip-header"
70
TYPE, CONFLICTINGPUBLICURLSTRATEGY, and CONFLICTINGINTERNALURLSTRATEGY are optional.'
71
            )
72
            ->addArgument(
73
                'file',
74
                InputArgument::REQUIRED,
75
                'Filepath of import CSV file'
76
            )
77
            ->addOption(
78
                'skip-header',
79
                null,
80
                InputOption::VALUE_NONE,
81
                'Skip the first line of the input file'
82
            )
83
            ->addOption(
84
                'default-redirect-type',
85
                null,
86
                InputOption::VALUE_REQUIRED,
87
                sprintf('Type of redirect, one of: %s', join(', ', array_keys(self::REDIRECT_TYPE_MAPPING))),
88
                'alias'
89
            )
90
            ->addOption(
91
                'default-conflicting-public-url-strategy',
92
                null,
93
                InputOption::VALUE_REQUIRED,
94
                sprintf('How to handle conflicting public url, one of: %s', join(', ', array_keys(self::DEFAULT_CONFLICTING_PUBLIC_URL_STRATEGY_MAPPING))),
95
                'keep'
96
            )
97
            ->addOption(
98
                'default-conflicting-internal-url-strategy',
99
                null,
100
                InputOption::VALUE_REQUIRED,
101
                sprintf('How to handle conflicting internal url, one of: %s', join(', ', array_keys(self::DEFAULT_CONFLICTING_INTERNAL_URL_STRATEGY_MAPPING))),
102
                'move-new-to-previous'
103
            )
104
            ->addOption(
105
                'csv-delimiter',
106
                null,
107
                InputOption::VALUE_REQUIRED,
108
                sprintf('Delimiter used when parsing a line of csv'),
109
                ','
110
            )
111
            ->addOption(
112
                'csv-enclosure',
113
                null,
114
                InputOption::VALUE_REQUIRED,
115
                sprintf('Enclosure used when parsing a line of csv'),
116
                '"'
117
            );
118
    }
119
120
    /**
121
     * {@inheritDoc}
122
     */
123
    protected function execute(InputInterface $input, OutputInterface $output)
124
    {
125
        $defaultRedirectType = self::REDIRECT_TYPE_MAPPING[$input->getOption('default-redirect-type')];
126
        $defaultConflictingPublicUrlStrategy = self::DEFAULT_CONFLICTING_PUBLIC_URL_STRATEGY_MAPPING[$input->getOption('default-conflicting-public-url-strategy')];
127
        $defaultConflictingInternalUrlStrategy = self::DEFAULT_CONFLICTING_INTERNAL_URL_STRATEGY_MAPPING[$input->getOption('default-conflicting-internal-url-strategy')];
128
        $csvDelimiter = $input->getOption('csv-delimiter');
129
        $csvEnclosure = $input->getOption('csv-enclosure');
130
131
        $aliasingService = $this->getContainer()->get('zicht_url.aliasing');
132
        $flush = $aliasingService->setIsBatch(true);
133
134
        $handle = fopen($input->getArgument('file'), 'r');
0 ignored issues
show
Bug introduced by
It seems like $input->getArgument('file') can also be of type string[]; however, parameter $filename of fopen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

134
        $handle = fopen(/** @scrutinizer ignore-type */ $input->getArgument('file'), 'r');
Loading history...
135
        if (false === $handle) {
136
            throw new \Exception('Can not open input file');
137
        }
138
139
        $lineNumber = 0;
140
141
        if ($input->getOption('skip-header')) {
142
            $lineNumber++;
143
            $data = fgetcsv($handle, null, $csvDelimiter, $csvEnclosure);
0 ignored issues
show
Bug introduced by
It seems like $csvDelimiter can also be of type string[]; however, parameter $delimiter of fgetcsv() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

143
            $data = fgetcsv($handle, null, /** @scrutinizer ignore-type */ $csvDelimiter, $csvEnclosure);
Loading history...
Bug introduced by
It seems like $csvEnclosure can also be of type string[]; however, parameter $enclosure of fgetcsv() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

143
            $data = fgetcsv($handle, null, $csvDelimiter, /** @scrutinizer ignore-type */ $csvEnclosure);
Loading history...
144
            if (false === $data) {
145
                throw new \Exception(sprintf('Can not read line %s in input file', $lineNumber));
146
            }
147
        }
148
149
        while ($data = fgetcsv($handle, null, $csvDelimiter, $csvEnclosure)) {
150
            $lineNumber++;
151
152
            if (false === $data) {
153
                throw new \Exception(sprintf('Can not read line %s in input file', $lineNumber));
154
            }
155
156
            if (null === $data || [null] === $data) {
157
                // skip empty line
158
                continue;
159
            }
160
161
            if (sizeof($data) < 2) {
162
                throw new \Exception('Every line in the file must have at least the publicUrl and internalUrl');
163
            }
164
165
            $publicUrl = trim($data[0]);
166
            $internalUrl = trim($data[1]);
167
            $type = $this->parseInputToMapping(self::REDIRECT_TYPE_MAPPING, $data, 2, $defaultRedirectType);
168
            $conflictingPublicUrlStrategy = $this->parseInputToMapping(self::DEFAULT_CONFLICTING_PUBLIC_URL_STRATEGY_MAPPING, $data, 3, $defaultConflictingPublicUrlStrategy);
169
            $conflictingInternalUrlStrategy = $this->parseInputToMapping(self::DEFAULT_CONFLICTING_INTERNAL_URL_STRATEGY_MAPPING, $data, 3, $defaultConflictingInternalUrlStrategy);
170
171
            $realInternalUrl = $aliasingService->hasInternalAlias($internalUrl);
172
            if (null !== $realInternalUrl && $internalUrl !== $realInternalUrl) {
173
                // the $internalUrl given in the csv is actually known as a public url in our system.
174
                // the $publicUrl given in the csv should redirect directly to this known public url.
175
                $redirectDescription = sprintf('%s -> %s -> %s', $publicUrl, $internalUrl, $realInternalUrl);
176
                $internalUrl = $realInternalUrl;
177
            } else {
178
                $redirectDescription = sprintf('%s -> %s', $publicUrl, $internalUrl);
179
            }
180
181
            // perform aliasing
182
            $aliasingService->addAlias($publicUrl, $internalUrl, $type, $conflictingPublicUrlStrategy, $conflictingInternalUrlStrategy);
183
            $output->writeln(sprintf('%s  %s  %s', $lineNumber, $type, $redirectDescription));
184
        }
185
186
        $flush();
187
    }
188
189
    /**
190
     * Return the parsed value from $data[$index]
191
     *
192
     * @param array $mapping
193
     * @param array $data
194
     * @param integer $index
195
     * @param integer $default
196
     * @return integer
197
     * @throws \Exception
198
     */
199
    private function parseInputToMapping($mapping, $data, $index, $default)
200
    {
201
        if (array_key_exists($index, $data)) {
202
            $value = strtolower(trim($data[$index]));
203
204
            if ('' === $value) {
205
                return $default;
206
            }
207
208
            if (array_key_exists($value, $mapping)) {
209
                return $mapping[$value];
210
            }
211
212
            throw new \Exception('Could not parse');
213
        }
214
215
        return $default;
216
    }
217
}
218