Completed
Push — master ( 053498...ac8a2c )
by Karel
05:38
created

FOSElasticaExtension   F

Complexity

Total Complexity 91

Size/Duplication

Total Lines 768
Duplicated Lines 1.56 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 87.02%

Importance

Changes 0
Metric Value
wmc 91
lcom 1
cbo 9
dl 12
loc 768
ccs 295
cts 339
cp 0.8702
rs 1.832
c 0
b 0
f 0

21 Methods

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