Completed
Pull Request — master (#1320)
by Maksim
14:06
created

FOSElasticaExtension::loadSerializer()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 2
crap 6
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 5
    public function load(array $configs, ContainerBuilder $container)
48
    {
49 5
        $configuration = $this->getConfiguration($configs, $container);
50 5
        $config = $this->processConfiguration($configuration, $configs);
51
52 5
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
53
54 5
        if (empty($config['clients']) || empty($config['indexes'])) {
55
            // No Clients or indexes are defined
56
            return;
57
        }
58
59 5
        foreach (['config', 'index', 'persister', 'provider', 'source', 'transformer', 'event_listener'] as $basename) {
60 5
            $loader->load(sprintf('%s.xml', $basename));
61
        }
62
63 5
        if (empty($config['default_client'])) {
64 5
            $keys = array_keys($config['clients']);
65 5
            $config['default_client'] = reset($keys);
66
        }
67
68 5
        if (empty($config['default_index'])) {
69 5
            $keys = array_keys($config['indexes']);
70 5
            $config['default_index'] = reset($keys);
71
        }
72
73 5
        if (isset($config['serializer'])) {
74 1
            $loader->load('serializer.xml');
75
76 1
            $this->loadSerializer($config['serializer'], $container);
77
        }
78
79 5
        $this->loadClients($config['clients'], $container);
80 5
        $container->setAlias('fos_elastica.client', sprintf('fos_elastica.client.%s', $config['default_client']));
81
82 5
        $this->loadIndexes($config['indexes'], $container);
83 5
        $container->setAlias('fos_elastica.index', sprintf('fos_elastica.index.%s', $config['default_index']));
84
        $container->setParameter('fos_elastica.default_index', $config['default_index']);
85 5
86
        $container->getDefinition('fos_elastica.config_source.container')->replaceArgument(0, $this->indexConfigs);
87 5
88
        $this->loadIndexManager($container);
89 5
90 5
        $this->createDefaultManagerAlias($config['default_manager'], $container);
91
    }
92
93
    /**
94
     * @param array            $config
95
     * @param ContainerBuilder $container
96
     *
97
     * @return Configuration
98 5
     */
99
    public function getConfiguration(array $config, ContainerBuilder $container)
100 5
    {
101
        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 5
     */
112
    private function loadClients(array $clients, ContainerBuilder $container)
113 5
    {
114 5
        foreach ($clients as $name => $clientConfig) {
115
            $clientId = sprintf('fos_elastica.client.%s', $name);
116 5
117 5
            $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
            $clientDef->replaceArgument(0, $clientConfig);
119 5
120 5
            $logger = $clientConfig['connections'][0]['logger'];
121 5
            if (false !== $logger) {
122
                $clientDef->addMethodCall('setLogger', [new Reference($logger)]);
123
            }
124 5
125
            $clientDef->addTag('fos_elastica.client');
126 5
127
            $container->setDefinition($clientId, $clientDef);
128 5
129 5
            $this->clients[$name] = [
130 5
                'id' => $clientId,
131
                'reference' => new Reference($clientId),
132
            ];
133 5
        }
134
    }
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 5
     */
146
    private function loadIndexes(array $indexes, ContainerBuilder $container)
147 5
    {
148
        $indexableCallbacks = [];
149 5
150 5
        foreach ($indexes as $name => $index) {
151 5
            $indexId = sprintf('fos_elastica.index.%s', $name);
152
            $indexName = isset($index['index_name']) ? $index['index_name'] : $name;
153 5
154 5
            $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 5
            $indexDef->setFactory([new Reference('fos_elastica.client'), 'getIndex']);
156 5
            $indexDef->replaceArgument(0, $indexName);
157 5
            $indexDef->addTag('fos_elastica.index', [
158
                'name' => $name,
159
            ]);
160 5
161 2
            if (isset($index['client'])) {
162
                $client = $this->getClient($index['client']);
163 2
164
                $indexDef->setFactory([$client, 'getIndex']);
165
            }
166 5
167 5
            $container->setDefinition($indexId, $indexDef);
168
            $reference = new Reference($indexId);
169 5
170 5
            $this->indexConfigs[$name] = [
171 5
                'elasticsearch_name' => $indexName,
172 5
                'reference' => $reference,
173 5
                'name' => $name,
174 5
                'settings' => $index['settings'],
175 5
                'type_prototype' => isset($index['type_prototype']) ? $index['type_prototype'] : [],
176
                'use_alias' => $index['use_alias'],
177
            ];
178 5
179
            if ($index['finder']) {
180
                $this->loadIndexFinder($container, $name, $reference);
181
            }
182 5
183
            $this->loadTypes((array) $index['types'], $container, $this->indexConfigs[$name], $indexableCallbacks);
184
        }
185 5
186 5
        $indexable = $container->getDefinition('fos_elastica.indexable');
187 5
        $indexable->replaceArgument(0, $indexableCallbacks);
188
    }
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 5
     */
224
    private function loadTypes(array $types, ContainerBuilder $container, array $indexConfig, array &$indexableCallbacks)
225 5
    {
226 5
        foreach ($types as $name => $type) {
227
            $indexName = $indexConfig['name'];
228 5
229 5
            $typeId = sprintf('%s.%s', $indexConfig['reference'], $name);
230 5
            $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 5
            $typeDef->setFactory([$indexConfig['reference'], 'getType']);
232
            $typeDef->replaceArgument(0, $name);
233 5
234
            $container->setDefinition($typeId, $typeDef);
235
236 5
            $typeConfig = [
237
                'name' => $name,
238
                'mapping' => [], // An array containing anything that gets sent directly to ElasticSearch
239
                'config' => [],
240
            ];
241
242 5
            foreach ([
243
                'dynamic_templates',
244
                'properties',
245
                '_all',
246
                '_id',
247
                '_parent',
248
                '_routing',
249
                '_source',
250 5
            ] as $field) {
251 5
                if (isset($type[$field])) {
252
                    $typeConfig['mapping'][$field] = $type[$field];
253
                }
254
            }
255
256 5
            foreach ([
257
                'persistence',
258
                'serializer',
259
                'analyzer',
260
                'search_analyzer',
261
                'dynamic',
262
                'date_detection',
263
                'dynamic_date_formats',
264
                'numeric_detection',
265 5
            ] as $field) {
266 5
                $typeConfig['config'][$field] = array_key_exists($field, $type) ?
267 5
                    $type[$field] :
268
                    null;
269
            }
270 5
271
            $this->indexConfigs[$indexName]['types'][$name] = $typeConfig;
272 5
273 4
            if (isset($type['persistence'])) {
274
                $this->loadTypePersistenceIntegration($type['persistence'], $container, new Reference($typeId), $indexName, $name);
275 4
276
                $typeConfig['persistence'] = $type['persistence'];
277
            }
278 5
279
            if (isset($type['_parent'])) {
280 2
                // _parent mapping cannot contain `property` and `identifier`, so removing them after building `persistence`
281
                unset($indexConfig['types'][$name]['mapping']['_parent']['property'], $indexConfig['types'][$name]['mapping']['_parent']['identifier']);
282
            }
283 5
284 1
            if (isset($type['indexable_callback'])) {
285
                $indexableCallbacks[sprintf('%s/%s', $indexName, $name)] = $type['indexable_callback'];
286
            }
287 5
288 1
            if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
289 1
                $typeSerializerId = sprintf('%s.serializer.callback', $typeId);
290
                $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 1
292 1
                if (isset($type['serializer']['groups'])) {
293
                    $typeSerializerDef->addMethodCall('setGroups', [$type['serializer']['groups']]);
294
                }
295 1
296 1
                if (isset($type['serializer']['serialize_null'])) {
297
                    $typeSerializerDef->addMethodCall('setSerializeNull', [$type['serializer']['serialize_null']]);
298
                }
299 1
300 1
                if (isset($type['serializer']['version'])) {
301
                    $typeSerializerDef->addMethodCall('setVersion', [$type['serializer']['version']]);
302
                }
303 1
304 5
                $typeDef->addMethodCall('setSerializer', [[new Reference($typeSerializerId), 'serialize']]);
305
                $container->setDefinition($typeSerializerId, $typeSerializerDef);
306
            }
307 5
        }
308
    }
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 4
     */
319
    private function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Reference $typeRef, $indexName, $typeName)
320 4
    {
321 4
        if (isset($typeConfig['driver'])) {
322
            $this->loadDriver($container, $typeConfig['driver']);
323
        }
324 4
325 4
        $elasticaToModelTransformerId = $this->loadElasticaToModelTransformer($typeConfig, $container, $indexName, $typeName);
326 4
        $modelToElasticaTransformerId = $this->loadModelToElasticaTransformer($typeConfig, $container, $indexName, $typeName);
327
        $objectPersisterId = $this->loadObjectPersister($typeConfig, $typeRef, $container, $indexName, $typeName, $modelToElasticaTransformerId);
328 4
329 4
        if (isset($typeConfig['provider'])) {
330
            $this->loadTypeProvider($typeConfig, $container, $objectPersisterId, $indexName, $typeName);
331 4
332 4
            if (isset($typeConfig['provider']['pager_provider']) && $typeConfig['provider']['pager_provider']) {
333
                if (!class_exists(Pagerfanta::class)) {
334 4
                    throw new \InvalidArgumentException('A pager provider needs "pagerfanta/pagerfanta:^1"  to be installed.');
335 4
                }
336
337 4
                $this->loadTypePagerProvider($typeConfig, $container, $indexName, $typeName);
338
            }
339
        }
340
        if (isset($typeConfig['finder'])) {
341
            $this->loadTypeFinder($typeConfig, $container, $elasticaToModelTransformerId, $typeRef, $indexName, $typeName);
342
        }
343
        if (isset($typeConfig['listener'])) {
344
            $this->loadTypeListener($typeConfig, $container, $objectPersisterId, $indexName, $typeName);
345
        }
346
    }
347
348
    /**
349 4
     * Creates and loads an ElasticaToModelTransformer.
350
     *
351 4
     * @param array            $typeConfig
352 1
     * @param ContainerBuilder $container
353
     * @param string           $indexName
354
     * @param string           $typeName
355
     *
356
     * @return string
357
     */
358 3
    private function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
359 3
    {
360 3
        if (isset($typeConfig['elastica_to_model_transformer']['service'])) {
361 3
            return $typeConfig['elastica_to_model_transformer']['service'];
362
        }
363
364 3
        /* Note: transformer services may conflict with "prototype.driver", if
365
         * the index and type names were "prototype" and a driver, respectively.
366 3
         */
367 3
        $abstractId = sprintf('fos_elastica.elastica_to_model_transformer.prototype.%s', $typeConfig['driver']);
368 3
        $serviceId = sprintf('fos_elastica.elastica_to_model_transformer.%s.%s', $indexName, $typeName);
369
        $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...
370 3
        $serviceDef->addTag('fos_elastica.elastica_to_model_transformer', ['type' => $typeName, 'index' => $indexName]);
371
372 3
        // Doctrine has a mandatory service as first argument
373
        $argPos = ('propel' === $typeConfig['driver']) ? 0 : 1;
374
375
        $serviceDef->replaceArgument($argPos, $typeConfig['model']);
376
        $serviceDef->replaceArgument($argPos + 1, array_merge($typeConfig['elastica_to_model_transformer'], [
377
            'identifier' => $typeConfig['identifier'],
378
        ]));
379
        $container->setDefinition($serviceId, $serviceDef);
380
381
        return $serviceId;
382
    }
383
384
    /**
385 4
     * Creates and loads a ModelToElasticaTransformer for an index/type.
386
     *
387 4
     * @param array            $typeConfig
388
     * @param ContainerBuilder $container
389
     * @param string           $indexName
390
     * @param string           $typeName
391 4
     *
392 1
     * @return string
393 4
     */
394
    private function loadModelToElasticaTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
395 4
    {
396 4
        if (isset($typeConfig['model_to_elastica_transformer']['service'])) {
397 4
            return $typeConfig['model_to_elastica_transformer']['service'];
398 4
        }
399
400 4
        $abstractId = $container->hasDefinition('fos_elastica.serializer_callback_prototype') ?
401
            'fos_elastica.model_to_elastica_identifier_transformer' :
402 4
            'fos_elastica.model_to_elastica_transformer';
403
404
        $serviceId = sprintf('fos_elastica.model_to_elastica_transformer.%s.%s', $indexName, $typeName);
405
        $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...
406
        $serviceDef->replaceArgument(0, [
407
            'identifier' => $typeConfig['identifier'],
408
        ]);
409
        $container->setDefinition($serviceId, $serviceDef);
410
411
        return $serviceId;
412
    }
413
414
    /**
415
     * Creates and loads an object persister for a type.
416
     *
417 4
     * @param array            $typeConfig
418
     * @param Reference        $typeRef
419 4
     * @param ContainerBuilder $container
420 1
     * @param string           $indexName
421
     * @param string           $typeName
422
     * @param string           $transformerId
423
     *
424 3
     * @return string
425 3
     */
426 3
    private function loadObjectPersister(array $typeConfig, Reference $typeRef, ContainerBuilder $container, $indexName, $typeName, $transformerId)
427
    {
428
        if (isset($typeConfig['persister']['service'])) {
429 3
            return $typeConfig['persister']['service'];
430 1
        }
431 1
432 1
        $arguments = [
433
            $typeRef,
434 2
            new Reference($transformerId),
435 2
            $typeConfig['model'],
436 2
        ];
437 2
438 1
        if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
439
            $abstractId = 'fos_elastica.object_serializer_persister';
440 2
            $callbackId = sprintf('%s.%s.serializer.callback', $this->indexConfigs[$indexName]['reference'], $typeName);
441
            $arguments[] = [new Reference($callbackId), 'serialize'];
442
        } else {
443 3
            $abstractId = 'fos_elastica.object_persister';
444 3
            $mapping = $this->indexConfigs[$indexName]['types'][$typeName]['mapping'];
445 3
            $argument = $mapping['properties'];
446 3
            if (isset($mapping['_parent'])) {
447
                $argument['_parent'] = $mapping['_parent'];
448
            }
449 3
            $arguments[] = $argument;
450
        }
451 3
452
        $serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName);
453
        $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...
454
        foreach ($arguments as $i => $argument) {
455
            $serviceDef->replaceArgument($i, $argument);
456
        }
457
458
        $serviceDef->addTag('fos_elastica.persister', ['index' => $indexName, 'type' => $typeName]);
459
460
        $container->setDefinition($serviceId, $serviceDef);
461
462
        return $serviceId;
463
    }
464
465 4
    /**
466
     * Loads a provider for a type.
467 4
     *
468
     * @param array            $typeConfig
469
     * @param ContainerBuilder $container
470
     * @param string           $objectPersisterId
471
     * @param string           $indexName
472
     * @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...
473
     *
474 4
     * @return string
475 4
     */
476 4
    private function loadTypeProvider(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
477 4
    {
478 4
        if (isset($typeConfig['provider']['service'])) {
479
            return $typeConfig['provider']['service'];
480 4
        }
481 4
482 4
        /* Note: provider services may conflict with "prototype.driver", if the
483
         * index and type names were "prototype" and a driver, respectively.
484 4
         */
485
        $providerId = sprintf('fos_elastica.provider.%s.%s', $indexName, $typeName);
486 4
        $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...
487
        $providerDef->addTag('fos_elastica.provider', ['index' => $indexName, 'type' => $typeName]);
488
        $providerDef->replaceArgument(0, new Reference($objectPersisterId));
489
        $providerDef->replaceArgument(2, $typeConfig['model']);
490
        // Propel provider can simply ignore Doctrine-specific options
491
        $providerDef->replaceArgument(3, array_merge(array_diff_key($typeConfig['provider'], ['service' => 1]), [
492
            'indexName' => $indexName,
493
            'typeName' => $typeName,
494
        ]));
495
        $container->setDefinition($providerId, $providerDef);
496
497
        return $providerId;
498
    }
499
500 4
    /**
501
     * Loads a pager provider for a type.
502 4
     *
503
     * @param array            $typeConfig
504
     * @param ContainerBuilder $container
505
     * @param string           $indexName
506
     * @param string           $typeName
507
     *
508
     * @return string
509 4
     */
510 4
    private function loadTypePagerProvider(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
511 4
    {
512 4
//     TODO don't forget to uncomment in master branch
513 4
//        if (isset($typeConfig['provider']['service'])) {
514
//            return $typeConfig['provider']['service'];
515 4
//        }
516
517
        $baseConfig = $typeConfig['provider'];
518 4
        unset($baseConfig['service']);
519 4
520 4
        $driver = $typeConfig['driver'];
521
522
        switch ($driver) {
523 4
            case 'orm':
524 4
                $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...
525 4
                $providerDef->replaceArgument(1, $typeConfig['model']);
526 4
                $providerDef->replaceArgument(2, $baseConfig);
527 4
528
                break;
529 View Code Duplication
            case 'mongodb':
530
                $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...
531
                $providerDef->replaceArgument(1, $typeConfig['model']);
532
                $providerDef->replaceArgument(2, $baseConfig);
533
534
                break;
535 View Code Duplication
            case 'phpcr':
536 4
                $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...
537
                $providerDef->replaceArgument(1, $typeConfig['model']);
538
                $providerDef->replaceArgument(2, $baseConfig);
539
540
                break;
541 View Code Duplication
            case 'propel':
542
                $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...
543
                $providerDef->replaceArgument(0, $typeConfig['model']);
544
                $providerDef->replaceArgument(1, $baseConfig);
545
546
                break;
547
            default:
548
                throw new \LogicException(sprintf('The pager provider for driver "%s" does not exist.', $driver));
549 4
        }
550
551 4
        /* Note: provider services may conflict with "prototype.driver", if the
552 4
         * index and type names were "prototype" and a driver, respectively.
553 4
         */
554
        $providerId = sprintf('fos_elastica.pager_provider.%s.%s', $indexName, $typeName);
555
        $providerDef->addTag('fos_elastica.pager_provider', ['index' => $indexName, 'type' => $typeName]);
556
557 4
        $container->setDefinition($providerId, $providerDef);
558
559 4
        return $providerId;
560
    }
561
562
    /**
563
     * Loads doctrine listeners to handle indexing of new or updated objects.
564
     *
565 4
     * @param array            $typeConfig
566
     * @param ContainerBuilder $container
567 4
     * @param string           $objectPersisterId
568 4
     * @param string           $indexName
569 4
     * @param string           $typeName
570 4
     *
571
     * @return string
572
     */
573
    private function loadTypeListener(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
574
    {
575
        if (isset($typeConfig['listener']['service'])) {
576
            return $typeConfig['listener']['service'];
577
        }
578
579
        /* Note: listener services may conflict with "prototype.driver", if the
580
         * index and type names were "prototype" and a driver, respectively.
581 4
         */
582
        $abstractListenerId = sprintf('fos_elastica.listener.prototype.%s', $typeConfig['driver']);
583 4
        $listenerId = sprintf('fos_elastica.listener.%s.%s', $indexName, $typeName);
584 4
        $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...
585 4
        $listenerDef->replaceArgument(0, new Reference($objectPersisterId));
586 4
        $listenerDef->replaceArgument(3, $typeConfig['listener']['logger'] ?
587
            new Reference($typeConfig['listener']['logger']) :
588
            null
589 4
        );
590 4
        $listenerConfig = [
591 4
            'identifier' => $typeConfig['identifier'],
592
            'indexName' => $indexName,
593
            'typeName' => $typeName,
594
        ];
595 4
596
        $tagName = null;
597
        switch ($typeConfig['driver']) {
598
            case 'orm':
599
                $tagName = 'doctrine.event_listener';
600
                break;
601
            case 'phpcr':
602
                $tagName = 'doctrine_phpcr.event_listener';
603
                break;
604
            case 'mongodb':
605
                $tagName = 'doctrine_mongodb.odm.event_listener';
606
                break;
607
        }
608
609
        if ($typeConfig['listener']['defer']) {
610 4
            $listenerDef->setPublic(true);
611
            $listenerDef->addTag(
612 4
                'kernel.event_listener',
613
                ['event' => 'kernel.terminate', 'method' => 'onTerminate']
614
            );
615 4
            $listenerDef->addTag(
616 4
                'kernel.event_listener',
617 4
                ['event' => 'console.terminate', 'method' => 'onTerminate']
618 4
            );
619 4
            $listenerConfig['defer'] = true;
620
        }
621
622 4
        $listenerDef->replaceArgument(2, $listenerConfig);
623 4
624 4
        if (null !== $tagName) {
625 1
            foreach ($this->getDoctrineEvents($typeConfig) as $event) {
626
                $listenerDef->addTag($tagName, ['event' => $event]);
627
            }
628 4
        }
629 4
630
        $container->setDefinition($listenerId, $listenerDef);
631 4
632 4
        return $listenerId;
633 4
    }
634
635 4
    /**
636
     * Map Elastica to Doctrine events for the current driver.
637
     */
638
    private function getDoctrineEvents(array $typeConfig)
639
    {
640
        switch ($typeConfig['driver']) {
641
            case 'orm':
642
                $eventsClass = '\Doctrine\ORM\Events';
643
                break;
644
            case 'phpcr':
645 5
                $eventsClass = '\Doctrine\ODM\PHPCR\Event';
646 5
                break;
647 5
            case 'mongodb':
648
                $eventsClass = '\Doctrine\ODM\MongoDB\Events';
649 5
                break;
650 5
            default:
651 5
                throw new \InvalidArgumentException(sprintf('Cannot determine events for driver "%s"', $typeConfig['driver']));
652
        }
653
654
        $events = [];
655
        $eventMapping = [
656
            'insert' => [constant($eventsClass.'::postPersist')],
657
            'update' => [constant($eventsClass.'::postUpdate')],
658
            'delete' => [constant($eventsClass.'::preRemove')],
659 4
            'flush' => [constant($eventsClass.'::postFlush')],
660
        ];
661 4
662 3
        foreach ($eventMapping as $event => $doctrineEvents) {
663
            if (isset($typeConfig['listener'][$event]) && $typeConfig['listener'][$event]) {
664
                $events = array_merge($events, $doctrineEvents);
665 4
            }
666 4
        }
667 4
668 4
        return $events;
669
    }
670
671
    /**
672
     * Loads a Type specific Finder.
673
     *
674
     * @param array            $typeConfig
675
     * @param ContainerBuilder $container
676 1
     * @param string           $elasticaToModelId
677
     * @param Reference        $typeRef
678 1
     * @param string           $indexName
679
     * @param string           $typeName
680 1
     *
681 1
     * @return string
682
     */
683 1
    private function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, Reference $typeRef, $indexName, $typeName)
684
    {
685
        if (isset($typeConfig['finder']['service'])) {
686 1
            $finderId = $typeConfig['finder']['service'];
687
        } else {
688
            $finderId = sprintf('fos_elastica.finder.%s.%s', $indexName, $typeName);
689
            $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...
690
            $finderDef->replaceArgument(0, $typeRef);
691
            $finderDef->replaceArgument(1, new Reference($elasticaToModelId));
692
            $container->setDefinition($finderId, $finderDef);
693
        }
694 5
695
        $indexTypeName = "$indexName/$typeName";
696 5
        $arguments = [$indexTypeName, new Reference($finderId)];
697 1
        if (isset($typeConfig['repository'])) {
698
            $arguments[] = $typeConfig['repository'];
699
        }
700 4
701 4
        $container->getDefinition('fos_elastica.repository_manager')
702
            ->addMethodCall('addType', $arguments);
703
704
        $managerId = sprintf('fos_elastica.manager.%s', $typeConfig['driver']);
705 4
        $container->getDefinition($managerId)
706
            ->addMethodCall('addEntity', [$typeConfig['model'], $indexTypeName]);
707
708 4
        return $finderId;
709 4
    }
710
711
    /**
712
     * Loads the index manager.
713
     *
714
     * @param ContainerBuilder $container
715
     **/
716
    private function loadIndexManager(ContainerBuilder $container)
717
    {
718
        $indexRefs = array_map(function ($index) {
719
            return $index['reference'];
720 2
        }, $this->indexConfigs);
721
722 2
        $managerDef = $container->getDefinition('fos_elastica.index_manager');
723
        $managerDef->replaceArgument(0, $indexRefs);
724
    }
725
726 2
    /**
727
     * Makes sure a specific driver has been loaded.
728
     *
729
     * @param ContainerBuilder $container
730
     * @param string           $driver
731
     */
732
    private function loadDriver(ContainerBuilder $container, $driver)
733
    {
734
        if (in_array($driver, $this->loadedDrivers)) {
735
            return;
736
        }
737
738
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
739
        $loader->load($driver.'.xml');
740
        $this->loadedDrivers[] = $driver;
741
    }
742
743
    /**
744
     * Loads and configures the serializer prototype.
745
     *
746
     * @param array            $config
747
     * @param ContainerBuilder $container
748
     */
749
    private function loadSerializer($config, ContainerBuilder $container)
750
    {
751
        $container->setAlias('fos_elastica.serializer', $config['serializer']);
752
753
        $serializer = $container->getDefinition('fos_elastica.serializer_callback_prototype');
754
        $serializer->setClass($config['callback_class']);
755
756
        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...
757
            $serializer->addMethodCall('setContainer', [new Reference('service_container')]);
758
        }
759
    }
760
761
    /**
762
     * Creates a default manager alias for defined default manager or the first loaded driver.
763
     *
764
     * @param string           $defaultManager
765
     * @param ContainerBuilder $container
766
     */
767
    private function createDefaultManagerAlias($defaultManager, ContainerBuilder $container)
768
    {
769
        if (0 == count($this->loadedDrivers)) {
770
            return;
771
        }
772
773
        if (count($this->loadedDrivers) > 1
774
            && in_array($defaultManager, $this->loadedDrivers)
775
        ) {
776
            $defaultManagerService = $defaultManager;
777
        } else {
778
            $defaultManagerService = $this->loadedDrivers[0];
779
        }
780
781
        $container->setAlias('fos_elastica.manager', sprintf('fos_elastica.manager.%s', $defaultManagerService));
782
    }
783
784
    /**
785
     * Returns a reference to a client given its configured name.
786
     *
787
     * @param string $clientName
788
     *
789
     * @return Reference
790
     *
791
     * @throws \InvalidArgumentException
792
     */
793
    private function getClient($clientName)
794
    {
795
        if (!array_key_exists($clientName, $this->clients)) {
796
            throw new \InvalidArgumentException(sprintf('The elastica client with name "%s" is not defined', $clientName));
797
        }
798
799
        return $this->clients[$clientName]['reference'];
800
    }
801
}
802