Completed
Push — master ( e0700f...6825fb )
by Markus
03:47
created

MathielenImportEngineExtension   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 373
Duplicated Lines 2.68 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 92.83%

Importance

Changes 21
Bugs 3 Features 2
Metric Value
wmc 56
c 21
b 3
f 2
lcom 1
cbo 8
dl 10
loc 373
ccs 233
cts 251
cp 0.9283
rs 6.5957

14 Methods

Rating   Name   Duplication   Size   Complexity  
A load() 0 11 2
B parseConfig() 0 26 5
A generateObjectFactoryDef() 0 10 2
D generateFinderDef() 10 32 9
D generateImporterDef() 0 34 9
A generateFiltersDef() 0 14 2
B generateTransformerDef() 0 44 5
A generateValidatorDef() 0 11 1
B generateValidationDef() 0 55 7
A setSourceStorageDef() 0 7 1
B addStorageProviderDef() 0 55 6
A getStorageFileDefinitionFromUri() 0 10 2
B getStorageDef() 0 34 4
A getAlias() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like MathielenImportEngineExtension often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MathielenImportEngineExtension, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Mathielen\ImportEngineBundle\DependencyInjection;
3
4
use Mathielen\ImportEngine\Exception\InvalidConfigurationException;
5
use Symfony\Component\DependencyInjection\ContainerBuilder;
6
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
7
use Symfony\Component\DependencyInjection\Extension\Extension;
8
use Symfony\Component\Config\FileLocator;
9
use Symfony\Component\DependencyInjection\Reference;
10
use Symfony\Component\DependencyInjection\Definition;
11
use Symfony\Component\ExpressionLanguage\Expression;
12
13
class MathielenImportEngineExtension extends Extension
14
{
15
16 11
    public function load(array $configs, ContainerBuilder $container)
17
    {
18 11
        $config = $this->processConfiguration(new Configuration(), $configs);
19
20 11
        if (!empty($config['importers'])) {
21 9
            $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
22 9
            $loader->load('services.xml');
23
24 9
            $this->parseConfig($config, $container);
25 9
        }
26 11
    }
27
28 9
    private function parseConfig(array $config, ContainerBuilder $container)
29
    {
30 9
        $storageLocatorDef = $container->findDefinition('mathielen_importengine.import.storagelocator');
31 9
        foreach ($config['storageprovider'] as $name => $sourceConfig) {
32 5
            $this->addStorageProviderDef($storageLocatorDef, $sourceConfig, $name);
33 9
        }
34
35 9
        $importerRepositoryDef = $container->findDefinition('mathielen_importengine.importer.repository');
36 9
        foreach ($config['importers'] as $name => $importConfig) {
37 9
            $finderDef = null;
38 9
            if (isset($importConfig['preconditions'])) {
39 5
                $finderDef = $this->generateFinderDef($importConfig['preconditions']);
40 5
            }
41
42 9
            $objectFactoryDef = null;
43 9
            if (isset($importConfig['object_factory'])) {
44 5
                $objectFactoryDef = $this->generateObjectFactoryDef($importConfig['object_factory']);
45 5
            }
46
47 9
            $importerRepositoryDef->addMethodCall('register', array(
48 9
                $name,
49 9
                $this->generateImporterDef($importConfig, $objectFactoryDef),
50
                $finderDef
51 9
            ));
52 9
        }
53 9
    }
54
55 5
    private function generateObjectFactoryDef(array $config)
56
    {
57 5
        if ($config['type'] == 'jms_serializer') {
58 5
            return new Definition('Mathielen\DataImport\Writer\ObjectWriter\JmsSerializerObjectFactory', array(
59 5
                $config['class'],
60 5
                new Reference('jms_serializer')));
61
        }
62
63 5
        return new Definition('Mathielen\DataImport\Writer\ObjectWriter\DefaultObjectFactory', array($config['class']));
64
    }
65
66
    /**
67
     * @return \Symfony\Component\DependencyInjection\Definition
68
     */
69 5
    private function generateFinderDef(array $finderConfig)
70
    {
71 5
        $finderDef = new Definition('Mathielen\ImportEngine\Importer\ImporterPrecondition');
72
73 5
        if (isset($finderConfig['filename'])) {
74 5
            foreach ($finderConfig['filename'] as $conf) {
75 5
                $finderDef->addMethodCall('filename', array($conf));
76 5
            }
77 5
        }
78
79 5 View Code Duplication
        if (isset($finderConfig['format'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
80 5
            foreach ($finderConfig['format'] as $conf) {
81 5
                $finderDef->addMethodCall('format', array($conf));
82 5
            }
83 5
        }
84
85 5
        if (isset($finderConfig['fieldcount'])) {
86 5
            $finderDef->addMethodCall('fieldcount', array($finderConfig['fieldcount']));
87 5
        }
88
89 5 View Code Duplication
        if (isset($finderConfig['fields'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
90 5
            foreach ($finderConfig['fields'] as $conf) {
91 5
                $finderDef->addMethodCall('field', array($conf));
92 5
            }
93 5
        }
94
95 5
        if (isset($finderConfig['fieldset'])) {
96 5
            $finderDef->addMethodCall('fieldset', array($finderConfig['fieldset']));
97 5
        }
98
99 5
        return $finderDef;
100
    }
101
102
    /**
103
     * @return \Symfony\Component\DependencyInjection\Definition
104
     */
105 9
    private function generateImporterDef(array $importConfig, Definition $objectFactoryDef=null)
106
    {
107 9
        $importerDef = new Definition('Mathielen\ImportEngine\Importer\Importer', array(
108 9
            $this->getStorageDef($importConfig['target'], $objectFactoryDef)
109 9
        ));
110
111 9
        if (isset($importConfig['source'])) {
112 5
            $this->setSourceStorageDef($importConfig['source'], $importerDef);
113 5
        }
114
115
        //enable validation?
116 9
        if (isset($importConfig['validation']) && !empty($importConfig['validation'])) {
117 7
            $this->generateValidationDef($importConfig['validation'], $importerDef, $objectFactoryDef);
118 7
        }
119
120
        //add converters?
121 9
        if (isset($importConfig['mappings']) && !empty($importConfig['mappings'])) {
122 5
            $this->generateTransformerDef($importConfig['mappings'], $importerDef);
123 5
        }
124
125
        //add filters?
126 9
        if (isset($importConfig['filters']) && !empty($importConfig['filters'])) {
127
            $this->generateFiltersDef($importConfig['filters'], $importerDef);
128
        }
129
130
        //has static context?
131 9
        if (isset($importConfig['context'])) {
132 9
            $importerDef->addMethodCall('setContext', array(
133 9
                $importConfig['context']
134 9
            ));
135 9
        }
136
137 9
        return $importerDef;
138
    }
139
140
    private function generateFiltersDef(array $filtersOptions, Definition $importerDef)
141
    {
142
        $filtersDef = new Definition('Mathielen\ImportEngine\Filter\Filters');
143
144
        foreach ($filtersOptions as $filtersOption) {
145
            $filtersDef->addMethodCall('add', array(
146
                new Reference($filtersOption)
147
            ));
148
        }
149
150
        $importerDef->addMethodCall('filters', array(
151
            $filtersDef
152
        ));
153
    }
154
155 5
    private function generateTransformerDef(array $mappingOptions, Definition $importerDef)
156
    {
157 5
        $mappingsDef = new Definition('Mathielen\ImportEngine\Mapping\Mappings');
158
159
        //set converters
160 5
        foreach ($mappingOptions as $field=>$fieldMapping) {
161 5
            $converter = null;
162 5
            if (isset($fieldMapping['converter'])) {
163 5
                $converter = $fieldMapping['converter'];
164 5
            }
165
166 5
            if (isset($fieldMapping['to'])) {
167 5
                $mappingsDef->addMethodCall('add', array(
168 5
                    $field,
169 5
                    $fieldMapping['to'],
170
                    $converter
171 5
            ));
172 5
            } elseif ($converter) {
173 5
                $mappingsDef->addMethodCall('setConverter', array(
174 5
                    $converter,
175
                    $field
176 5
                ));
177 5
            }
178 5
        }
179
180 5
        $mappingFactoryDef = new Definition('Mathielen\ImportEngine\Mapping\DefaultMappingFactory', array(
181
            $mappingsDef
182 5
        ));
183 5
        $converterProviderDef = new Definition('Mathielen\ImportEngine\Mapping\Converter\Provider\ContainerAwareConverterProvider', array(
184 5
            new Reference('service_container')
185 5
        ));
186
187 5
        $transformerDef = new Definition('Mathielen\ImportEngine\Transformation\Transformation');
188 5
        $transformerDef->addMethodCall('setMappingFactory', array(
189
            $mappingFactoryDef
190 5
        ));
191 5
        $transformerDef->addMethodCall('setConverterProvider', array(
192
            $converterProviderDef
193 5
        ));
194
195 5
        $importerDef->addMethodCall('transformation', array(
196
            $transformerDef
197 5
        ));
198 5
    }
199
200 7
    private function generateValidatorDef(array $options)
201
    {
202
        //eventdispatcher aware source validatorfilter
203 7
        $validatorFilterDef = new Definition('Mathielen\DataImport\Filter\ValidatorFilter', array(
204 7
            new Reference('validator'),
205 7
            $options,
206 7
            new Reference('event_dispatcher')
207 7
        ));
208
209 7
        return $validatorFilterDef;
210
    }
211
212 7
    private function generateValidationDef(array $validationConfig, Definition $importerDef, Definition $objectFactoryDef=null)
213
    {
214 7
        $validationDef = new Definition('Mathielen\ImportEngine\Validation\ValidatorValidation', array(
215 7
            new Reference('validator')
216 7
        ));
217 7
        $importerDef->addMethodCall('validation', array(
218
            $validationDef
219 7
        ));
220
221 7
        $validatorFilterDef = $this->generateValidatorDef(
222 7
            isset($validationConfig['options'])?$validationConfig['options']:array()
223 7
        );
224
225 7
        if (isset($validationConfig['source'])) {
226 3
            $validationDef->addMethodCall('setSourceValidatorFilter', array(
227
                $validatorFilterDef
228 3
            ));
229
230 3
            foreach ($validationConfig['source']['constraints'] as $field=>$constraint) {
231 3
                $validationDef->addMethodCall('addSourceConstraint', array(
232 3
                    $field,
233 3
                    new Definition($constraint)
234 3
                ));
235 3
            }
236 3
        }
237
238
        //automatically apply class validation
239 7
        if (isset($validationConfig['target'])) {
240
241
            //using objects as result
242 7
            if ($objectFactoryDef) {
243
244
                //set eventdispatcher aware target CLASS-validatorfilter
245 5
                $validatorFilterDef = new Definition('Mathielen\DataImport\Filter\ClassValidatorFilter', array(
246 5
                    new Reference('validator'),
247 5
                    $objectFactoryDef,
248 5
                    new Reference('event_dispatcher')
249 5
                ));
250
251 5
            } else {
252 3
                foreach ($validationConfig['target']['constraints'] as $field=>$constraint) {
253 3
                    $validationDef->addMethodCall('addTargetConstraint', array(
254 3
                        $field,
255 3
                        new Definition($constraint)
256 3
                    ));
257 3
                }
258
            }
259
260 7
            $validationDef->addMethodCall('setTargetValidatorFilter', array(
261
                $validatorFilterDef
262 7
            ));
263 7
        }
264
265 7
        return $validationDef;
266
    }
267
268 5
    private function setSourceStorageDef(array $sourceConfig, Definition $importerDef)
269
    {
270 5
        $sDef = $this->getStorageDef($sourceConfig, $importerDef);
271 5
        $importerDef->addMethodCall('setSourceStorage', array(
272
            $sDef
273 5
        ));
274 5
    }
275
276 5
    private function addStorageProviderDef(Definition $storageLocatorDef, $config, $id = 'default')
277
    {
278 5
        $formatDiscoverLocalFileStorageFactoryDef = new Definition('Mathielen\ImportEngine\Storage\Factory\FormatDiscoverLocalFileStorageFactory', array(
279 5
            new Definition('Mathielen\ImportEngine\Storage\Format\Discovery\MimeTypeDiscoverStrategy', array(
280
                array(
281 5
                    'text/plain'=>new Definition('Mathielen\ImportEngine\Storage\Format\Factory\CsvAutoDelimiterFormatFactory'),
282 5
                    'text/csv'=>new Definition('Mathielen\ImportEngine\Storage\Format\Factory\CsvAutoDelimiterFormatFactory'),
283
                )
284 5
            )),
285 5
            new Reference('logger')
286 5
        ));
287
288 5
        switch ($config['type']) {
289 5
            case 'directory':
290 5
                $spFinderDef = new Definition('Symfony\Component\Finder\Finder');
291 5
                $spFinderDef->addMethodCall('in', array(
292 5
                    $config['uri']
293 5
                ));
294 5
                $spDef = new Definition('Mathielen\ImportEngine\Storage\Provider\FinderFileStorageProvider', array(
295 5
                    $spFinderDef,
296
                    $formatDiscoverLocalFileStorageFactoryDef
297 5
                ));
298 5
                break;
299 5
            case 'upload':
300 5
                $spDef = new Definition('Mathielen\ImportEngine\Storage\Provider\UploadFileStorageProvider', array(
301 5
                    $config['uri'],
302
                    $formatDiscoverLocalFileStorageFactoryDef
303 5
                ));
304 5
                break;
305 5
            case 'doctrine':
306 5
                $spDef = new Definition('Mathielen\ImportEngine\Storage\Provider\DoctrineQueryStorageProvider', array(
307 5
                    new Reference('doctrine.orm.entity_manager'),
308 5
                    $config['queries']
309 5
                ));
310 5
                break;
311 5
            case 'service':
312 5
                $spDef = new Definition('Mathielen\ImportEngine\Storage\Provider\ServiceStorageProvider', array(
313 5
                    new Reference('service_container'),
314 5
                    $config['services']
315 5
                ));
316 5
                break;
317 5
            case 'file':
318 5
                $spDef = new Definition('Mathielen\ImportEngine\Storage\Provider\FileStorageProvider', array(
319
                    $formatDiscoverLocalFileStorageFactoryDef
320 5
                ));
321 5
                break;
322
            default:
323
                throw new InvalidConfigurationException('Unknown type for storage provider: '.$config['type']);
324 5
        }
325
326 5
        $storageLocatorDef->addMethodCall('register', array(
327 5
            $id,
328
            $spDef
329 5
        ));
330 5
    }
331
332 9
    private function getStorageFileDefinitionFromUri($uri)
333
    {
334 9
        if (substr($uri, 0, 2) === '@=') {
335
            $uri = new Expression(substr($uri, 2));
336
        }
337
338 9
        return new Definition('SplFileInfo', array(
339
            $uri
340 9
        ));
341
    }
342
343
    /**
344
     * @return Definition
345
     */
346 9
    private function getStorageDef(array $config, Definition $objectFactoryDef=null)
347
    {
348 9
        switch ($config['type']) {
349 9
            case 'file':
350 9
                $fileDef = $this->getStorageFileDefinitionFromUri($config['uri']);
351
352 9
                $format = $config['format'];
353 9
                $storageDef = new Definition('Mathielen\ImportEngine\Storage\LocalFileStorage', array(
354 9
                    $fileDef,
355 9
                    new Definition('Mathielen\ImportEngine\Storage\Format\\'.ucfirst($format['type'])."Format", $format['arguments'])
356 9
                ));
357
358 9
                break;
359 5
            case 'doctrine':
360 5
                $storageDef = new Definition('Mathielen\ImportEngine\Storage\DoctrineStorage', array(
361 5
                    new Reference('doctrine.orm.entity_manager'),
362 5
                    $config['entity']
363 5
                ));
364
365 5
                break;
366 5
            case 'service':
367 5
                $storageDef = new Definition('Mathielen\ImportEngine\Storage\ServiceStorage', array(
368 5
                    array(new Reference($config['service']), $config['method']), //callable
369 5
                    array(),
370
                    $objectFactoryDef //from parameter array
371 5
                ));
372
373 5
                break;
374
            default:
375
                throw new InvalidConfigurationException('Unknown type for storage: '.$config['type']);
376 9
        }
377
378 9
        return $storageDef;
379
    }
380
381 11
    public function getAlias()
382
    {
383 11
        return 'mathielen_import_engine';
384
    }
385
}
386