Completed
Pull Request — master (#917)
by Dmitry
08:52
created

FOSElasticaExtension::loadTypeFinder()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12
Metric Value
dl 0
loc 22
ccs 0
cts 15
cp 0
rs 9.2
cc 3
eloc 16
nc 4
nop 6
crap 12
1
<?php
2
3
namespace FOS\ElasticaBundle\DependencyInjection;
4
5
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
6
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
7
use Symfony\Component\DependencyInjection\ContainerBuilder;
8
use Symfony\Component\DependencyInjection\DefinitionDecorator;
9
use Symfony\Component\DependencyInjection\Reference;
10
use Symfony\Component\Config\FileLocator;
11
use InvalidArgumentException;
12
13
class FOSElasticaExtension extends Extension
14
{
15
    /**
16
     * Definition of elastica clients as configured by this extension.
17
     *
18
     * @var array
19
     */
20
    private $clients = array();
21
22
    /**
23
     * An array of indexes as configured by the extension.
24
     *
25
     * @var array
26
     */
27
    private $indexConfigs = array();
28
29
    /**
30
     * An array of index templates as configured by the extension.
31
     *
32
     * @var array
33
     */
34
    private $indexTemplateConfigs = array();
35
36
    /**
37
     * If we've encountered a type mapped to a specific persistence driver, it will be loaded
38
     * here.
39
     *
40
     * @var array
41
     */
42
    private $loadedDrivers = array();
43
44 14
    public function load(array $configs, ContainerBuilder $container)
45
    {
46 14
        $configuration = $this->getConfiguration($configs, $container);
47 14
        $config = $this->processConfiguration($configuration, $configs);
48
49 14
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
50
51 14
        if (empty($config['clients']) || empty($config['indexes'])) {
52
            // No Clients or indexes are defined
53
            return;
54
        }
55
56 14
        foreach (array('config', 'index', 'persister', 'provider', 'source', 'transformer') as $basename) {
57 14
            $loader->load(sprintf('%s.xml', $basename));
58
        }
59
60
        if (empty($config['default_client'])) {
61
            $keys = array_keys($config['clients']);
62
            $config['default_client'] = reset($keys);
63
        }
64
65
        if (empty($config['default_index'])) {
66
            $keys = array_keys($config['indexes']);
67
            $config['default_index'] = reset($keys);
68
        }
69
70
        if (isset($config['serializer'])) {
71
            $loader->load('serializer.xml');
72
73
            $this->loadSerializer($config['serializer'], $container);
74
        }
75
76
        $this->loadClients($config['clients'], $container);
77
        $container->setAlias('fos_elastica.client', sprintf('fos_elastica.client.%s', $config['default_client']));
78
79
        $this->loadIndexes($config['indexes'], $container);
80
        $container->setAlias('fos_elastica.index', sprintf('fos_elastica.index.%s', $config['default_index']));
81
82
        $this->loadIndexTemplates($config['index_templates'], $container);
83
84
        $container->getDefinition('fos_elastica.config_source.container')->replaceArgument(0, $this->indexConfigs);
85
        $container->getDefinition('fos_elastica.config_source.template_container')->replaceArgument(0, $this->indexTemplateConfigs);
86
87
        $this->loadIndexManager($container);
88
89
        $this->createDefaultManagerAlias($config['default_manager'], $container);
90
    }
91
92
    /**
93
     * @param array            $config
94
     * @param ContainerBuilder $container
95
     *
96
     * @return Configuration
97
     */
98 14
    public function getConfiguration(array $config, ContainerBuilder $container)
99
    {
100 14
        return new Configuration($container->getParameter('kernel.debug'));
101
    }
102
103
    /**
104
     * Loads the configured clients.
105
     *
106
     * @param array            $clients   An array of clients configurations
107
     * @param ContainerBuilder $container A ContainerBuilder instance
108
     *
109
     * @return array
110
     */
111
    private function loadClients(array $clients, ContainerBuilder $container)
112
    {
113
        foreach ($clients as $name => $clientConfig) {
114
            $clientId = sprintf('fos_elastica.client.%s', $name);
115
116
            $clientDef = new DefinitionDecorator('fos_elastica.client_prototype');
117
            $clientDef->replaceArgument(0, $clientConfig);
118
119
            $logger = $clientConfig['connections'][0]['logger'];
120
            if (false !== $logger) {
121
                $clientDef->addMethodCall('setLogger', array(new Reference($logger)));
122
            }
123
124
            $clientDef->addTag('fos_elastica.client');
125
126
            $container->setDefinition($clientId, $clientDef);
127
128
            $this->clients[$name] = array(
129
                'id' => $clientId,
130
                'reference' => new Reference($clientId),
131
            );
132
        }
133
    }
134
135
    /**
136
     * Loads the configured indexes.
137
     *
138
     * @param array            $indexes   An array of indexes configurations
139
     * @param ContainerBuilder $container A ContainerBuilder instance
140
     *
141
     * @throws \InvalidArgumentException
142
     *
143
     * @return array
144
     */
145
    private function loadIndexes(array $indexes, ContainerBuilder $container)
146
    {
147
        $indexableCallbacks = array();
148
149
        foreach ($indexes as $name => $index) {
150
            $indexId = sprintf('fos_elastica.index.%s', $name);
151
            $indexName = isset($index['index_name']) ? $index['index_name'] : $name;
152
153
            $indexDef = new DefinitionDecorator('fos_elastica.index_prototype');
154
            $indexDef->replaceArgument(0, $indexName);
155
            $indexDef->addTag('fos_elastica.index', array(
156
                'name' => $name,
157
            ));
158
159
            if (method_exists($indexDef, 'setFactory')) {
160
                $indexDef->setFactory(array(new Reference('fos_elastica.client'), 'getIndex'));
161
            } else {
162
                // To be removed when dependency on Symfony DependencyInjection is bumped to 2.6
163
                $indexDef->setFactoryService('fos_elastica.client');
0 ignored issues
show
Bug introduced by
The method setFactoryService() does not exist on Symfony\Component\Depend...ion\DefinitionDecorator. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
164
                $indexDef->setFactoryMethod('getIndex');
0 ignored issues
show
Bug introduced by
The method setFactoryMethod() does not exist on Symfony\Component\Depend...ion\DefinitionDecorator. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
165
            }
166
167
            if (isset($index['client'])) {
168
                $client = $this->getClient($index['client']);
169
170
                if (method_exists($indexDef, 'setFactory')) {
171
                    $indexDef->setFactory(array($client, 'getIndex'));
172
                } else {
173
                    // To be removed when dependency on Symfony DependencyInjection is bumped to 2.6
174
                    $indexDef->setFactoryService($client);
0 ignored issues
show
Bug introduced by
The method setFactoryService() does not exist on Symfony\Component\Depend...ion\DefinitionDecorator. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
175
                    $indexDef->setFactoryMethod('getIndex');
0 ignored issues
show
Bug introduced by
The method setFactoryMethod() does not exist on Symfony\Component\Depend...ion\DefinitionDecorator. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
176
                }
177
            }
178
179
            $container->setDefinition($indexId, $indexDef);
180
            $reference = new Reference($indexId);
181
182
            $this->indexConfigs[$name] = array(
183
                'elasticsearch_name' => $indexName,
184
                'reference' => $reference,
185
                'name' => $name,
186
                'settings' => $index['settings'],
187
                'type_prototype' => isset($index['type_prototype']) ? $index['type_prototype'] : array(),
188
                'use_alias' => $index['use_alias'],
189
            );
190
191
            if ($index['finder']) {
192
                $this->loadIndexFinder($container, $name, $reference);
193
            }
194
195
            $this->loadTypes((array) $index['types'], $container, $this->indexConfigs[$name], $indexableCallbacks);
196
        }
197
198
        $indexable = $container->getDefinition('fos_elastica.indexable');
199
        $indexable->replaceArgument(0, $indexableCallbacks);
200
    }
201
202
203
    /**
204
     * Loads the configured indexes.
205
     *
206
     * @param array            $indexTemplates   An array of indexes configurations
207
     * @param ContainerBuilder $container A ContainerBuilder instance
208
     *
209
     * @throws \InvalidArgumentException
210
     *
211
     * @return array
212
     */
213
    private function loadIndexTemplates(array $indexTemplates, ContainerBuilder $container)
214
    {
215
        $indexableCallbacks = array();
216
        foreach ($indexTemplates as $name => $indexTemplate) {
217
            $indexId = sprintf('fos_elastica.index_template.%s', $name);
218
            $indexTemplateName = isset($indexTemplate['template_name']) ? $indexTemplate['template_name'] : $name;
219
220
            $indexDef = new DefinitionDecorator('fos_elastica.index_template_prototype');
221
            $indexDef->replaceArgument(0, $indexTemplateName);
222
            $indexDef->addTag('fos_elastica.index_template', array(
223
                'name' => $name,
224
            ));
225
226
            if (isset($indexTemplate['client'])) {
227
                $client = $this->getClient($indexTemplate['client']);
228
                $indexDef->setFactoryService($client);
0 ignored issues
show
Bug introduced by
The method setFactoryService() does not exist on Symfony\Component\Depend...ion\DefinitionDecorator. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
229
            }
230
231
            $container->setDefinition($indexId, $indexDef);
232
            $reference = new Reference($indexId);
233
234
            $this->indexTemplateConfigs[$name] = array(
235
                'elasticsearch_name' => $indexTemplateName,
236
                'reference' => $reference,
237
                'name' => $name,
238
                'settings' => $indexTemplate['settings'],
239
                'template' => $indexTemplate['template'],
240
            );
241
242
            $this->loadTypes((array) $indexTemplate['types'], $container, $this->indexTemplateConfigs[$name], $indexableCallbacks);
243
        }
244
    }
245
246
    /**
247
     * Loads the configured index finders.
248
     *
249
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
250
     * @param string                                                  $name      The index name
251
     * @param Reference                                               $index     Reference to the related index
252
     *
253
     * @return string
254
     */
255
    private function loadIndexFinder(ContainerBuilder $container, $name, Reference $index)
256
    {
257
        /* Note: transformer services may conflict with "collection.index", if
258
         * an index and type names were "collection" and an index, respectively.
259
         */
260
        $transformerId = sprintf('fos_elastica.elastica_to_model_transformer.collection.%s', $name);
261
        $transformerDef = new DefinitionDecorator('fos_elastica.elastica_to_model_transformer.collection');
262
        $container->setDefinition($transformerId, $transformerDef);
263
264
        $finderId = sprintf('fos_elastica.finder.%s', $name);
265
        $finderDef = new DefinitionDecorator('fos_elastica.finder');
266
        $finderDef->replaceArgument(0, $index);
267
        $finderDef->replaceArgument(1, new Reference($transformerId));
268
269
        $container->setDefinition($finderId, $finderDef);
270
    }
271
272
    /**
273
     * Loads the configured types.
274
     *
275
     * @param array            $types
276
     * @param ContainerBuilder $container
277
     * @param array            $indexConfig
278
     * @param array            $indexableCallbacks
279
     */
280
    private function loadTypes(array $types, ContainerBuilder $container, array &$indexConfig, array &$indexableCallbacks)
281
    {
282
        foreach ($types as $name => $type) {
283
            $indexName = $indexConfig['name'];
284
285
            $typeId = sprintf('%s.%s', $indexConfig['reference'], $name);
286
            $typeDef = new DefinitionDecorator('fos_elastica.type_prototype');
287
            $typeDef->replaceArgument(0, $name);
288
289
            if (method_exists($typeDef, 'setFactory')) {
290
                $typeDef->setFactory(array($indexConfig['reference'], 'getType'));
291
            } else {
292
                // To be removed when dependency on Symfony DependencyInjection is bumped to 2.6
293
                $typeDef->setFactoryService($indexConfig['reference']);
0 ignored issues
show
Bug introduced by
The method setFactoryService() does not exist on Symfony\Component\Depend...ion\DefinitionDecorator. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
294
                $typeDef->setFactoryMethod('getType');
0 ignored issues
show
Bug introduced by
The method setFactoryMethod() does not exist on Symfony\Component\Depend...ion\DefinitionDecorator. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
295
            }
296
297
            $container->setDefinition($typeId, $typeDef);
298
299
            $typeConfig = array(
300
                'name' => $name,
301
                'mapping' => array(), // An array containing anything that gets sent directly to ElasticSearch
302
                'config' => array(),
303
            );
304
305
            foreach (array(
306
                'dynamic_templates',
307
                'properties',
308
                '_all',
309
                '_boost',
310
                '_id',
311
                '_parent',
312
                '_routing',
313
                '_source',
314
                '_timestamp',
315
                '_ttl',
316
            ) as $field) {
317
                if (isset($type[$field])) {
318
                    $typeConfig['mapping'][$field] = $type[$field];
319
                }
320
            }
321
322
            foreach (array(
323
                'persistence',
324
                'serializer',
325
                'index_analyzer',
326
                'search_analyzer',
327
                'dynamic',
328
                'date_detection',
329
                'dynamic_date_formats',
330
                'numeric_detection',
331
            ) as $field) {
332
                $typeConfig['config'][$field] = array_key_exists($field, $type) ?
333
                    $type[$field] :
334
                    null;
335
            }
336
337
            $indexConfig['types'][$name] = $typeConfig;
338
339
            if (isset($type['persistence'])) {
340
                $this->loadTypePersistenceIntegration($type['persistence'], $container, new Reference($typeId), $indexName, $name);
341
342
                $typeConfig['persistence'] = $type['persistence'];
343
            }
344
345
            if (isset($type['indexable_callback'])) {
346
                $indexableCallbacks[sprintf('%s/%s', $indexName, $name)] = $type['indexable_callback'];
347
            }
348
349
            if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
350
                $typeSerializerId = sprintf('%s.serializer.callback', $typeId);
351
                $typeSerializerDef = new DefinitionDecorator('fos_elastica.serializer_callback_prototype');
352
353
                if (isset($type['serializer']['groups'])) {
354
                    $typeSerializerDef->addMethodCall('setGroups', array($type['serializer']['groups']));
355
                }
356
357
                if (isset($type['serializer']['serialize_null'])) {
358
                    $typeSerializerDef->addMethodCall('setSerializeNull', array($type['serializer']['serialize_null']));
359
                }
360
361
                if (isset($type['serializer']['version'])) {
362
                    $typeSerializerDef->addMethodCall('setVersion', array($type['serializer']['version']));
363
                }
364
365
                $typeDef->addMethodCall('setSerializer', array(array(new Reference($typeSerializerId), 'serialize')));
366
                $container->setDefinition($typeSerializerId, $typeSerializerDef);
367
            }
368
        }
369
    }
370
371
    /**
372
     * Loads the optional provider and finder for a type.
373
     *
374
     * @param array            $typeConfig
375
     * @param ContainerBuilder $container
376
     * @param Reference        $typeRef
377
     * @param string           $indexName
378
     * @param string           $typeName
379
     */
380
    private function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Reference $typeRef, $indexName, $typeName)
381
    {
382
        if (isset($typeConfig['driver'])) {
383
            $this->loadDriver($container, $typeConfig['driver']);
384
        }
385
386
        $elasticaToModelTransformerId = $this->loadElasticaToModelTransformer($typeConfig, $container, $indexName, $typeName);
387
        $modelToElasticaTransformerId = $this->loadModelToElasticaTransformer($typeConfig, $container, $indexName, $typeName);
388
        $objectPersisterId = $this->loadObjectPersister($typeConfig, $typeRef, $container, $indexName, $typeName, $modelToElasticaTransformerId);
389
390
        if (isset($typeConfig['provider'])) {
391
            $this->loadTypeProvider($typeConfig, $container, $objectPersisterId, $indexName, $typeName);
392
        }
393
        if (isset($typeConfig['finder'])) {
394
            $this->loadTypeFinder($typeConfig, $container, $elasticaToModelTransformerId, $typeRef, $indexName, $typeName);
395
        }
396
        if (isset($typeConfig['listener'])) {
397
            $this->loadTypeListener($typeConfig, $container, $objectPersisterId, $indexName, $typeName);
398
        }
399
    }
400
401
    /**
402
     * Creates and loads an ElasticaToModelTransformer.
403
     *
404
     * @param array            $typeConfig
405
     * @param ContainerBuilder $container
406
     * @param string           $indexName
407
     * @param string           $typeName
408
     *
409
     * @return string
410
     */
411
    private function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
412
    {
413
        if (isset($typeConfig['elastica_to_model_transformer']['service'])) {
414
            return $typeConfig['elastica_to_model_transformer']['service'];
415
        }
416
417
        /* Note: transformer services may conflict with "prototype.driver", if
418
         * the index and type names were "prototype" and a driver, respectively.
419
         */
420
        $abstractId = sprintf('fos_elastica.elastica_to_model_transformer.prototype.%s', $typeConfig['driver']);
421
        $serviceId = sprintf('fos_elastica.elastica_to_model_transformer.%s.%s', $indexName, $typeName);
422
        $serviceDef = new DefinitionDecorator($abstractId);
423
        $serviceDef->addTag('fos_elastica.elastica_to_model_transformer', array('type' => $typeName, 'index' => $indexName));
424
425
        // Doctrine has a mandatory service as first argument
426
        $argPos = ('propel' === $typeConfig['driver']) ? 0 : 1;
427
428
        $serviceDef->replaceArgument($argPos, $typeConfig['model']);
429
        $serviceDef->replaceArgument($argPos + 1, array_merge($typeConfig['elastica_to_model_transformer'], array(
430
            'identifier' => $typeConfig['identifier'],
431
        )));
432
        $container->setDefinition($serviceId, $serviceDef);
433
434
        return $serviceId;
435
    }
436
437
    /**
438
     * Creates and loads a ModelToElasticaTransformer for an index/type.
439
     *
440
     * @param array            $typeConfig
441
     * @param ContainerBuilder $container
442
     * @param string           $indexName
443
     * @param string           $typeName
444
     *
445
     * @return string
446
     */
447
    private function loadModelToElasticaTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
448
    {
449
        if (isset($typeConfig['model_to_elastica_transformer']['service'])) {
450
            return $typeConfig['model_to_elastica_transformer']['service'];
451
        }
452
453
        $abstractId = $container->hasDefinition('fos_elastica.serializer_callback_prototype') ?
454
            'fos_elastica.model_to_elastica_identifier_transformer' :
455
            'fos_elastica.model_to_elastica_transformer';
456
457
        $serviceId = sprintf('fos_elastica.model_to_elastica_transformer.%s.%s', $indexName, $typeName);
458
        $serviceDef = new DefinitionDecorator($abstractId);
459
        $serviceDef->replaceArgument(0, array(
460
            'identifier' => $typeConfig['identifier'],
461
        ));
462
        $container->setDefinition($serviceId, $serviceDef);
463
464
        return $serviceId;
465
    }
466
467
    /**
468
     * Creates and loads an object persister for a type.
469
     *
470
     * @param array            $typeConfig
471
     * @param Reference        $typeRef
472
     * @param ContainerBuilder $container
473
     * @param string           $indexName
474
     * @param string           $typeName
475
     * @param string           $transformerId
476
     *
477
     * @return string
478
     */
479
    private function loadObjectPersister(array $typeConfig, Reference $typeRef, ContainerBuilder $container, $indexName, $typeName, $transformerId)
480
    {
481
        if (isset($typeConfig['persister']['service'])) {
482
            return $typeConfig['persister']['service'];
483
        }
484
485
        $arguments = array(
486
            $typeRef,
487
            new Reference($transformerId),
488
            $typeConfig['model'],
489
        );
490
491
        if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
492
            $abstractId = 'fos_elastica.object_serializer_persister';
493
            $callbackId = sprintf('%s.%s.serializer.callback', $this->indexConfigs[$indexName]['reference'], $typeName);
494
            $arguments[] = array(new Reference($callbackId), 'serialize');
495
        } else {
496
            $abstractId = 'fos_elastica.object_persister';
497
            $mapping = $this->indexConfigs[$indexName]['types'][$typeName]['mapping'];
498
            $argument = $mapping['properties'];
499
            if (isset($mapping['_parent'])) {
500
                $argument['_parent'] = $mapping['_parent'];
501
            }
502
            $arguments[] = $argument;
503
        }
504
505
        $serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName);
506
        $serviceDef = new DefinitionDecorator($abstractId);
507
        foreach ($arguments as $i => $argument) {
508
            $serviceDef->replaceArgument($i, $argument);
509
        }
510
511
        $container->setDefinition($serviceId, $serviceDef);
512
513
        return $serviceId;
514
    }
515
516
    /**
517
     * Loads a provider for a type.
518
     *
519
     * @param array            $typeConfig
520
     * @param ContainerBuilder $container
521
     * @param string           $objectPersisterId
522
     * @param string           $indexName
523
     * @param string           $typeName
524
     *
525
     * @return string
526
     */
527
    private function loadTypeProvider(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
528
    {
529
        if (isset($typeConfig['provider']['service'])) {
530
            return $typeConfig['provider']['service'];
531
        }
532
533
        /* Note: provider services may conflict with "prototype.driver", if the
534
         * index and type names were "prototype" and a driver, respectively.
535
         */
536
        $providerId = sprintf('fos_elastica.provider.%s.%s', $indexName, $typeName);
537
        $providerDef = new DefinitionDecorator('fos_elastica.provider.prototype.'.$typeConfig['driver']);
538
        $providerDef->addTag('fos_elastica.provider', array('index' => $indexName, 'type' => $typeName));
539
        $providerDef->replaceArgument(0, new Reference($objectPersisterId));
540
        $providerDef->replaceArgument(2, $typeConfig['model']);
541
        // Propel provider can simply ignore Doctrine-specific options
542
        $providerDef->replaceArgument(3, array_merge(array_diff_key($typeConfig['provider'], array('service' => 1)), array(
543
            'indexName' => $indexName,
544
            'typeName' => $typeName,
545
        )));
546
        $container->setDefinition($providerId, $providerDef);
547
548
        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
    private function loadTypeListener(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
563
    {
564
        if (isset($typeConfig['listener']['service'])) {
565
            return $typeConfig['listener']['service'];
566
        }
567
568
        /* Note: listener services may conflict with "prototype.driver", if the
569
         * index and type names were "prototype" and a driver, respectively.
570
         */
571
        $abstractListenerId = sprintf('fos_elastica.listener.prototype.%s', $typeConfig['driver']);
572
        $listenerId = sprintf('fos_elastica.listener.%s.%s', $indexName, $typeName);
573
        $listenerDef = new DefinitionDecorator($abstractListenerId);
574
        $listenerDef->replaceArgument(0, new Reference($objectPersisterId));
575
        $listenerDef->replaceArgument(2, array(
576
            'identifier' => $typeConfig['identifier'],
577
            'indexName' => $indexName,
578
            'typeName' => $typeName,
579
        ));
580
        $listenerDef->replaceArgument(3, $typeConfig['listener']['logger'] ?
581
            new Reference($typeConfig['listener']['logger']) :
582
            null
583
        );
584
585
        $tagName = null;
586
        switch ($typeConfig['driver']) {
587
            case 'orm':
588
                $tagName = 'doctrine.event_listener';
589
                break;
590
            case 'phpcr':
591
                $tagName = 'doctrine_phpcr.event_listener';
592
                break;
593
            case 'mongodb':
594
                $tagName = 'doctrine_mongodb.odm.event_listener';
595
                break;
596
        }
597
598
        if (null !== $tagName) {
599
            foreach ($this->getDoctrineEvents($typeConfig) as $event) {
600
                $listenerDef->addTag($tagName, array('event' => $event));
601
            }
602
        }
603
604
        $container->setDefinition($listenerId, $listenerDef);
605
606
        return $listenerId;
607
    }
608
609
    /**
610
     * Map Elastica to Doctrine events for the current driver.
611
     */
612
    private function getDoctrineEvents(array $typeConfig)
613
    {
614
        switch ($typeConfig['driver']) {
615
            case 'orm':
616
                $eventsClass = '\Doctrine\ORM\Events';
617
                break;
618
            case 'phpcr':
619
                $eventsClass = '\Doctrine\ODM\PHPCR\Event';
620
                break;
621
            case 'mongodb':
622
                $eventsClass = '\Doctrine\ODM\MongoDB\Events';
623
                break;
624
            default:
625
                throw new InvalidArgumentException(sprintf('Cannot determine events for driver "%s"', $typeConfig['driver']));
626
        }
627
628
        $events = array();
629
        $eventMapping = array(
630
            'insert' => array(constant($eventsClass.'::postPersist')),
631
            'update' => array(constant($eventsClass.'::postUpdate')),
632
            'delete' => array(constant($eventsClass.'::preRemove')),
633
            'flush' => array($typeConfig['listener']['immediate'] ? constant($eventsClass.'::preFlush') : constant($eventsClass.'::postFlush')),
634
        );
635
636
        foreach ($eventMapping as $event => $doctrineEvents) {
637
            if (isset($typeConfig['listener'][$event]) && $typeConfig['listener'][$event]) {
638
                $events = array_merge($events, $doctrineEvents);
639
            }
640
        }
641
642
        return $events;
643
    }
644
645
    /**
646
     * Loads a Type specific Finder.
647
     *
648
     * @param array            $typeConfig
649
     * @param ContainerBuilder $container
650
     * @param string           $elasticaToModelId
651
     * @param Reference        $typeRef
652
     * @param string           $indexName
653
     * @param string           $typeName
654
     *
655
     * @return string
656
     */
657
    private function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, Reference $typeRef, $indexName, $typeName)
658
    {
659
        if (isset($typeConfig['finder']['service'])) {
660
            $finderId = $typeConfig['finder']['service'];
661
        } else {
662
            $finderId = sprintf('fos_elastica.finder.%s.%s', $indexName, $typeName);
663
            $finderDef = new DefinitionDecorator('fos_elastica.finder');
664
            $finderDef->replaceArgument(0, $typeRef);
665
            $finderDef->replaceArgument(1, new Reference($elasticaToModelId));
666
            $container->setDefinition($finderId, $finderDef);
667
        }
668
669
        $managerId = sprintf('fos_elastica.manager.%s', $typeConfig['driver']);
670
        $managerDef = $container->getDefinition($managerId);
671
        $arguments = array( $typeConfig['model'], new Reference($finderId));
672
        if (isset($typeConfig['repository'])) {
673
            $arguments[] = $typeConfig['repository'];
674
        }
675
        $managerDef->addMethodCall('addEntity', $arguments);
676
677
        return $finderId;
678
    }
679
680
    /**
681
     * Loads the index manager.
682
     *
683
     * @param ContainerBuilder $container
684
     **/
685
    private function loadIndexManager(ContainerBuilder $container)
686
    {
687
        $indexRefs = array_map(function ($index) {
688
            return $index['reference'];
689
        }, $this->indexConfigs);
690
691
        $indexTemplateRefs = array_map(function ($index) {
692
            return $index['reference'];
693
        }, $this->indexTemplateConfigs);
694
695
        $managerDef = $container->getDefinition('fos_elastica.index_manager');
696
        $managerDef->replaceArgument(0, $indexRefs);
697
        $managerDef->replaceArgument(2, $indexTemplateRefs);
698
    }
699
700
    /**
701
     * Makes sure a specific driver has been loaded.
702
     *
703
     * @param ContainerBuilder $container
704
     * @param string           $driver
705
     */
706
    private function loadDriver(ContainerBuilder $container, $driver)
707
    {
708
        if (in_array($driver, $this->loadedDrivers)) {
709
            return;
710
        }
711
712
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
713
        $loader->load($driver.'.xml');
714
        $this->loadedDrivers[] = $driver;
715
    }
716
717
    /**
718
     * Loads and configures the serializer prototype.
719
     *
720
     * @param array            $config
721
     * @param ContainerBuilder $container
722
     */
723
    private function loadSerializer($config, ContainerBuilder $container)
724
    {
725
        $container->setAlias('fos_elastica.serializer', $config['serializer']);
726
727
        $serializer = $container->getDefinition('fos_elastica.serializer_callback_prototype');
728
        $serializer->setClass($config['callback_class']);
729
730
        $callbackClassImplementedInterfaces = class_implements($config['callback_class']);
731
        if (isset($callbackClassImplementedInterfaces['Symfony\Component\DependencyInjection\ContainerAwareInterface'])) {
732
            $serializer->addMethodCall('setContainer', array(new Reference('service_container')));
733
        }
734
    }
735
736
    /**
737
     * Creates a default manager alias for defined default manager or the first loaded driver.
738
     *
739
     * @param string           $defaultManager
740
     * @param ContainerBuilder $container
741
     */
742
    private function createDefaultManagerAlias($defaultManager, ContainerBuilder $container)
743
    {
744
        if (0 == count($this->loadedDrivers)) {
745
            return;
746
        }
747
748
        if (count($this->loadedDrivers) > 1
749
            && in_array($defaultManager, $this->loadedDrivers)
750
        ) {
751
            $defaultManagerService = $defaultManager;
752
        } else {
753
            $defaultManagerService = $this->loadedDrivers[0];
754
        }
755
756
        $container->setAlias('fos_elastica.manager', sprintf('fos_elastica.manager.%s', $defaultManagerService));
757
    }
758
759
    /**
760
     * Returns a reference to a client given its configured name.
761
     *
762
     * @param string $clientName
763
     *
764
     * @return Reference
765
     *
766
     * @throws \InvalidArgumentException
767
     */
768
    private function getClient($clientName)
769
    {
770
        if (!array_key_exists($clientName, $this->clients)) {
771
            throw new InvalidArgumentException(sprintf('The elastica client with name "%s" is not defined', $clientName));
772
        }
773
774
        return $this->clients[$clientName]['reference'];
775
    }
776
}
777