Completed
Pull Request — master (#1325)
by Maksim
03:02
created

FOSElasticaExtension::getDoctrineEvents()   C

Complexity

Conditions 7
Paths 10

Size

Total Lines 32
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7.392

Importance

Changes 0
Metric Value
dl 0
loc 32
ccs 16
cts 20
cp 0.8
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 23
nc 10
nop 1
crap 7.392
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 Symfony\Component\Config\FileLocator;
15
use Pagerfanta\Pagerfanta;
16
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
17
use Symfony\Component\DependencyInjection\ContainerBuilder;
18
use Symfony\Component\DependencyInjection\DefinitionDecorator;
19
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
20
use Symfony\Component\DependencyInjection\Reference;
21
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
22
23
class FOSElasticaExtension extends Extension
24
{
25
    /**
26
     * Definition of elastica clients as configured by this extension.
27
     *
28
     * @var array
29
     */
30
    private $clients = [];
31
32
    /**
33
     * An array of indexes as configured by the extension.
34
     *
35
     * @var array
36
     */
37
    private $indexConfigs = [];
38
39
    /**
40
     * If we've encountered a type mapped to a specific persistence driver, it will be loaded
41
     * here.
42
     *
43
     * @var array
44
     */
45
    private $loadedDrivers = [];
46
47 12
    public function load(array $configs, ContainerBuilder $container)
48
    {
49 12
        $configuration = $this->getConfiguration($configs, $container);
50 12
        $config = $this->processConfiguration($configuration, $configs);
51
52 12
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
53
54 12
        if (empty($config['clients']) || empty($config['indexes'])) {
55
            // No Clients or indexes are defined
56
            return;
57
        }
58
59 12
        foreach (['config', 'index', 'persister', 'provider', 'source', 'transformer', 'event_listener'] as $basename) {
60 12
            $loader->load(sprintf('%s.xml', $basename));
61
        }
62
63 12
        if (empty($config['default_client'])) {
64 12
            $keys = array_keys($config['clients']);
65 12
            $config['default_client'] = reset($keys);
66
        }
67
68 12
        if (empty($config['default_index'])) {
69 12
            $keys = array_keys($config['indexes']);
70 12
            $config['default_index'] = reset($keys);
71
        }
72
73 12
        if (isset($config['serializer'])) {
74 1
            $loader->load('serializer.xml');
75
76 1
            $this->loadSerializer($config['serializer'], $container);
77
        }
78
79 12
        $this->loadClients($config['clients'], $container);
80 12
        $container->setAlias('fos_elastica.client', sprintf('fos_elastica.client.%s', $config['default_client']));
81
82 12
        $this->loadIndexes($config['indexes'], $container);
83 12
        $container->setAlias('fos_elastica.index', sprintf('fos_elastica.index.%s', $config['default_index']));
84 12
        $container->setParameter('fos_elastica.default_index', $config['default_index']);
85
86 12
        $container->getDefinition('fos_elastica.config_source.container')->replaceArgument(0, $this->indexConfigs);
87
88 12
        $this->loadIndexManager($container);
89
90 12
        $this->createDefaultManagerAlias($config['default_manager'], $container);
91 12
    }
92
93
    /**
94
     * @param array            $config
95
     * @param ContainerBuilder $container
96
     *
97
     * @return Configuration
98
     */
99 12
    public function getConfiguration(array $config, ContainerBuilder $container)
100
    {
101 12
        return new Configuration($container->getParameter('kernel.debug'));
102
    }
103
104
    /**
105
     * Loads the configured clients.
106
     *
107
     * @param array            $clients   An array of clients configurations
108
     * @param ContainerBuilder $container A ContainerBuilder instance
109
     *
110
     * @return array
111
     */
112 12
    private function loadClients(array $clients, ContainerBuilder $container)
113
    {
114 12
        foreach ($clients as $name => $clientConfig) {
115 12
            $clientId = sprintf('fos_elastica.client.%s', $name);
116
117 12
            $clientDef = new DefinitionDecorator('fos_elastica.client_prototype');
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
118 12
            $clientDef->replaceArgument(0, $clientConfig);
119
120 12
            $logger = $clientConfig['connections'][0]['logger'];
121 12
            if (false !== $logger) {
122 12
                $clientDef->addMethodCall('setLogger', [new Reference($logger)]);
123
            }
124
125 12
            $clientDef->addTag('fos_elastica.client');
126
127 12
            $container->setDefinition($clientId, $clientDef);
128
129 12
            $this->clients[$name] = [
130 12
                'id' => $clientId,
131 12
                'reference' => new Reference($clientId),
132
            ];
133
        }
134 12
    }
135
136
    /**
137
     * Loads the configured indexes.
138
     *
139
     * @param array            $indexes   An array of indexes configurations
140
     * @param ContainerBuilder $container A ContainerBuilder instance
141
     *
142
     * @throws \InvalidArgumentException
143
     *
144
     * @return array
145
     */
146 12
    private function loadIndexes(array $indexes, ContainerBuilder $container)
147
    {
148 12
        $indexableCallbacks = [];
149
150 12
        foreach ($indexes as $name => $index) {
151 12
            $indexId = sprintf('fos_elastica.index.%s', $name);
152 12
            $indexName = isset($index['index_name']) ? $index['index_name'] : $name;
153
154 12
            $indexDef = new DefinitionDecorator('fos_elastica.index_prototype');
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
155 12
            $indexDef->setFactory([new Reference('fos_elastica.client'), 'getIndex']);
156 12
            $indexDef->replaceArgument(0, $indexName);
157 12
            $indexDef->addTag('fos_elastica.index', [
158 12
                'name' => $name,
159
            ]);
160
161 12
            if (isset($index['client'])) {
162 2
                $client = $this->getClient($index['client']);
163
164 2
                $indexDef->setFactory([$client, 'getIndex']);
165
            }
166
167 12
            $container->setDefinition($indexId, $indexDef);
168 12
            $reference = new Reference($indexId);
169
170 12
            $this->indexConfigs[$name] = [
171 12
                'elasticsearch_name' => $indexName,
172 12
                'reference' => $reference,
173 12
                'name' => $name,
174 12
                'settings' => $index['settings'],
175 12
                'type_prototype' => isset($index['type_prototype']) ? $index['type_prototype'] : [],
176 12
                'use_alias' => $index['use_alias'],
177
            ];
178
179 12
            if ($index['finder']) {
180
                $this->loadIndexFinder($container, $name, $reference);
181
            }
182
183 12
            $this->loadTypes((array) $index['types'], $container, $this->indexConfigs[$name], $indexableCallbacks);
184
        }
185
186 12
        $indexable = $container->getDefinition('fos_elastica.indexable');
187 12
        $indexable->replaceArgument(0, $indexableCallbacks);
188 12
    }
189
190
    /**
191
     * Loads the configured index finders.
192
     *
193
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
194
     * @param string                                                  $name      The index name
195
     * @param Reference                                               $index     Reference to the related index
196
     *
197
     * @return string
198
     */
199
    private function loadIndexFinder(ContainerBuilder $container, $name, Reference $index)
200
    {
201
        /* Note: transformer services may conflict with "collection.index", if
202
         * an index and type names were "collection" and an index, respectively.
203
         */
204
        $transformerId = sprintf('fos_elastica.elastica_to_model_transformer.collection.%s', $name);
205
        $transformerDef = new DefinitionDecorator('fos_elastica.elastica_to_model_transformer.collection');
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
206
        $container->setDefinition($transformerId, $transformerDef);
207
208
        $finderId = sprintf('fos_elastica.finder.%s', $name);
209
        $finderDef = new DefinitionDecorator('fos_elastica.finder');
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
210
        $finderDef->replaceArgument(0, $index);
211
        $finderDef->replaceArgument(1, new Reference($transformerId));
212
213
        $container->setDefinition($finderId, $finderDef);
214
    }
215
216
    /**
217
     * Loads the configured types.
218
     *
219
     * @param array            $types
220
     * @param ContainerBuilder $container
221
     * @param array            $indexConfig
222
     * @param array            $indexableCallbacks
223
     */
224 12
    private function loadTypes(array $types, ContainerBuilder $container, array $indexConfig, array &$indexableCallbacks)
225
    {
226 12
        foreach ($types as $name => $type) {
227 12
            $indexName = $indexConfig['name'];
228
229 12
            $typeId = sprintf('%s.%s', $indexConfig['reference'], $name);
230 12
            $typeDef = new DefinitionDecorator('fos_elastica.type_prototype');
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
231 12
            $typeDef->setFactory([$indexConfig['reference'], 'getType']);
232 12
            $typeDef->replaceArgument(0, $name);
233
234 12
            $container->setDefinition($typeId, $typeDef);
235
236
            $typeConfig = [
237 12
                'name' => $name,
238
                'mapping' => [], // An array containing anything that gets sent directly to ElasticSearch
239
                'config' => [],
240
            ];
241
242
            foreach ([
243 12
                'dynamic_templates',
244
                'properties',
245
                '_all',
246
                '_id',
247
                '_parent',
248
                '_routing',
249
                '_source',
250
            ] as $field) {
251 12
                if (isset($type[$field])) {
252 12
                    $typeConfig['mapping'][$field] = $type[$field];
253
                }
254
            }
255
256
            foreach ([
257 12
                'persistence',
258
                'serializer',
259
                'analyzer',
260
                'search_analyzer',
261
                'dynamic',
262
                'date_detection',
263
                'dynamic_date_formats',
264
                'numeric_detection',
265
            ] as $field) {
266 12
                $typeConfig['config'][$field] = array_key_exists($field, $type) ?
267 12
                    $type[$field] :
268 12
                    null;
269
            }
270
271 12
            $this->indexConfigs[$indexName]['types'][$name] = $typeConfig;
272
273 12
            if (isset($type['persistence'])) {
274 11
                $this->loadTypePersistenceIntegration($type['persistence'], $container, new Reference($typeId), $indexName, $name);
275
276 11
                $typeConfig['persistence'] = $type['persistence'];
277
            }
278
279 12
            if (isset($type['_parent'])) {
280
                // _parent mapping cannot contain `property` and `identifier`, so removing them after building `persistence`
281 2
                unset($indexConfig['types'][$name]['mapping']['_parent']['property'], $indexConfig['types'][$name]['mapping']['_parent']['identifier']);
282
            }
283
284 12
            if (isset($type['indexable_callback'])) {
285 1
                $indexableCallbacks[sprintf('%s/%s', $indexName, $name)] = $type['indexable_callback'];
286
            }
287
288 12
            if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
289 1
                $typeSerializerId = sprintf('%s.serializer.callback', $typeId);
290 1
                $typeSerializerDef = new DefinitionDecorator('fos_elastica.serializer_callback_prototype');
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
291
292 1
                if (isset($type['serializer']['groups'])) {
293 1
                    $typeSerializerDef->addMethodCall('setGroups', [$type['serializer']['groups']]);
294
                }
295
296 1
                if (isset($type['serializer']['serialize_null'])) {
297 1
                    $typeSerializerDef->addMethodCall('setSerializeNull', [$type['serializer']['serialize_null']]);
298
                }
299
300 1
                if (isset($type['serializer']['version'])) {
301 1
                    $typeSerializerDef->addMethodCall('setVersion', [$type['serializer']['version']]);
302
                }
303
304 1
                $typeDef->addMethodCall('setSerializer', [[new Reference($typeSerializerId), 'serialize']]);
305 12
                $container->setDefinition($typeSerializerId, $typeSerializerDef);
306
            }
307
        }
308 12
    }
309
310
    /**
311
     * Loads the optional provider and finder for a type.
312
     *
313
     * @param array            $typeConfig
314
     * @param ContainerBuilder $container
315
     * @param Reference        $typeRef
316
     * @param string           $indexName
317
     * @param string           $typeName
318
     */
319 11
    private function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Reference $typeRef, $indexName, $typeName)
320
    {
321 11
        if (isset($typeConfig['driver'])) {
322 11
            $this->loadDriver($container, $typeConfig['driver']);
323
        }
324
325 11
        $elasticaToModelTransformerId = $this->loadElasticaToModelTransformer($typeConfig, $container, $indexName, $typeName);
326 11
        $modelToElasticaTransformerId = $this->loadModelToElasticaTransformer($typeConfig, $container, $indexName, $typeName);
327 11
        $objectPersisterId = $this->loadObjectPersister($typeConfig, $typeRef, $container, $indexName, $typeName, $modelToElasticaTransformerId);
328
329 11
        if (isset($typeConfig['provider'])) {
330 11
            if (!class_exists(Pagerfanta::class)) {
331
                throw new \InvalidArgumentException('A pager provider needs "pagerfanta/pagerfanta:^1"  to be installed.');
332 11
            }
333 6
334
            $this->loadTypePagerProvider($typeConfig, $container, $indexName, $typeName);
335
        }
336
        if (isset($typeConfig['finder'])) {
337 6
            $this->loadTypeFinder($typeConfig, $container, $elasticaToModelTransformerId, $typeRef, $indexName, $typeName);
338
        }
339
        if (isset($typeConfig['listener'])) {
340 11
            $this->loadTypeListener($typeConfig, $container, $objectPersisterId, $indexName, $typeName);
341 11
        }
342
    }
343 11
344 11
    /**
345
     * Creates and loads an ElasticaToModelTransformer.
346 11
     *
347
     * @param array            $typeConfig
348
     * @param ContainerBuilder $container
349
     * @param string           $indexName
350
     * @param string           $typeName
351
     *
352
     * @return string
353
     */
354
    private function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
355
    {
356
        if (isset($typeConfig['elastica_to_model_transformer']['service'])) {
357
            return $typeConfig['elastica_to_model_transformer']['service'];
358 11
        }
359
360 11
        /* Note: transformer services may conflict with "prototype.driver", if
361 1
         * the index and type names were "prototype" and a driver, respectively.
362
         */
363
        $abstractId = sprintf('fos_elastica.elastica_to_model_transformer.prototype.%s', $typeConfig['driver']);
364
        $serviceId = sprintf('fos_elastica.elastica_to_model_transformer.%s.%s', $indexName, $typeName);
365
        $serviceDef = new DefinitionDecorator($abstractId);
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
366
        $serviceDef->addTag('fos_elastica.elastica_to_model_transformer', ['type' => $typeName, 'index' => $indexName]);
367 10
368 10
        // Doctrine has a mandatory service as first argument
369 10
        $argPos = ('propel' === $typeConfig['driver']) ? 0 : 1;
370 10
371
        $serviceDef->replaceArgument($argPos, $typeConfig['model']);
372
        $serviceDef->replaceArgument($argPos + 1, array_merge($typeConfig['elastica_to_model_transformer'], [
373 10
            'identifier' => $typeConfig['identifier'],
374
        ]));
375 10
        $container->setDefinition($serviceId, $serviceDef);
376 10
377 10
        return $serviceId;
378
    }
379 10
380
    /**
381 10
     * Creates and loads a ModelToElasticaTransformer for an index/type.
382
     *
383
     * @param array            $typeConfig
384
     * @param ContainerBuilder $container
385
     * @param string           $indexName
386
     * @param string           $typeName
387
     *
388
     * @return string
389
     */
390
    private function loadModelToElasticaTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
391
    {
392
        if (isset($typeConfig['model_to_elastica_transformer']['service'])) {
393
            return $typeConfig['model_to_elastica_transformer']['service'];
394 11
        }
395
396 11
        $abstractId = $container->hasDefinition('fos_elastica.serializer_callback_prototype') ?
397
            'fos_elastica.model_to_elastica_identifier_transformer' :
398
            'fos_elastica.model_to_elastica_transformer';
399
400 11
        $serviceId = sprintf('fos_elastica.model_to_elastica_transformer.%s.%s', $indexName, $typeName);
401 1
        $serviceDef = new DefinitionDecorator($abstractId);
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
402 11
        $serviceDef->replaceArgument(0, [
403
            'identifier' => $typeConfig['identifier'],
404 11
        ]);
405 11
        $container->setDefinition($serviceId, $serviceDef);
406 11
407 11
        return $serviceId;
408
    }
409 11
410
    /**
411 11
     * Creates and loads an object persister for a type.
412
     *
413
     * @param array            $typeConfig
414
     * @param Reference        $typeRef
415
     * @param ContainerBuilder $container
416
     * @param string           $indexName
417
     * @param string           $typeName
418
     * @param string           $transformerId
419
     *
420
     * @return string
421
     */
422
    private function loadObjectPersister(array $typeConfig, Reference $typeRef, ContainerBuilder $container, $indexName, $typeName, $transformerId)
423
    {
424
        if (isset($typeConfig['persister']['service'])) {
425
            return $typeConfig['persister']['service'];
426 11
        }
427
428 11
        $arguments = [
429 1
            $typeRef,
430
            new Reference($transformerId),
431
            $typeConfig['model'],
432
        ];
433 10
434 10
        if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
435 10
            $abstractId = 'fos_elastica.object_serializer_persister';
436
            $callbackId = sprintf('%s.%s.serializer.callback', $this->indexConfigs[$indexName]['reference'], $typeName);
437
            $arguments[] = [new Reference($callbackId), 'serialize'];
438 10
        } else {
439 1
            $abstractId = 'fos_elastica.object_persister';
440 1
            $mapping = $this->indexConfigs[$indexName]['types'][$typeName]['mapping'];
441 1
            $argument = $mapping['properties'];
442
            if (isset($mapping['_parent'])) {
443 9
                $argument['_parent'] = $mapping['_parent'];
444 9
            }
445 9
            $arguments[] = $argument;
446 9
        }
447 1
448
        $serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName);
449 9
        $serviceDef = new DefinitionDecorator($abstractId);
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
450
        foreach ($arguments as $i => $argument) {
451
            $serviceDef->replaceArgument($i, $argument);
452 10
        }
453 10
454 10
        $serviceDef->addTag('fos_elastica.persister', ['index' => $indexName, 'type' => $typeName]);
455 10
456
        $container->setDefinition($serviceId, $serviceDef);
457
458 10
        return $serviceId;
459
    }
460 10
461
    /**
462 10
     * Loads a provider for a type.
463
     *
464
     * @param array            $typeConfig
465
     * @param ContainerBuilder $container
466
     * @param string           $objectPersisterId
467
     * @param string           $indexName
468
     * @param string           $typpeName
0 ignored issues
show
Documentation introduced by
There is no parameter named $typpeName. Did you maybe mean $typeName?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
469
     *
470
     * @return string
471
     */
472
    private function loadTypeProvider(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
473
    {
474
        if (isset($typeConfig['provider']['service'])) {
475
            return $typeConfig['provider']['service'];
476 11
        }
477
478 11
        /* Note: provider services may conflict with "prototype.driver", if the
479
         * index and type names were "prototype" and a driver, respectively.
480
         */
481
        $providerId = sprintf('fos_elastica.provider.%s.%s', $indexName, $typeName);
482
        $providerDef = new DefinitionDecorator('fos_elastica.provider.prototype.'.$typeConfig['driver']);
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
483
        $providerDef->addTag('fos_elastica.provider', ['index' => $indexName, 'type' => $typeName]);
484
        $providerDef->replaceArgument(0, new Reference($objectPersisterId));
485 11
        $providerDef->replaceArgument(2, $typeConfig['model']);
486 11
        // Propel provider can simply ignore Doctrine-specific options
487 11
        $providerDef->replaceArgument(3, array_merge(array_diff_key($typeConfig['provider'], ['service' => 1]), [
488 11
            'indexName' => $indexName,
489 11
            'typeName' => $typeName,
490
        ]));
491 11
        $container->setDefinition($providerId, $providerDef);
492 11
493 11
        return $providerId;
494
    }
495 11
496
    /**
497 11
     * 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
    private function loadTypePagerProvider(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
507
    {
508
//     TODO don't forget to uncomment in master branch
509
//        if (isset($typeConfig['provider']['service'])) {
510 6
//            return $typeConfig['provider']['service'];
511
//        }
512
513
        $baseConfig = $typeConfig['provider'];
514
        unset($baseConfig['service']);
515
516
        $driver = $typeConfig['driver'];
517 6
518 6
        switch ($driver) {
519
            case 'orm':
520 6
                $providerDef = new DefinitionDecorator('fos_elastica.pager_provider.prototype.'.$driver);
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
521
                $providerDef->replaceArgument(2, $typeConfig['model']);
522
                $providerDef->replaceArgument(3, $baseConfig);
523 6
524 2
                break;
525 2 View Code Duplication
            case 'mongodb':
526 2
                $providerDef = new DefinitionDecorator('fos_elastica.pager_provider.prototype.'.$driver);
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
527
                $providerDef->replaceArgument(2, $typeConfig['model']);
528 2
                $providerDef->replaceArgument(3, $baseConfig);
529 4
530
                break;
531 View Code Duplication
            case 'phpcr':
532
                $providerDef = new DefinitionDecorator('fos_elastica.pager_provider.prototype.'.$driver);
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
533
                $providerDef->replaceArgument(2, $typeConfig['model']);
534
                $providerDef->replaceArgument(3, $baseConfig);
535 4
536 1
                break;
537 1 View Code Duplication
            case 'propel':
538 1
                $providerDef = new DefinitionDecorator('fos_elastica.pager_provider.prototype.'.$driver);
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
539
                $providerDef->replaceArgument(0, $typeConfig['model']);
540 1
                $providerDef->replaceArgument(1, $baseConfig);
541 3
542 3
                break;
543 3
            default:
544 3
                throw new \LogicException(sprintf('The pager provider for driver "%s" does not exist.', $driver));
545
        }
546 3
547
        /* Note: provider services may conflict with "prototype.driver", if the
548
         * index and type names were "prototype" and a driver, respectively.
549
         */
550
        $providerId = sprintf('fos_elastica.pager_provider.%s.%s', $indexName, $typeName);
551
        $providerDef->addTag('fos_elastica.pager_provider', ['index' => $indexName, 'type' => $typeName]);
552
553
        $container->setDefinition($providerId, $providerDef);
554 6
555 6
        return $providerId;
556
    }
557 6
558
    /**
559 6
     * Loads doctrine listeners to handle indexing of new or updated objects.
560
     *
561
     * @param array            $typeConfig
562
     * @param ContainerBuilder $container
563
     * @param string           $objectPersisterId
564
     * @param string           $indexName
565
     * @param string           $typeName
566
     *
567
     * @return string
568
     */
569
    private function loadTypeListener(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
570
    {
571
        if (isset($typeConfig['listener']['service'])) {
572
            return $typeConfig['listener']['service'];
573 11
        }
574
575 11
        /* Note: listener services may conflict with "prototype.driver", if the
576
         * index and type names were "prototype" and a driver, respectively.
577
         */
578
        $abstractListenerId = sprintf('fos_elastica.listener.prototype.%s', $typeConfig['driver']);
579
        $listenerId = sprintf('fos_elastica.listener.%s.%s', $indexName, $typeName);
580
        $listenerDef = new DefinitionDecorator($abstractListenerId);
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
581
        $listenerDef->replaceArgument(0, new Reference($objectPersisterId));
582 11
        $listenerDef->replaceArgument(3, $typeConfig['listener']['logger'] ?
583 11
            new Reference($typeConfig['listener']['logger']) :
584 11
            null
585 11
        );
586 11
        $listenerConfig = [
587
            'identifier' => $typeConfig['identifier'],
588 11
            'indexName' => $indexName,
589
            'typeName' => $typeName,
590
        ];
591 11
592 11
        $tagName = null;
593 11
        switch ($typeConfig['driver']) {
594
            case 'orm':
595
                $tagName = 'doctrine.event_listener';
596 11
                break;
597 11
            case 'phpcr':
598 11
                $tagName = 'doctrine_phpcr.event_listener';
599 7
                break;
600 7
            case 'mongodb':
601 4
                $tagName = 'doctrine_mongodb.odm.event_listener';
602 1
                break;
603 1
        }
604 3
605
        if ($typeConfig['listener']['defer']) {
606
            $listenerDef->setPublic(true);
607
            $listenerDef->addTag(
608
                'kernel.event_listener',
609 11
                ['event' => 'kernel.terminate', 'method' => 'onTerminate']
610
            );
611
            $listenerDef->addTag(
612
                'kernel.event_listener',
613
                ['event' => 'console.terminate', 'method' => 'onTerminate']
614
            );
615
            $listenerConfig['defer'] = true;
616
        }
617
618
        $listenerDef->replaceArgument(2, $listenerConfig);
619
620
        if (null !== $tagName) {
621
            foreach ($this->getDoctrineEvents($typeConfig) as $event) {
622 11
                $listenerDef->addTag($tagName, ['event' => $event]);
623
            }
624 11
        }
625 8
626 8
        $container->setDefinition($listenerId, $listenerDef);
627
628
        return $listenerId;
629
    }
630 11
631
    /**
632 11
     * Map Elastica to Doctrine events for the current driver.
633
     */
634
    private function getDoctrineEvents(array $typeConfig)
635
    {
636
        switch ($typeConfig['driver']) {
637
            case 'orm':
638 8
                $eventsClass = '\Doctrine\ORM\Events';
639
                break;
640 8
            case 'phpcr':
641 8
                $eventsClass = '\Doctrine\ODM\PHPCR\Event';
642 7
                break;
643 7
            case 'mongodb':
644 1
                $eventsClass = '\Doctrine\ODM\MongoDB\Events';
645 1
                break;
646 1
            default:
647
                throw new \InvalidArgumentException(sprintf('Cannot determine events for driver "%s"', $typeConfig['driver']));
648
        }
649
650
        $events = [];
651
        $eventMapping = [
652
            'insert' => [constant($eventsClass.'::postPersist')],
653
            'update' => [constant($eventsClass.'::postUpdate')],
654 8
            'delete' => [constant($eventsClass.'::preRemove')],
655
            'flush' => [constant($eventsClass.'::postFlush')],
656 8
        ];
657 8
658 8
        foreach ($eventMapping as $event => $doctrineEvents) {
659 8
            if (isset($typeConfig['listener'][$event]) && $typeConfig['listener'][$event]) {
660
                $events = array_merge($events, $doctrineEvents);
661
            }
662 8
        }
663 8
664 8
        return $events;
665
    }
666
667
    /**
668 8
     * Loads a Type specific Finder.
669
     *
670
     * @param array            $typeConfig
671
     * @param ContainerBuilder $container
672
     * @param string           $elasticaToModelId
673
     * @param Reference        $typeRef
674
     * @param string           $indexName
675
     * @param string           $typeName
676
     *
677
     * @return string
678
     */
679
    private function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, Reference $typeRef, $indexName, $typeName)
680
    {
681
        if (isset($typeConfig['finder']['service'])) {
682
            $finderId = $typeConfig['finder']['service'];
683 11
        } else {
684
            $finderId = sprintf('fos_elastica.finder.%s.%s', $indexName, $typeName);
685 11
            $finderDef = new DefinitionDecorator('fos_elastica.finder');
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
686
            $finderDef->replaceArgument(0, $typeRef);
687
            $finderDef->replaceArgument(1, new Reference($elasticaToModelId));
688 11
            $container->setDefinition($finderId, $finderDef);
689 11
        }
690 11
691 11
        $indexTypeName = "$indexName/$typeName";
692 11
        $arguments = [$indexTypeName, new Reference($finderId)];
693
        if (isset($typeConfig['repository'])) {
694
            $arguments[] = $typeConfig['repository'];
695 11
        }
696 11
697 11
        $container->getDefinition('fos_elastica.repository_manager')
698 1
            ->addMethodCall('addType', $arguments);
699
700
        $managerId = sprintf('fos_elastica.manager.%s', $typeConfig['driver']);
701 11
        $container->getDefinition($managerId)
702 11
            ->addMethodCall('addEntity', [$typeConfig['model'], $indexTypeName]);
703
704 11
        return $finderId;
705 11
    }
706 11
707
    /**
708 11
     * Loads the index manager.
709
     *
710
     * @param ContainerBuilder $container
711
     **/
712
    private function loadIndexManager(ContainerBuilder $container)
713
    {
714
        $indexRefs = array_map(function ($index) {
715
            return $index['reference'];
716
        }, $this->indexConfigs);
717
718 12
        $managerDef = $container->getDefinition('fos_elastica.index_manager');
719 12
        $managerDef->replaceArgument(0, $indexRefs);
720 12
    }
721
722 12
    /**
723 12
     * Makes sure a specific driver has been loaded.
724 12
     *
725
     * @param ContainerBuilder $container
726
     * @param string           $driver
727
     */
728
    private function loadDriver(ContainerBuilder $container, $driver)
729
    {
730
        if (in_array($driver, $this->loadedDrivers)) {
731
            return;
732 11
        }
733
734 11
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
735 3
        $loader->load($driver.'.xml');
736
        $this->loadedDrivers[] = $driver;
737
    }
738 11
739 11
    /**
740 11
     * Loads and configures the serializer prototype.
741 11
     *
742
     * @param array            $config
743
     * @param ContainerBuilder $container
744
     */
745
    private function loadSerializer($config, ContainerBuilder $container)
746
    {
747
        $container->setAlias('fos_elastica.serializer', $config['serializer']);
748
749 1
        $serializer = $container->getDefinition('fos_elastica.serializer_callback_prototype');
750
        $serializer->setClass($config['callback_class']);
751 1
752
        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...
753 1
            $serializer->addMethodCall('setContainer', [new Reference('service_container')]);
754 1
        }
755
    }
756 1
757
    /**
758
     * Creates a default manager alias for defined default manager or the first loaded driver.
759 1
     *
760
     * @param string           $defaultManager
761
     * @param ContainerBuilder $container
762
     */
763
    private function createDefaultManagerAlias($defaultManager, ContainerBuilder $container)
764
    {
765
        if (0 == count($this->loadedDrivers)) {
766
            return;
767 12
        }
768
769 12
        if (count($this->loadedDrivers) > 1
770 1
            && in_array($defaultManager, $this->loadedDrivers)
771
        ) {
772
            $defaultManagerService = $defaultManager;
773 11
        } else {
774 11
            $defaultManagerService = $this->loadedDrivers[0];
775
        }
776
777
        $container->setAlias('fos_elastica.manager', sprintf('fos_elastica.manager.%s', $defaultManagerService));
778 11
    }
779
780
    /**
781 11
     * Returns a reference to a client given its configured name.
782 11
     *
783
     * @param string $clientName
784
     *
785
     * @return Reference
786
     *
787
     * @throws \InvalidArgumentException
788
     */
789
    private function getClient($clientName)
790
    {
791
        if (!array_key_exists($clientName, $this->clients)) {
792
            throw new \InvalidArgumentException(sprintf('The elastica client with name "%s" is not defined', $clientName));
793 2
        }
794
795 2
        return $this->clients[$clientName]['reference'];
796
    }
797
}
798