Completed
Pull Request — master (#1343)
by Dmitry
08:46
created

FOSElasticaExtension::loadIndexTemplates()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 42

Duplication

Lines 4
Ratio 9.52 %

Code Coverage

Tests 13
CRAP Score 5

Importance

Changes 0
Metric Value
dl 4
loc 42
ccs 13
cts 13
cp 1
rs 8.9368
c 0
b 0
f 0
cc 5
nc 10
nop 2
crap 5
1
<?php
2
3
/*
4
 * This file is part of the FOSElasticaBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace FOS\ElasticaBundle\DependencyInjection;
13
14
use Elastica\Client as ElasticaClient;
15
use FOS\ElasticaBundle\Elastica\Client;
16
use FOS\ElasticaBundle\Manager\RepositoryManagerInterface;
17
use Symfony\Component\Config\FileLocator;
18
use Symfony\Component\DependencyInjection\Alias;
19
use Symfony\Component\DependencyInjection\ChildDefinition;
20
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
21
use Symfony\Component\DependencyInjection\ContainerBuilder;
22
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
23
use Symfony\Component\DependencyInjection\Reference;
24
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
25
26
class FOSElasticaExtension extends Extension
27
{
28
    /**
29
     * Definition of elastica clients as configured by this extension.
30
     *
31
     * @var array
32
     */
33
    private $clients = [];
34
35
    /**
36
     * An array of indexes as configured by the extension.
37
     *
38
     * @var array
39
     */
40
    private $indexConfigs = [];
41
42
    /**
43
     * An array of index templates as configured by the extension.
44
     *
45
     * @var array
46
     */
47
    private $indexTemplateConfigs = array();
48
49
    /**
50 18
     * If we've encountered a type mapped to a specific persistence driver, it will be loaded
51
     * here.
52 18
     *
53 18
     * @var array
54
     */
55 18
    private $loadedDrivers = [];
56
57 18
    public function load(array $configs, ContainerBuilder $container)
58
    {
59
        $configuration = $this->getConfiguration($configs, $container);
60
        $config = $this->processConfiguration($configuration, $configs);
61
62 18
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
63 18
64
        if (empty($config['clients']) || empty($config['indexes'])) {
65
            // No Clients or indexes are defined
66 18
            return;
67 18
        }
68 18
69
        foreach (['config', 'index', 'persister', 'provider', 'source', 'transformer', 'event_listener', 'commands'] as $basename) {
70
            $loader->load(sprintf('%s.xml', $basename));
71 18
        }
72 18
73 18
        if (empty($config['default_client'])) {
74
            $keys = array_keys($config['clients']);
75
            $config['default_client'] = reset($keys);
76 18
        }
77 1
78
        if (empty($config['default_index'])) {
79 1
            $keys = array_keys($config['indexes']);
80
            $config['default_index'] = reset($keys);
81
        }
82 18
83 18
        if (isset($config['serializer'])) {
84 18
            $loader->load('serializer.xml');
85 18
86 18
            $this->loadSerializer($config['serializer'], $container);
87 18
        }
88
89 18
        $this->loadClients($config['clients'], $container);
90 18
        $container->setAlias('fos_elastica.client', sprintf('fos_elastica.client.%s', $config['default_client']));
91 18
        $container->getAlias('fos_elastica.client')->setPublic(true);
92 18
        $container->setAlias(ElasticaClient::class, new Alias('fos_elastica.client', false));
93
        $container->setAlias(Client::class, 'fos_elastica.client');
94 18
        $container->getAlias(Client::class)->setPublic(false);
95
96 18
        $this->loadIndexes($config['indexes'], $container);
97
        $container->setAlias('fos_elastica.index', sprintf('fos_elastica.index.%s', $config['default_index']));
98 18
        $container->getAlias('fos_elastica.index')->setPublic(true);
99 18
        $container->setParameter('fos_elastica.default_index', $config['default_index']);
100
101
        if ($usedIndexNames = \array_intersect_key($config['indexes'], $config['index_templates'])) {
102
            throw new \DomainException(
103
                \sprintf(
104
                    'Index names "%s" are already in use and can not be used for index templates names',
105
                    \implode('","', \array_keys($usedIndexNames))
106
                )
107 18
            );
108
        }
109 18
        $this->loadIndexTemplates($config['index_templates'], $container);
110
111
        $container->getDefinition('fos_elastica.config_source.container')->replaceArgument(0, $this->indexConfigs);
112
        $container
113
            ->getDefinition('fos_elastica.config_source.template_container')
114
            ->replaceArgument(0, $this->indexTemplateConfigs);
115
116
        $this->loadIndexManager($container);
117
        $this->loadIndexTemplateManager($container);
118
119
        $this->createDefaultManagerAlias($config['default_manager'], $container);
120 18
    }
121
122 18
    /**
123 18
     * @param array            $config
124
     * @param ContainerBuilder $container
125 18
     *
126 18
     * @return Configuration
127
     */
128 18
    public function getConfiguration(array $config, ContainerBuilder $container)
129 18
    {
130 18
        return new Configuration($container->getParameter('kernel.debug'));
131
    }
132
133 18
    /**
134
     * Loads the configured clients.
135 18
     *
136
     * @param array            $clients   An array of clients configurations
137 18
     * @param ContainerBuilder $container A ContainerBuilder instance
138 18
     *
139 18
     * @return array
140
     */
141
    private function loadClients(array $clients, ContainerBuilder $container)
142 18
    {
143
        foreach ($clients as $name => $clientConfig) {
144
            $clientId = sprintf('fos_elastica.client.%s', $name);
145
146
            $clientDef = new ChildDefinition('fos_elastica.client_prototype');
147
            $clientDef->replaceArgument(0, $clientConfig);
148
149
            $logger = $clientConfig['connections'][0]['logger'];
150
            if (false !== $logger) {
151
                $clientDef->addMethodCall('setLogger', [new Reference($logger)]);
152
            }
153
154 18
            $clientDef->addTag('fos_elastica.client');
155
156 18
            $container->setDefinition($clientId, $clientDef);
157
158 18
            $this->clients[$name] = [
159 18
                'id' => $clientId,
160 18
                'reference' => new Reference($clientId),
161
            ];
162 18
        }
163 18
    }
164 18
165 18
    /**
166 18
     * Loads the configured indexes.
167
     *
168
     * @param array            $indexes   An array of indexes configurations
169 18
     * @param ContainerBuilder $container A ContainerBuilder instance
170 2
     *
171
     * @throws \InvalidArgumentException
172 2
     *
173
     * @return array
174
     */
175 18
    private function loadIndexes(array $indexes, ContainerBuilder $container)
176 18
    {
177
        $indexableCallbacks = [];
178 18
179 18
        foreach ($indexes as $name => $index) {
180 18
            $indexId = sprintf('fos_elastica.index.%s', $name);
181 18
            $indexName = isset($index['index_name']) ? $index['index_name'] : $name;
182 18
183 18
            $indexDef = new ChildDefinition('fos_elastica.index_prototype');
184 18
            $indexDef->setFactory([new Reference('fos_elastica.client'), 'getIndex']);
185
            $indexDef->replaceArgument(0, $indexName);
186
            $indexDef->addTag('fos_elastica.index', [
187 18
                'name' => $name,
188
            ]);
189
190 View Code Duplication
            if (isset($index['client'])) {
191 18
                $client = $this->getClient($index['client']);
192
193
                $indexDef->setFactory([$client, 'getIndex']);
194 18
            }
195 18
196 18
            $container->setDefinition($indexId, $indexDef);
197
            $reference = new Reference($indexId);
198
199
            $this->indexConfigs[$name] = [
200
                'elasticsearch_name' => $indexName,
201
                'reference' => $reference,
202
                'name' => $name,
203
                'settings' => $index['settings'],
204
                'type_prototype' => isset($index['type_prototype']) ? $index['type_prototype'] : [],
205
                'use_alias' => $index['use_alias'],
206
            ];
207
208
            if ($index['finder']) {
209
                $this->loadIndexFinder($container, $name, $reference);
210
            }
211
212
            $this->loadTypes((array) $index['types'], $container, $this->indexConfigs[$name], $indexableCallbacks);
213
        }
214
215
        $indexable = $container->getDefinition('fos_elastica.indexable');
216
        $indexable->replaceArgument(0, $indexableCallbacks);
217
    }
218
219
    /**
220
     * Loads the configured indexes.
221
     *
222
     * @param array            $indexTemplates   An array of indexes configurations
223
     * @param ContainerBuilder $container A ContainerBuilder instance
224
     *
225
     * @throws \InvalidArgumentException
226
     *
227
     * @return void
228
     */
229
    private function loadIndexTemplates(array $indexTemplates, ContainerBuilder $container)
230
    {
231
        $indexableCallbacks = array();
232 18
        foreach ($indexTemplates as $name => $indexTemplate) {
233
            $indexId = sprintf('fos_elastica.index_template.%s', $name);
234 18
            $indexTemplateName = isset($indexTemplate['template_name']) ? $indexTemplate['template_name'] : $name;
235 18
236
            $indexDef = new ChildDefinition('fos_elastica.index_template_prototype');
237 18
            $indexDef->setFactory([new Reference('fos_elastica.client'), 'getIndexTemplate']);
238 18
            $indexDef->replaceArgument(0, $indexTemplateName);
239 18
            $indexDef->addTag('fos_elastica.index_template', array(
240 18
                'name' => $name,
241
            ));
242 18
243 View Code Duplication
            if (isset($indexTemplate['client'])) {
244
                $client = $this->getClient($indexTemplate['client']);
245 18
                $indexDef->setFactory([$client, 'getIndexTemplate']);
246
            }
247
248
            $container->setDefinition($indexId, $indexDef);
249
            $reference = new Reference($indexId);
250
251 18
            $this->indexTemplateConfigs[$name] = array(
252
                'elasticsearch_name' => $indexTemplateName,
253
                'reference' => $reference,
254
                'name' => $name,
255
                'settings' => $indexTemplate['settings'],
256
                'template' => $indexTemplate['template'],
257
            );
258
259 18
            $this->loadTypes(
260 18
                (array) $indexTemplate['types'],
261
                $container,
262
                $this->indexTemplateConfigs[$name],
263
                $indexableCallbacks
264
            );
265 18
        }
266
267
        if ($indexableCallbacks) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $indexableCallbacks of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
268
            throw new \RuntimeException('`indexable_callback` option is not supported by index templates');
269
        }
270
    }
271
272
    /**
273
     * Loads the configured index finders.
274 18
     *
275 18
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
276 18
     * @param string                                                  $name      The index name
277
     * @param Reference                                               $index     Reference to the related index
278
     *
279 18
     * @return string
280
     */
281 18
    private function loadIndexFinder(ContainerBuilder $container, $name, Reference $index)
282 15
    {
283
        /* Note: transformer services may conflict with "collection.index", if
284 15
         * an index and type names were "collection" and an index, respectively.
285
         */
286
        $transformerId = sprintf('fos_elastica.elastica_to_model_transformer.collection.%s', $name);
287 18
        $transformerDef = new ChildDefinition('fos_elastica.elastica_to_model_transformer.collection');
288
        $container->setDefinition($transformerId, $transformerDef);
289 4
290
        $finderId = sprintf('fos_elastica.finder.%s', $name);
291
        $finderDef = new ChildDefinition('fos_elastica.finder');
292 18
        $finderDef->replaceArgument(0, $index);
293 4
        $finderDef->replaceArgument(1, new Reference($transformerId));
294
295
        $container->setDefinition($finderId, $finderDef);
296 18
    }
297 1
298 1
    /**
299
     * Loads the configured types.
300 1
     *
301 1
     * @param array            $types
302
     * @param ContainerBuilder $container
303
     * @param array            $indexConfig
304 1
     * @param array            $indexableCallbacks
305 1
     */
306
    private function loadTypes(array $types, ContainerBuilder $container, array &$indexConfig, array &$indexableCallbacks)
307
    {
308 1
        foreach ($types as $name => $type) {
309 1
            $indexName = $indexConfig['name'];
310
311
            $typeId = sprintf('%s.%s', $indexConfig['reference'], $name);
312 1
            $typeDef = new ChildDefinition('fos_elastica.type_prototype');
313 18
            $typeDef->setFactory([$indexConfig['reference'], 'getType']);
314
            $typeDef->replaceArgument(0, $name);
315
316 18
            $container->setDefinition($typeId, $typeDef);
317
318 4
            $typeConfig = [
319
                'name' => $name,
320 4
                'mapping' => [], // An array containing anything that gets sent directly to ElasticSearch
321 4
                'config' => [],
322
            ];
323
324
            foreach ([
325 4
                'dynamic_templates',
326 4
                'properties',
327
                '_all',
328
                '_id',
329
                '_parent',
330 4
                '_routing',
331
                '_source',
332
            ] as $field) {
333
                if (isset($type[$field])) {
334 4
                    $typeConfig['mapping'][$field] = $type[$field];
335
                }
336
            }
337 4
338 4
            foreach ([
339
                'persistence',
340
                'serializer',
341
                'analyzer',
342
                'search_analyzer',
343
                'dynamic',
344 4
                'date_detection',
345
                'dynamic_date_formats',
346 4
                'numeric_detection',
347
            ] as $field) {
348
                $typeConfig['config'][$field] = array_key_exists($field, $type) ?
349
                    $type[$field] :
350
                    null;
351
            }
352
353
            $indexConfig['types'][$name] = $typeConfig;
354
355
            if (isset($type['persistence'])) {
356
                $this->loadTypePersistenceIntegration($type['persistence'], $container, new Reference($typeId), $indexName, $name);
357
358 15
                $typeConfig['persistence'] = $type['persistence'];
359
            }
360 15
361 15
            if (isset($type['_parent'])) {
362
                // _parent mapping cannot contain `property` and `identifier`, so removing them after building `persistence`
363
                unset($indexConfig['types'][$name]['mapping']['_parent']['property'], $indexConfig['types'][$name]['mapping']['_parent']['identifier']);
364 15
            }
365 15
366 15
            if (isset($type['indexable_callback'])) {
367
                $indexableCallbacks[sprintf('%s/%s', $indexName, $name)] = $this->buildCallback($type['indexable_callback'], $name);
368 15
            }
369 15
370
            if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
371 15
                $typeSerializerId = sprintf('%s.serializer.callback', $typeId);
372 15
                $typeSerializerDef = new ChildDefinition('fos_elastica.serializer_callback_prototype');
373
374 15
                if (isset($type['serializer']['groups'])) {
375 14
                    $typeSerializerDef->addMethodCall('setGroups', [$type['serializer']['groups']]);
376
                }
377 15
378
                if (isset($type['serializer']['serialize_null'])) {
379
                    $typeSerializerDef->addMethodCall('setSerializeNull', [$type['serializer']['serialize_null']]);
380
                }
381
382
                if (isset($type['serializer']['version'])) {
383
                    $typeSerializerDef->addMethodCall('setVersion', [$type['serializer']['version']]);
384
                }
385
386
                $typeDef->addMethodCall('setSerializer', [[new Reference($typeSerializerId), 'serialize']]);
387
                $container->setDefinition($typeSerializerId, $typeSerializerDef);
388
            }
389 15
        }
390
    }
391 15
392 1
    private function buildCallback($indexCallback, $typeName)
393
    {
394
        if (is_array($indexCallback)) {
395
            if (!isset($indexCallback[0])) {
396
                throw new \InvalidArgumentException(sprintf('Invalid indexable_callback for type %s', $typeName));
397
            }
398 14
399 14
            $classOrServiceRef = $this->transformServiceReference($indexCallback[0]);
400 14
            if ($classOrServiceRef instanceof Reference && !isset($indexCallback[1])) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\DependencyInjection\Reference does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
401 14
                return $classOrServiceRef; // __invoke
402
            }
403 14
404 14
            if (!isset($indexCallback[1])) {
405 14
                throw new \InvalidArgumentException(sprintf('Invalid indexable_callback for type %s', $typeName));
406
            }
407 14
408
            return [$classOrServiceRef, $indexCallback[1]];
409 14
        }
410
411
        if (is_string($indexCallback)) {
412
            return $this->transformServiceReference($indexCallback);
413
        }
414
415
        throw new \InvalidArgumentException(sprintf('Invalid indexable_callback for type %s', $typeName));
416
    }
417
418
    private function transformServiceReference($classOrService)
419
    {
420
        return 0 === strpos($classOrService, '@') ? new Reference(substr($classOrService, 1)) : $classOrService;
421
    }
422 15
423
    /**
424 15
     * Loads the optional provider and finder for a type.
425
     *
426
     * @param array            $typeConfig
427
     * @param ContainerBuilder $container
428 15
     * @param Reference        $typeRef
429 1
     * @param string           $indexName
430 15
     * @param string           $typeName
431
     */
432 15
    private function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Reference $typeRef, $indexName, $typeName)
433 15
    {
434 15
        if (isset($typeConfig['driver'])) {
435 15
            $this->loadDriver($container, $typeConfig['driver']);
436 15
        }
437
438 15
        $elasticaToModelTransformerId = $this->loadElasticaToModelTransformer($typeConfig, $container, $indexName, $typeName);
439
        $modelToElasticaTransformerId = $this->loadModelToElasticaTransformer($typeConfig, $container, $indexName, $typeName);
440 15
        $objectPersisterId = $this->loadObjectPersister($typeConfig, $typeRef, $container, $indexName, $typeName, $modelToElasticaTransformerId);
441
442
        if (isset($typeConfig['provider'])) {
443
            $this->loadTypePagerProvider($typeConfig, $container, $indexName, $typeName);
444
        }
445
        if (isset($typeConfig['finder'])) {
446
            $this->loadTypeFinder($typeConfig, $container, $elasticaToModelTransformerId, $typeRef, $indexName, $typeName);
447
        }
448
        if (isset($typeConfig['listener']) && $typeConfig['listener']['enabled']) {
449
            $this->loadTypeListener($typeConfig, $container, $objectPersisterId, $indexName, $typeName);
450
        }
451
    }
452
453
    /**
454
     * Creates and loads an ElasticaToModelTransformer.
455 15
     *
456
     * @param array            $typeConfig
457 15
     * @param ContainerBuilder $container
458 1
     * @param string           $indexName
459
     * @param string           $typeName
460
     *
461
     * @return string
462 14
     */
463 14
    private function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
464 14
    {
465
        if (isset($typeConfig['elastica_to_model_transformer']['service'])) {
466
            return $typeConfig['elastica_to_model_transformer']['service'];
467 14
        }
468 1
469 1
        /* Note: transformer services may conflict with "prototype.driver", if
470 1
         * the index and type names were "prototype" and a driver, respectively.
471
         */
472 13
        $abstractId = sprintf('fos_elastica.elastica_to_model_transformer.prototype.%s', $typeConfig['driver']);
473 13
        $serviceId = sprintf('fos_elastica.elastica_to_model_transformer.%s.%s', $indexName, $typeName);
474 13
        $serviceDef = new ChildDefinition($abstractId);
475 13
        $serviceDef->addTag('fos_elastica.elastica_to_model_transformer', ['type' => $typeName, 'index' => $indexName]);
476 1
477
        $serviceDef->replaceArgument(1, $typeConfig['model']);
478 13
        $serviceDef->replaceArgument(2, array_merge($typeConfig['elastica_to_model_transformer'], [
479
            'identifier' => $typeConfig['identifier'],
480
        ]));
481 14
        $container->setDefinition($serviceId, $serviceDef);
482
483 14
        return $serviceId;
484 14
    }
485 14
486 14
    /**
487
     * Creates and loads a ModelToElasticaTransformer for an index/type.
488
     *
489 14
     * @param array            $typeConfig
490
     * @param ContainerBuilder $container
491 14
     * @param string           $indexName
492
     * @param string           $typeName
493 14
     *
494
     * @return string
495
     */
496
    private function loadModelToElasticaTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
497
    {
498
        if (isset($typeConfig['model_to_elastica_transformer']['service'])) {
499
            return $typeConfig['model_to_elastica_transformer']['service'];
500
        }
501
502
        $abstractId = $container->hasDefinition('fos_elastica.serializer_callback_prototype') ?
503
            'fos_elastica.model_to_elastica_identifier_transformer' :
504
            'fos_elastica.model_to_elastica_transformer';
505
506 15
        $serviceId = sprintf('fos_elastica.model_to_elastica_transformer.%s.%s', $indexName, $typeName);
507
        $serviceDef = new ChildDefinition($abstractId);
508 15
        $serviceDef->replaceArgument(0, [
509
            'identifier' => $typeConfig['identifier'],
510
            'index' => $indexName,
511
        ]);
512 15
        $container->setDefinition($serviceId, $serviceDef);
513 15
514
        return $serviceId;
515 15
    }
516
517 15
    /**
518 15
     * Creates and loads an object persister for a type.
519 14
     *
520 14
     * @param array            $typeConfig
521 14
     * @param Reference        $typeRef
522
     * @param ContainerBuilder $container
523 14
     * @param string           $indexName
524 1
     * @param string           $typeName
525
     * @param string           $transformerId
526
     *
527
     * @return string
528
     */
529
    private function loadObjectPersister(array $typeConfig, Reference $typeRef, ContainerBuilder $container, $indexName, $typeName, $transformerId)
530 1
    {
531 1
        if (isset($typeConfig['persister']['service'])) {
532 1
            return $typeConfig['persister']['service'];
533 1
        }
534
535 1
        $arguments = [
536
            $typeRef,
537
            new Reference($transformerId),
538
            $typeConfig['model'],
539
        ];
540
541
        if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
542
            $abstractId = 'fos_elastica.object_serializer_persister';
543 15
            $callbackId = sprintf('%s.%s.serializer.callback', $this->indexConfigs[$indexName]['reference'], $typeName);
544 15
            $arguments[] = [new Reference($callbackId), 'serialize'];
545
        } else {
546 15
            $abstractId = 'fos_elastica.object_persister';
547
            $mapping = $this->indexConfigs[$indexName]['types'][$typeName]['mapping'];
548 15
            $argument = $mapping['properties'];
549
            if (isset($mapping['_parent'])) {
550
                $argument['_parent'] = $mapping['_parent'];
551
            }
552
            $arguments[] = $argument;
553
        }
554
555
        $arguments[] = array_intersect_key($typeConfig['persister'], array_flip(['refresh']));
556
557
        $serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName);
558
        $serviceDef = new ChildDefinition($abstractId);
559
        foreach ($arguments as $i => $argument) {
560
            $serviceDef->replaceArgument($i, $argument);
561
        }
562 14
563
        $serviceDef->addTag('fos_elastica.persister', ['index' => $indexName, 'type' => $typeName]);
564 14
565
        $container->setDefinition($serviceId, $serviceDef);
566
567
        return $serviceId;
568
    }
569
570
    /**
571 14
     * Loads a pager provider for a type.
572 14
     *
573 14
     * @param array            $typeConfig
574 14
     * @param ContainerBuilder $container
575 14
     * @param string           $indexName
576
     * @param string           $typeName
577 14
     *
578
     * @return string
579
     */
580 14
    private function loadTypePagerProvider(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
581 14
    {
582 14
        if (isset($typeConfig['provider']['service'])) {
583
            return $typeConfig['provider']['service'];
584
        }
585 14
586 14
        $baseConfig = $typeConfig['provider'];
587 14
        unset($baseConfig['service']);
588 13
589 13
        $driver = $typeConfig['driver'];
590 1
591 1
        switch ($driver) {
592 1
            case 'orm':
593
                $providerDef = new ChildDefinition('fos_elastica.pager_provider.prototype.'.$driver);
594
                $providerDef->replaceArgument(2, $typeConfig['model']);
595
                $providerDef->replaceArgument(3, $baseConfig);
596
597
                break;
598 14 View Code Duplication
            case 'mongodb':
599
                $providerDef = new ChildDefinition('fos_elastica.pager_provider.prototype.'.$driver);
600
                $providerDef->replaceArgument(2, $typeConfig['model']);
601
                $providerDef->replaceArgument(3, $baseConfig);
602
603
                break;
604 View Code Duplication
            case 'phpcr':
605
                $providerDef = new ChildDefinition('fos_elastica.pager_provider.prototype.'.$driver);
606
                $providerDef->replaceArgument(2, $typeConfig['model']);
607
                $providerDef->replaceArgument(3, $baseConfig);
608
609
                break;
610
            default:
611 14
                throw new \LogicException(sprintf('The pager provider for driver "%s" does not exist.', $driver));
612
        }
613 14
614 14
        /* Note: provider services may conflict with "prototype.driver", if the
615 14
         * index and type names were "prototype" and a driver, respectively.
616
         */
617
        $providerId = sprintf('fos_elastica.pager_provider.%s.%s', $indexName, $typeName);
618
        $providerDef->addTag('fos_elastica.pager_provider', ['index' => $indexName, 'type' => $typeName]);
619 14
620
        $container->setDefinition($providerId, $providerDef);
621 14
622
        return $providerId;
623
    }
624
625
    /**
626
     * Loads doctrine listeners to handle indexing of new or updated objects.
627 14
     *
628
     * @param array            $typeConfig
629 14
     * @param ContainerBuilder $container
630 14
     * @param string           $objectPersisterId
631 13
     * @param string           $indexName
632 13
     * @param string           $typeName
633 1
     *
634 1
     * @return string
635 1
     */
636
    private function loadTypeListener(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
637
    {
638
        if (isset($typeConfig['listener']['service'])) {
639
            return $typeConfig['listener']['service'];
640
        }
641
642
        /* Note: listener services may conflict with "prototype.driver", if the
643 14
         * index and type names were "prototype" and a driver, respectively.
644
         */
645 14
        $abstractListenerId = sprintf('fos_elastica.listener.prototype.%s', $typeConfig['driver']);
646 14
        $listenerId = sprintf('fos_elastica.listener.%s.%s', $indexName, $typeName);
647 14
        $listenerDef = new ChildDefinition($abstractListenerId);
648 14
        $listenerDef->replaceArgument(0, new Reference($objectPersisterId));
649
        $listenerDef->replaceArgument(3, $typeConfig['listener']['logger'] ?
650
            new Reference($typeConfig['listener']['logger']) :
651 14
            null
652 14
        );
653 14
        $listenerConfig = [
654
            'identifier' => $typeConfig['identifier'],
655
            'indexName' => $indexName,
656
            'typeName' => $typeName,
657 14
        ];
658
659
        $tagName = null;
660
        switch ($typeConfig['driver']) {
661
            case 'orm':
662
                $tagName = 'doctrine.event_listener';
663
                break;
664
            case 'phpcr':
665
                $tagName = 'doctrine_phpcr.event_listener';
666
                break;
667
            case 'mongodb':
668
                $tagName = 'doctrine_mongodb.odm.event_listener';
669
                break;
670
        }
671
672 15
        if ($typeConfig['listener']['defer']) {
673
            $listenerDef->setPublic(true);
674 15
            $listenerDef->addTag(
675
                'kernel.event_listener',
676
                ['event' => 'kernel.terminate', 'method' => 'onTerminate']
677 15
            );
678 15
            $listenerDef->addTag(
679 15
                'kernel.event_listener',
680 15
                ['event' => 'console.terminate', 'method' => 'onTerminate']
681 15
            );
682
            $listenerConfig['defer'] = true;
683
        }
684 15
685 15
        $listenerDef->replaceArgument(2, $listenerConfig);
686 15
687 4
        if (null !== $tagName) {
688
            foreach ($this->getDoctrineEvents($typeConfig) as $event) {
689
                $listenerDef->addTag($tagName, ['event' => $event]);
690 15
            }
691 15
        }
692
693 15
        $container->setDefinition($listenerId, $listenerDef);
694 15
695 15
        return $listenerId;
696
    }
697 15
698
    /**
699
     * Map Elastica to Doctrine events for the current driver.
700
     */
701
    private function getDoctrineEvents(array $typeConfig)
702
    {
703
        switch ($typeConfig['driver']) {
704
            case 'orm':
705
                $eventsClass = '\Doctrine\ORM\Events';
706
                break;
707 18
            case 'phpcr':
708 18
                $eventsClass = '\Doctrine\ODM\PHPCR\Event';
709 18
                break;
710
            case 'mongodb':
711 18
                $eventsClass = '\Doctrine\ODM\MongoDB\Events';
712 18
                break;
713 18
            default:
714
                throw new \InvalidArgumentException(sprintf('Cannot determine events for driver "%s"', $typeConfig['driver']));
715
        }
716
717
        $events = [];
718
        $eventMapping = [
719
            'insert' => [constant($eventsClass.'::postPersist')],
720
            'update' => [constant($eventsClass.'::postUpdate')],
721 15
            'delete' => [constant($eventsClass.'::preRemove')],
722
            'flush' => [constant($eventsClass.'::postFlush')],
723 15
        ];
724 6
725
        foreach ($eventMapping as $event => $doctrineEvents) {
726
            if (isset($typeConfig['listener'][$event]) && $typeConfig['listener'][$event]) {
727 15
                $events = array_merge($events, $doctrineEvents);
728 15
            }
729 15
        }
730 15
731
        return $events;
732
    }
733
734
    /**
735
     * Loads a Type specific Finder.
736
     *
737
     * @param array            $typeConfig
738 1
     * @param ContainerBuilder $container
739
     * @param string           $elasticaToModelId
740 1
     * @param Reference        $typeRef
741
     * @param string           $indexName
742 1
     * @param string           $typeName
743 1
     *
744
     * @return string
745 1
     */
746
    private function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, Reference $typeRef, $indexName, $typeName)
747
    {
748 1
        if (isset($typeConfig['finder']['service'])) {
749
            $finderId = $typeConfig['finder']['service'];
750
        } else {
751
            $finderId = sprintf('fos_elastica.finder.%s.%s', $indexName, $typeName);
752
            $finderDef = new ChildDefinition('fos_elastica.finder');
753
            $finderDef->replaceArgument(0, $typeRef);
754
            $finderDef->replaceArgument(1, new Reference($elasticaToModelId));
755
            $container->setDefinition($finderId, $finderDef);
756 18
        }
757
758 18
        $indexTypeName = "$indexName/$typeName";
759 3
        $arguments = [$indexTypeName, new Reference($finderId)];
760
        if (isset($typeConfig['repository'])) {
761
            $arguments[] = $typeConfig['repository'];
762 15
        }
763 15
764
        $container->getDefinition('fos_elastica.repository_manager')
765
            ->addMethodCall('addType', $arguments);
766
767 15
        $managerId = sprintf('fos_elastica.manager.%s', $typeConfig['driver']);
768
        $container->getDefinition($managerId)
769
            ->addMethodCall('addEntity', [$typeConfig['model'], $indexTypeName]);
770 15
771 15
        return $finderId;
772 15
    }
773 15
774 15
    /**
775
     * Loads the index manager.
776
     *
777
     * @param ContainerBuilder $container
778
     **/
779 View Code Duplication
    private function loadIndexManager(ContainerBuilder $container)
780
    {
781
        $indexRefs = array_map(function ($index) {
782
            return $index['reference'];
783
        }, $this->indexConfigs);
784
785 2
        $managerDef = $container->getDefinition('fos_elastica.index_manager');
786
        $managerDef->replaceArgument(0, $indexRefs);
787 2
    }
788
789
    /**
790
     * Load index template manager
791 2
     *
792
     * @param ContainerBuilder $container
793
     *
794
     * @return void
795
     */
796 View Code Duplication
    private function loadIndexTemplateManager(ContainerBuilder $container)
797
    {
798
        $indexTemplateRefs = array_map(function ($index) {
799
            return $index['reference'];
800
        }, $this->indexTemplateConfigs);
801
802
        $managerDef = $container->getDefinition('fos_elastica.index_template_manager');
803
        $managerDef->replaceArgument(0, $indexTemplateRefs);
804
    }
805
806
    /**
807
     * Makes sure a specific driver has been loaded.
808
     *
809
     * @param ContainerBuilder $container
810
     * @param string           $driver
811
     */
812
    private function loadDriver(ContainerBuilder $container, $driver)
813
    {
814
        if (in_array($driver, $this->loadedDrivers)) {
815
            return;
816
        }
817
818
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
819
        $loader->load($driver.'.xml');
820
        $this->loadedDrivers[] = $driver;
821
    }
822
823
    /**
824
     * Loads and configures the serializer prototype.
825
     *
826
     * @param array            $config
827
     * @param ContainerBuilder $container
828
     */
829
    private function loadSerializer($config, ContainerBuilder $container)
830
    {
831
        $container->setAlias('fos_elastica.serializer', $config['serializer']);
832
833
        $serializer = $container->getDefinition('fos_elastica.serializer_callback_prototype');
834
        $serializer->setClass($config['callback_class']);
835
836
        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...
837
            $serializer->addMethodCall('setContainer', [new Reference('service_container')]);
838
        }
839
    }
840
841
    /**
842
     * Creates a default manager alias for defined default manager or the first loaded driver.
843
     *
844
     * @param string           $defaultManager
845
     * @param ContainerBuilder $container
846
     */
847
    private function createDefaultManagerAlias($defaultManager, ContainerBuilder $container)
848
    {
849
        if (0 == count($this->loadedDrivers)) {
850
            return;
851
        }
852
853
        if (count($this->loadedDrivers) > 1
854
            && in_array($defaultManager, $this->loadedDrivers)
855
        ) {
856
            $defaultManagerService = $defaultManager;
857
        } else {
858
            $defaultManagerService = $this->loadedDrivers[0];
859
        }
860
861
        $container->setAlias('fos_elastica.manager', sprintf('fos_elastica.manager.%s', $defaultManagerService));
862
        $container->getAlias('fos_elastica.manager')->setPublic(true);
863
        $container->setAlias(RepositoryManagerInterface::class, 'fos_elastica.manager');
864
        $container->getAlias(RepositoryManagerInterface::class)->setPublic(false);
865
    }
866
867
    /**
868
     * Returns a reference to a client given its configured name.
869
     *
870
     * @param string $clientName
871
     *
872
     * @return Reference
873
     *
874
     * @throws \InvalidArgumentException
875
     */
876
    private function getClient($clientName)
877
    {
878
        if (!array_key_exists($clientName, $this->clients)) {
879
            throw new \InvalidArgumentException(sprintf('The elastica client with name "%s" is not defined', $clientName));
880
        }
881
882
        return $this->clients[$clientName]['reference'];
883
    }
884
}
885