Completed
Pull Request — master (#1331)
by
unknown
06:18
created

FOSElasticaExtension   D

Complexity

Total Complexity 92

Size/Duplication

Total Lines 768
Duplicated Lines 2.34 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 87.77%

Importance

Changes 0
Metric Value
wmc 92
lcom 1
cbo 9
dl 18
loc 768
ccs 280
cts 319
cp 0.8777
rs 4.4444
c 0
b 0
f 0

21 Methods

Rating   Name   Duplication   Size   Complexity  
C load() 0 46 7
A getConfiguration() 0 4 1
A loadClients() 0 23 3
B loadIndexes() 0 43 6
A loadIndexFinder() 0 16 1
F loadTypes() 0 85 13
C buildCallback() 0 25 7
A transformServiceReference() 0 4 2
B loadTypePersistenceIntegration() 0 20 5
B loadElasticaToModelTransformer() 0 25 3
A loadModelToElasticaTransformer() 0 19 3
B loadObjectPersister() 0 38 5
B loadTypePagerProvider() 18 50 6
C loadTypeListener() 0 61 9
C getDoctrineEvents() 0 32 7
B loadTypeFinder() 0 27 3
A loadIndexManager() 0 9 1
A loadDriver() 0 10 2
A loadSerializer() 0 11 2
A createDefaultManagerAlias() 0 17 4
A getClient() 0 8 2

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 FOSElasticaExtension 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 FOSElasticaExtension, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the FOSElasticaBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace FOS\ElasticaBundle\DependencyInjection;
13
14
use FOS\ElasticaBundle\Elastica\Client;
15
use FOS\ElasticaBundle\Manager\RepositoryManagerInterface;
16
use Symfony\Component\Config\FileLocator;
17
use Symfony\Component\DependencyInjection\ChildDefinition;
18
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
19
use Symfony\Component\DependencyInjection\ContainerBuilder;
20
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
21
use Symfony\Component\DependencyInjection\Reference;
22
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
23
24
class FOSElasticaExtension extends Extension
25
{
26
    /**
27
     * Definition of elastica clients as configured by this extension.
28
     *
29
     * @var array
30
     */
31
    private $clients = [];
32
33
    /**
34
     * An array of indexes as configured by the extension.
35
     *
36
     * @var array
37
     */
38
    private $indexConfigs = [];
39
40
    /**
41
     * If we've encountered a type mapped to a specific persistence driver, it will be loaded
42
     * here.
43
     *
44
     * @var array
45
     */
46 12
    private $loadedDrivers = [];
47
48 12
    public function load(array $configs, ContainerBuilder $container)
49 12
    {
50
        $configuration = $this->getConfiguration($configs, $container);
51 12
        $config = $this->processConfiguration($configuration, $configs);
52
53 12
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
54
55
        if (empty($config['clients']) || empty($config['indexes'])) {
56
            // No Clients or indexes are defined
57
            return;
58 12
        }
59 12
60
        foreach (['config', 'index', 'persister', 'provider', 'source', 'transformer', 'event_listener', 'commands'] as $basename) {
61
            $loader->load(sprintf('%s.xml', $basename));
62 12
        }
63 12
64 12
        if (empty($config['default_client'])) {
65
            $keys = array_keys($config['clients']);
66
            $config['default_client'] = reset($keys);
67 12
        }
68 12
69 12
        if (empty($config['default_index'])) {
70
            $keys = array_keys($config['indexes']);
71
            $config['default_index'] = reset($keys);
72 12
        }
73 1
74
        if (isset($config['serializer'])) {
75 1
            $loader->load('serializer.xml');
76
77
            $this->loadSerializer($config['serializer'], $container);
78 12
        }
79 12
80
        $this->loadClients($config['clients'], $container);
81 12
        $container->setAlias('fos_elastica.client', sprintf('fos_elastica.client.%s', $config['default_client']))->setPublic(true);
82 12
        $container->setAlias(Client::class, 'fos_elastica.client')->setPublic(false);
83 12
84
        $this->loadIndexes($config['indexes'], $container);
85 12
        $container->setAlias('fos_elastica.index', sprintf('fos_elastica.index.%s', $config['default_index']))->setPublic(true);
86
        $container->setParameter('fos_elastica.default_index', $config['default_index']);
87 12
88
        $container->getDefinition('fos_elastica.config_source.container')->replaceArgument(0, $this->indexConfigs);
89 12
90 12
        $this->loadIndexManager($container);
91
92
        $this->createDefaultManagerAlias($config['default_manager'], $container);
93
    }
94
95
    /**
96
     * @param array            $config
97
     * @param ContainerBuilder $container
98 12
     *
99
     * @return Configuration
100 12
     */
101
    public function getConfiguration(array $config, ContainerBuilder $container)
102
    {
103
        return new Configuration($container->getParameter('kernel.debug'));
104
    }
105
106
    /**
107
     * Loads the configured clients.
108
     *
109
     * @param array            $clients   An array of clients configurations
110
     * @param ContainerBuilder $container A ContainerBuilder instance
111 12
     *
112
     * @return array
113 12
     */
114 12
    private function loadClients(array $clients, ContainerBuilder $container)
115
    {
116 12
        foreach ($clients as $name => $clientConfig) {
117 12
            $clientId = sprintf('fos_elastica.client.%s', $name);
118
119 12
            $clientDef = new ChildDefinition('fos_elastica.client_prototype');
120 12
            $clientDef->replaceArgument(0, $clientConfig);
121 12
122
            $logger = $clientConfig['connections'][0]['logger'];
123
            if (false !== $logger) {
124 12
                $clientDef->addMethodCall('setLogger', [new Reference($logger)]);
125
            }
126 12
127
            $clientDef->addTag('fos_elastica.client');
128 12
129 12
            $container->setDefinition($clientId, $clientDef);
130 12
131
            $this->clients[$name] = [
132
                'id' => $clientId,
133 12
                'reference' => new Reference($clientId),
134
            ];
135
        }
136
    }
137
138
    /**
139
     * Loads the configured indexes.
140
     *
141
     * @param array            $indexes   An array of indexes configurations
142
     * @param ContainerBuilder $container A ContainerBuilder instance
143
     *
144
     * @throws \InvalidArgumentException
145 12
     *
146
     * @return array
147 12
     */
148
    private function loadIndexes(array $indexes, ContainerBuilder $container)
149 12
    {
150 12
        $indexableCallbacks = [];
151 12
152
        foreach ($indexes as $name => $index) {
153 12
            $indexId = sprintf('fos_elastica.index.%s', $name);
154 12
            $indexName = isset($index['index_name']) ? $index['index_name'] : $name;
155 12
156 12
            $indexDef = new ChildDefinition('fos_elastica.index_prototype');
157 12
            $indexDef->setFactory([new Reference('fos_elastica.client'), 'getIndex']);
158
            $indexDef->replaceArgument(0, $indexName);
159
            $indexDef->addTag('fos_elastica.index', [
160 12
                'name' => $name,
161 2
            ]);
162
163 2
            if (isset($index['client'])) {
164
                $client = $this->getClient($index['client']);
165
166 12
                $indexDef->setFactory([$client, 'getIndex']);
167 12
            }
168
169 12
            $container->setDefinition($indexId, $indexDef);
170 12
            $reference = new Reference($indexId);
171 12
172 12
            $this->indexConfigs[$name] = [
173 12
                'elasticsearch_name' => $indexName,
174 12
                'reference' => $reference,
175 12
                'name' => $name,
176
                'settings' => $index['settings'],
177
                'type_prototype' => isset($index['type_prototype']) ? $index['type_prototype'] : [],
178 12
                'use_alias' => $index['use_alias'],
179
            ];
180
181
            if ($index['finder']) {
182 12
                $this->loadIndexFinder($container, $name, $reference);
183
            }
184
185 12
            $this->loadTypes((array) $index['types'], $container, $this->indexConfigs[$name], $indexableCallbacks);
186 12
        }
187 12
188
        $indexable = $container->getDefinition('fos_elastica.indexable');
189
        $indexable->replaceArgument(0, $indexableCallbacks);
190
    }
191
192
    /**
193
     * Loads the configured index finders.
194
     *
195
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
196
     * @param string                                                  $name      The index name
197
     * @param Reference                                               $index     Reference to the related index
198
     *
199
     * @return string
200
     */
201
    private function loadIndexFinder(ContainerBuilder $container, $name, Reference $index)
202
    {
203
        /* Note: transformer services may conflict with "collection.index", if
204
         * an index and type names were "collection" and an index, respectively.
205
         */
206
        $transformerId = sprintf('fos_elastica.elastica_to_model_transformer.collection.%s', $name);
207
        $transformerDef = new ChildDefinition('fos_elastica.elastica_to_model_transformer.collection');
208
        $container->setDefinition($transformerId, $transformerDef);
209
210
        $finderId = sprintf('fos_elastica.finder.%s', $name);
211
        $finderDef = new ChildDefinition('fos_elastica.finder');
212
        $finderDef->replaceArgument(0, $index);
213
        $finderDef->replaceArgument(1, new Reference($transformerId));
214
215
        $container->setDefinition($finderId, $finderDef);
216
    }
217
218
    /**
219
     * Loads the configured types.
220
     *
221
     * @param array            $types
222
     * @param ContainerBuilder $container
223 12
     * @param array            $indexConfig
224
     * @param array            $indexableCallbacks
225 12
     */
226 12
    private function loadTypes(array $types, ContainerBuilder $container, array $indexConfig, array &$indexableCallbacks)
227
    {
228 12
        foreach ($types as $name => $type) {
229 12
            $indexName = $indexConfig['name'];
230 12
231 12
            $typeId = sprintf('%s.%s', $indexConfig['reference'], $name);
232
            $typeDef = new ChildDefinition('fos_elastica.type_prototype');
233 12
            $typeDef->setFactory([$indexConfig['reference'], 'getType']);
234
            $typeDef->replaceArgument(0, $name);
235
236 12
            $container->setDefinition($typeId, $typeDef);
237
238
            $typeConfig = [
239
                'name' => $name,
240
                'mapping' => [], // An array containing anything that gets sent directly to ElasticSearch
241
                'config' => [],
242 12
            ];
243
244
            foreach ([
245
                'dynamic_templates',
246
                'properties',
247
                '_all',
248
                '_id',
249
                '_parent',
250 12
                '_routing',
251 12
                '_source',
252
            ] as $field) {
253
                if (isset($type[$field])) {
254
                    $typeConfig['mapping'][$field] = $type[$field];
255
                }
256 12
            }
257
258
            foreach ([
259
                'persistence',
260
                'serializer',
261
                'analyzer',
262
                'search_analyzer',
263
                'dynamic',
264
                'date_detection',
265 12
                'dynamic_date_formats',
266 12
                'numeric_detection',
267 12
            ] as $field) {
268
                $typeConfig['config'][$field] = array_key_exists($field, $type) ?
269
                    $type[$field] :
270 12
                    null;
271
            }
272 12
273 11
            $this->indexConfigs[$indexName]['types'][$name] = $typeConfig;
274
275 11
            if (isset($type['persistence'])) {
276
                $this->loadTypePersistenceIntegration($type['persistence'], $container, new Reference($typeId), $indexName, $name);
277
278 12
                $typeConfig['persistence'] = $type['persistence'];
279
            }
280 2
281
            if (isset($type['_parent'])) {
282
                // _parent mapping cannot contain `property` and `identifier`, so removing them after building `persistence`
283 12
                unset($indexConfig['types'][$name]['mapping']['_parent']['property'], $indexConfig['types'][$name]['mapping']['_parent']['identifier']);
284 1
            }
285
286
            if (isset($type['indexable_callback'])) {
287 12
                $indexableCallbacks[sprintf('%s/%s', $indexName, $name)] = $this->buildCallback($type['indexable_callback'], $name);
288 1
            }
289 1
290
            if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
291 1
                $typeSerializerId = sprintf('%s.serializer.callback', $typeId);
292 1
                $typeSerializerDef = new ChildDefinition('fos_elastica.serializer_callback_prototype');
293
294
                if (isset($type['serializer']['groups'])) {
295 1
                    $typeSerializerDef->addMethodCall('setGroups', [$type['serializer']['groups']]);
296 1
                }
297
298
                if (isset($type['serializer']['serialize_null'])) {
299 1
                    $typeSerializerDef->addMethodCall('setSerializeNull', [$type['serializer']['serialize_null']]);
300 1
                }
301
302
                if (isset($type['serializer']['version'])) {
303 1
                    $typeSerializerDef->addMethodCall('setVersion', [$type['serializer']['version']]);
304 12
                }
305
306
                $typeDef->addMethodCall('setSerializer', [[new Reference($typeSerializerId), 'serialize']]);
307 12
                $container->setDefinition($typeSerializerId, $typeSerializerDef);
308
            }
309
        }
310
    }
311
312
    private function buildCallback($indexCallback, $typeName)
313
    {
314
        if (is_array($indexCallback)) {
315
            if (!isset($indexCallback[0])) {
316
                throw new \InvalidArgumentException(sprintf('Invalid indexable_callback for type %s'), $typeName);
317
            }
318 11
319
            $classOrServiceRef = $this->transformServiceReference($indexCallback[0]);
320 11
            if ($classOrServiceRef instanceof Reference && !isset($indexCallback[1])) {
321 11
                return $classOrServiceRef; // __invoke
322
            }
323
324 11
            if (!isset($indexCallback[1])) {
325 11
                throw new \InvalidArgumentException(sprintf('Invalid indexable_callback for type %s'), $typeName);
326 11
            }
327
328 11
            return [$classOrServiceRef, $indexCallback[1]];
329 11
        };
330
331 11
        if (is_string($indexCallback)) {
332 11
            return $this->transformServiceReference($indexCallback);
333
        }
334 11
335 11
        throw new \InvalidArgumentException(sprintf('Invalid indexable_callback for type %s'), $typeName);
336
    }
337 11
338
    private function transformServiceReference($classOrService)
339
    {
340
        return 0 === strpos($classOrService, '@') ? new Reference(substr($classOrService, 1)) : $classOrService;
341
    }
342
343
    /**
344
     * Loads the optional provider and finder for a type.
345
     *
346
     * @param array            $typeConfig
347
     * @param ContainerBuilder $container
348
     * @param Reference        $typeRef
349 11
     * @param string           $indexName
350
     * @param string           $typeName
351 11
     */
352 1
    private function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Reference $typeRef, $indexName, $typeName)
353
    {
354
        if (isset($typeConfig['driver'])) {
355
            $this->loadDriver($container, $typeConfig['driver']);
356
        }
357
358 10
        $elasticaToModelTransformerId = $this->loadElasticaToModelTransformer($typeConfig, $container, $indexName, $typeName);
359 10
        $modelToElasticaTransformerId = $this->loadModelToElasticaTransformer($typeConfig, $container, $indexName, $typeName);
360 10
        $objectPersisterId = $this->loadObjectPersister($typeConfig, $typeRef, $container, $indexName, $typeName, $modelToElasticaTransformerId);
361 10
362
        if (isset($typeConfig['provider'])) {
363
            $this->loadTypePagerProvider($typeConfig, $container, $indexName, $typeName);
364 10
        }
365
        if (isset($typeConfig['finder'])) {
366 10
            $this->loadTypeFinder($typeConfig, $container, $elasticaToModelTransformerId, $typeRef, $indexName, $typeName);
367 10
        }
368 10
        if (isset($typeConfig['listener'])) {
369
            $this->loadTypeListener($typeConfig, $container, $objectPersisterId, $indexName, $typeName);
370 10
        }
371
    }
372 10
373
    /**
374
     * Creates and loads an ElasticaToModelTransformer.
375
     *
376
     * @param array            $typeConfig
377
     * @param ContainerBuilder $container
378
     * @param string           $indexName
379
     * @param string           $typeName
380
     *
381
     * @return string
382
     */
383
    private function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
384
    {
385 11
        if (isset($typeConfig['elastica_to_model_transformer']['service'])) {
386
            return $typeConfig['elastica_to_model_transformer']['service'];
387 11
        }
388
389
        /* Note: transformer services may conflict with "prototype.driver", if
390
         * the index and type names were "prototype" and a driver, respectively.
391 11
         */
392 1
        $abstractId = sprintf('fos_elastica.elastica_to_model_transformer.prototype.%s', $typeConfig['driver']);
393 11
        $serviceId = sprintf('fos_elastica.elastica_to_model_transformer.%s.%s', $indexName, $typeName);
394
        $serviceDef = new ChildDefinition($abstractId);
395 11
        $serviceDef->addTag('fos_elastica.elastica_to_model_transformer', ['type' => $typeName, 'index' => $indexName]);
396 11
397 11
        // Doctrine has a mandatory service as first argument
398 11
        $argPos = ('propel' === $typeConfig['driver']) ? 0 : 1;
399
400 11
        $serviceDef->replaceArgument($argPos, $typeConfig['model']);
401
        $serviceDef->replaceArgument($argPos + 1, array_merge($typeConfig['elastica_to_model_transformer'], [
402 11
            'identifier' => $typeConfig['identifier'],
403
        ]));
404
        $container->setDefinition($serviceId, $serviceDef);
405
406
        return $serviceId;
407
    }
408
409
    /**
410
     * Creates and loads a ModelToElasticaTransformer for an index/type.
411
     *
412
     * @param array            $typeConfig
413
     * @param ContainerBuilder $container
414
     * @param string           $indexName
415
     * @param string           $typeName
416
     *
417 11
     * @return string
418
     */
419 11
    private function loadModelToElasticaTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
420 1
    {
421
        if (isset($typeConfig['model_to_elastica_transformer']['service'])) {
422
            return $typeConfig['model_to_elastica_transformer']['service'];
423
        }
424 10
425 10
        $abstractId = $container->hasDefinition('fos_elastica.serializer_callback_prototype') ?
426 10
            'fos_elastica.model_to_elastica_identifier_transformer' :
427
            'fos_elastica.model_to_elastica_transformer';
428
429 10
        $serviceId = sprintf('fos_elastica.model_to_elastica_transformer.%s.%s', $indexName, $typeName);
430 1
        $serviceDef = new ChildDefinition($abstractId);
431 1
        $serviceDef->replaceArgument(0, [
432 1
            'identifier' => $typeConfig['identifier'],
433
        ]);
434 9
        $container->setDefinition($serviceId, $serviceDef);
435 9
436 9
        return $serviceId;
437 9
    }
438 1
439
    /**
440 9
     * Creates and loads an object persister for a type.
441
     *
442
     * @param array            $typeConfig
443 10
     * @param Reference        $typeRef
444 10
     * @param ContainerBuilder $container
445 10
     * @param string           $indexName
446 10
     * @param string           $typeName
447
     * @param string           $transformerId
448
     *
449 10
     * @return string
450
     */
451 10
    private function loadObjectPersister(array $typeConfig, Reference $typeRef, ContainerBuilder $container, $indexName, $typeName, $transformerId)
452
    {
453 10
        if (isset($typeConfig['persister']['service'])) {
454
            return $typeConfig['persister']['service'];
455
        }
456
457
        $arguments = [
458
            $typeRef,
459
            new Reference($transformerId),
460
            $typeConfig['model'],
461
        ];
462
463
        if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
464
            $abstractId = 'fos_elastica.object_serializer_persister';
465
            $callbackId = sprintf('%s.%s.serializer.callback', $this->indexConfigs[$indexName]['reference'], $typeName);
466 11
            $arguments[] = [new Reference($callbackId), 'serialize'];
467
        } else {
468 11
            $abstractId = 'fos_elastica.object_persister';
469
            $mapping = $this->indexConfigs[$indexName]['types'][$typeName]['mapping'];
470
            $argument = $mapping['properties'];
471
            if (isset($mapping['_parent'])) {
472 11
                $argument['_parent'] = $mapping['_parent'];
473 11
            }
474
            $arguments[] = $argument;
475 11
        }
476
477
        $serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName);
478 11
        $serviceDef = new ChildDefinition($abstractId);
479 6
        foreach ($arguments as $i => $argument) {
480 6
            $serviceDef->replaceArgument($i, $argument);
481 6
        }
482
483 6
        $serviceDef->addTag('fos_elastica.persister', ['index' => $indexName, 'type' => $typeName]);
484 5
485
        $container->setDefinition($serviceId, $serviceDef);
486
487
        return $serviceId;
488
    }
489
490 5
    /**
491 1
     * Loads a pager provider for a type.
492 1
     *
493 1
     * @param array            $typeConfig
494
     * @param ContainerBuilder $container
495 1
     * @param string           $indexName
496 4
     * @param string           $typeName
497 4
     *
498 4
     * @return string
499 4
     */
500
    private function loadTypePagerProvider(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
501 4
    {
502
        if (isset($typeConfig['provider']['service'])) {
503
            return $typeConfig['provider']['service'];
504
        }
505
506
        $baseConfig = $typeConfig['provider'];
507
        unset($baseConfig['service']);
508
509 11
        $driver = $typeConfig['driver'];
510 11
511
        switch ($driver) {
512 11
            case 'orm':
513
                $providerDef = new ChildDefinition('fos_elastica.pager_provider.prototype.'.$driver);
514 11
                $providerDef->replaceArgument(2, $typeConfig['model']);
515
                $providerDef->replaceArgument(3, $baseConfig);
516
517
                break;
518 View Code Duplication
            case 'mongodb':
519
                $providerDef = new ChildDefinition('fos_elastica.pager_provider.prototype.'.$driver);
520
                $providerDef->replaceArgument(2, $typeConfig['model']);
521
                $providerDef->replaceArgument(3, $baseConfig);
522
523
                break;
524 View Code Duplication
            case 'phpcr':
525
                $providerDef = new ChildDefinition('fos_elastica.pager_provider.prototype.'.$driver);
526
                $providerDef->replaceArgument(2, $typeConfig['model']);
527
                $providerDef->replaceArgument(3, $baseConfig);
528 11
529
                break;
530 11 View Code Duplication
            case 'propel':
531
                $providerDef = new ChildDefinition('fos_elastica.pager_provider.prototype.'.$driver);
532
                $providerDef->replaceArgument(0, $typeConfig['model']);
533
                $providerDef->replaceArgument(1, $baseConfig);
534
535
                break;
536
            default:
537 11
                throw new \LogicException(sprintf('The pager provider for driver "%s" does not exist.', $driver));
538 11
        }
539 11
540 11
        /* Note: provider services may conflict with "prototype.driver", if the
541 11
         * index and type names were "prototype" and a driver, respectively.
542
         */
543 11
        $providerId = sprintf('fos_elastica.pager_provider.%s.%s', $indexName, $typeName);
544
        $providerDef->addTag('fos_elastica.pager_provider', ['index' => $indexName, 'type' => $typeName]);
545
546 11
        $container->setDefinition($providerId, $providerDef);
547 11
548 11
        return $providerId;
549
    }
550
551 11
    /**
552 11
     * Loads doctrine listeners to handle indexing of new or updated objects.
553 11
     *
554 6
     * @param array            $typeConfig
555 6
     * @param ContainerBuilder $container
556 5
     * @param string           $objectPersisterId
557 1
     * @param string           $indexName
558 1
     * @param string           $typeName
559 4
     *
560
     * @return string
561
     */
562
    private function loadTypeListener(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
563
    {
564 11
        if (isset($typeConfig['listener']['service'])) {
565
            return $typeConfig['listener']['service'];
566
        }
567
568
        /* Note: listener services may conflict with "prototype.driver", if the
569
         * index and type names were "prototype" and a driver, respectively.
570
         */
571
        $abstractListenerId = sprintf('fos_elastica.listener.prototype.%s', $typeConfig['driver']);
572
        $listenerId = sprintf('fos_elastica.listener.%s.%s', $indexName, $typeName);
573
        $listenerDef = new ChildDefinition($abstractListenerId);
574
        $listenerDef->replaceArgument(0, new Reference($objectPersisterId));
575
        $listenerDef->replaceArgument(3, $typeConfig['listener']['logger'] ?
576
            new Reference($typeConfig['listener']['logger']) :
577 11
            null
578
        );
579 11
        $listenerConfig = [
580 7
            'identifier' => $typeConfig['identifier'],
581 7
            'indexName' => $indexName,
582
            'typeName' => $typeName,
583
        ];
584
585 11
        $tagName = null;
586
        switch ($typeConfig['driver']) {
587 11
            case 'orm':
588
                $tagName = 'doctrine.event_listener';
589
                break;
590
            case 'phpcr':
591
                $tagName = 'doctrine_phpcr.event_listener';
592
                break;
593 7
            case 'mongodb':
594
                $tagName = 'doctrine_mongodb.odm.event_listener';
595 7
                break;
596 7
        }
597 6
598 6
        if ($typeConfig['listener']['defer']) {
599 1
            $listenerDef->setPublic(true);
600 1
            $listenerDef->addTag(
601 1
                'kernel.event_listener',
602
                ['event' => 'kernel.terminate', 'method' => 'onTerminate']
603
            );
604
            $listenerDef->addTag(
605
                'kernel.event_listener',
606
                ['event' => 'console.terminate', 'method' => 'onTerminate']
607
            );
608
            $listenerConfig['defer'] = true;
609 7
        }
610
611 7
        $listenerDef->replaceArgument(2, $listenerConfig);
612 7
613 7
        if (null !== $tagName) {
614 7
            foreach ($this->getDoctrineEvents($typeConfig) as $event) {
615
                $listenerDef->addTag($tagName, ['event' => $event]);
616
            }
617 7
        }
618 7
619 7
        $container->setDefinition($listenerId, $listenerDef);
620
621
        return $listenerId;
622
    }
623 7
624
    /**
625
     * Map Elastica to Doctrine events for the current driver.
626
     */
627
    private function getDoctrineEvents(array $typeConfig)
628
    {
629
        switch ($typeConfig['driver']) {
630
            case 'orm':
631
                $eventsClass = '\Doctrine\ORM\Events';
632
                break;
633
            case 'phpcr':
634
                $eventsClass = '\Doctrine\ODM\PHPCR\Event';
635
                break;
636
            case 'mongodb':
637
                $eventsClass = '\Doctrine\ODM\MongoDB\Events';
638 11
                break;
639
            default:
640 11
                throw new \InvalidArgumentException(sprintf('Cannot determine events for driver "%s"', $typeConfig['driver']));
641
        }
642
643 11
        $events = [];
644 11
        $eventMapping = [
645 11
            'insert' => [constant($eventsClass.'::postPersist')],
646 11
            'update' => [constant($eventsClass.'::postUpdate')],
647 11
            'delete' => [constant($eventsClass.'::preRemove')],
648
            'flush' => [constant($eventsClass.'::postFlush')],
649
        ];
650 11
651 11
        foreach ($eventMapping as $event => $doctrineEvents) {
652 11
            if (isset($typeConfig['listener'][$event]) && $typeConfig['listener'][$event]) {
653 1
                $events = array_merge($events, $doctrineEvents);
654
            }
655
        }
656 11
657 11
        return $events;
658
    }
659 11
660 11
    /**
661 11
     * Loads a Type specific Finder.
662
     *
663 11
     * @param array            $typeConfig
664
     * @param ContainerBuilder $container
665
     * @param string           $elasticaToModelId
666
     * @param Reference        $typeRef
667
     * @param string           $indexName
668
     * @param string           $typeName
669
     *
670
     * @return string
671
     */
672
    private function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, Reference $typeRef, $indexName, $typeName)
673 12
    {
674 12
        if (isset($typeConfig['finder']['service'])) {
675 12
            $finderId = $typeConfig['finder']['service'];
676
        } else {
677 12
            $finderId = sprintf('fos_elastica.finder.%s.%s', $indexName, $typeName);
678 12
            $finderDef = new ChildDefinition('fos_elastica.finder');
679 12
            $finderDef->replaceArgument(0, $typeRef);
680
            $finderDef->replaceArgument(1, new Reference($elasticaToModelId));
681
            $container->setDefinition($finderId, $finderDef);
682
        }
683
684
        $indexTypeName = "$indexName/$typeName";
685
        $arguments = [$indexTypeName, new Reference($finderId)];
686
        if (isset($typeConfig['repository'])) {
687 11
            $arguments[] = $typeConfig['repository'];
688
        }
689 11
690 3
        $container->getDefinition('fos_elastica.repository_manager')
691
            ->addMethodCall('addType', $arguments);
692
693 11
        $managerId = sprintf('fos_elastica.manager.%s', $typeConfig['driver']);
694 11
        $container->getDefinition($managerId)
695 11
            ->addMethodCall('addEntity', [$typeConfig['model'], $indexTypeName]);
696 11
697
        return $finderId;
698
    }
699
700
    /**
701
     * Loads the index manager.
702
     *
703
     * @param ContainerBuilder $container
704 1
     **/
705
    private function loadIndexManager(ContainerBuilder $container)
706 1
    {
707
        $indexRefs = array_map(function ($index) {
708 1
            return $index['reference'];
709 1
        }, $this->indexConfigs);
710
711 1
        $managerDef = $container->getDefinition('fos_elastica.index_manager');
712
        $managerDef->replaceArgument(0, $indexRefs);
713
    }
714 1
715
    /**
716
     * Makes sure a specific driver has been loaded.
717
     *
718
     * @param ContainerBuilder $container
719
     * @param string           $driver
720
     */
721
    private function loadDriver(ContainerBuilder $container, $driver)
722 12
    {
723
        if (in_array($driver, $this->loadedDrivers)) {
724 12
            return;
725 1
        }
726
727
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
728 11
        $loader->load($driver.'.xml');
729 11
        $this->loadedDrivers[] = $driver;
730
    }
731
732
    /**
733 11
     * Loads and configures the serializer prototype.
734
     *
735
     * @param array            $config
736 11
     * @param ContainerBuilder $container
737 11
     */
738
    private function loadSerializer($config, ContainerBuilder $container)
739
    {
740
        $container->setAlias('fos_elastica.serializer', $config['serializer']);
741
742
        $serializer = $container->getDefinition('fos_elastica.serializer_callback_prototype');
743
        $serializer->setClass($config['callback_class']);
744
745
        if (is_subclass_of($config['callback_class'], ContainerAwareInterface::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \Symfony\Component\Depen...erAwareInterface::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
746
            $serializer->addMethodCall('setContainer', [new Reference('service_container')]);
747
        }
748 2
    }
749
750 2
    /**
751
     * Creates a default manager alias for defined default manager or the first loaded driver.
752
     *
753
     * @param string           $defaultManager
754 2
     * @param ContainerBuilder $container
755
     */
756
    private function createDefaultManagerAlias($defaultManager, ContainerBuilder $container)
757
    {
758
        if (0 == count($this->loadedDrivers)) {
759
            return;
760
        }
761
762
        if (count($this->loadedDrivers) > 1
763
            && in_array($defaultManager, $this->loadedDrivers)
764
        ) {
765
            $defaultManagerService = $defaultManager;
766
        } else {
767
            $defaultManagerService = $this->loadedDrivers[0];
768
        }
769
770
        $container->setAlias('fos_elastica.manager', sprintf('fos_elastica.manager.%s', $defaultManagerService))->setPublic(true);
771
        $container->setAlias(RepositoryManagerInterface::class, 'fos_elastica.manager')->setPublic(false);
772
    }
773
774
    /**
775
     * Returns a reference to a client given its configured name.
776
     *
777
     * @param string $clientName
778
     *
779
     * @return Reference
780
     *
781
     * @throws \InvalidArgumentException
782
     */
783
    private function getClient($clientName)
784
    {
785
        if (!array_key_exists($clientName, $this->clients)) {
786
            throw new \InvalidArgumentException(sprintf('The elastica client with name "%s" is not defined', $clientName));
787
        }
788
789
        return $this->clients[$clientName]['reference'];
790
    }
791
}
792