Completed
Push — master ( 4725c6...ad278e )
by Andreas
22s
created

DoctrineExtension::ormLoad()   B

Complexity

Conditions 9
Paths 64

Size

Total Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 58
rs 7.3608
c 0
b 0
f 0
cc 9
nc 64
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
4
5
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
6
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;
7
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader;
8
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\SymfonyBridgeAdapter;
9
use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory;
10
use Doctrine\ORM\Version;
11
use LogicException;
12
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
13
use Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware;
14
use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor;
15
use Symfony\Component\Config\FileLocator;
16
use Symfony\Component\DependencyInjection\Alias;
17
use Symfony\Component\DependencyInjection\ChildDefinition;
18
use Symfony\Component\DependencyInjection\ContainerBuilder;
19
use Symfony\Component\DependencyInjection\Definition;
20
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
21
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
22
use Symfony\Component\DependencyInjection\Reference;
23
use Symfony\Component\Messenger\MessageBusInterface;
24
use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface;
25
26
/**
27
 * DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
28
 */
29
class DoctrineExtension extends AbstractDoctrineExtension
30
{
31
    /** @var string */
32
    private $defaultConnection;
33
34
    /** @var SymfonyBridgeAdapter */
35
    private $adapter;
36
37
    public function __construct(SymfonyBridgeAdapter $adapter = null)
38
    {
39
        $this->adapter = $adapter ?: new SymfonyBridgeAdapter(new CacheProviderLoader(), 'doctrine.orm', 'orm');
40
    }
41
42
    /**
43
     * {@inheritDoc}
44
     */
45
    public function load(array $configs, ContainerBuilder $container)
46
    {
47
        $configuration = $this->getConfiguration($configs, $container);
48
        $config        = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 47 can be null; however, Symfony\Component\Depend...:processConfiguration() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
49
50
        $this->adapter->loadServicesConfiguration($container);
51
52
        if (! empty($config['dbal'])) {
53
            $this->dbalLoad($config['dbal'], $container);
54
        }
55
56
        if (empty($config['orm'])) {
57
            return;
58
        }
59
60
        if (empty($config['dbal'])) {
61
            throw new LogicException('Configuring the ORM layer requires to configure the DBAL layer as well.');
62
        }
63
64
        if (! class_exists(Version::class)) {
65
            throw new LogicException('To configure the ORM layer, you must first install the doctrine/orm package.');
66
        }
67
68
        $this->ormLoad($config['orm'], $container);
69
    }
70
71
    /**
72
     * Loads the DBAL configuration.
73
     *
74
     * Usage example:
75
     *
76
     *      <doctrine:dbal id="myconn" dbname="sfweb" user="root" />
77
     *
78
     * @param array            $config    An array of configuration settings
79
     * @param ContainerBuilder $container A ContainerBuilder instance
80
     */
81
    protected function dbalLoad(array $config, ContainerBuilder $container)
82
    {
83
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
84
        $loader->load('dbal.xml');
85
86
        if (empty($config['default_connection'])) {
87
            $keys                         = array_keys($config['connections']);
88
            $config['default_connection'] = reset($keys);
89
        }
90
91
        $this->defaultConnection = $config['default_connection'];
92
93
        $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection));
94
        $container->getAlias('database_connection')->setPublic(true);
95
        $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false));
96
97
        $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']);
98
99
        $connections = [];
100
101
        foreach (array_keys($config['connections']) as $name) {
102
            $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name);
103
        }
104
105
        $container->setParameter('doctrine.connections', $connections);
106
        $container->setParameter('doctrine.default_connection', $this->defaultConnection);
107
108
        foreach ($config['connections'] as $name => $connection) {
109
            $this->loadDbalConnection($name, $connection, $container);
110
        }
111
    }
112
113
    /**
114
     * Loads a configured DBAL connection.
115
     *
116
     * @param string           $name       The name of the connection
117
     * @param array            $connection A dbal connection configuration.
118
     * @param ContainerBuilder $container  A ContainerBuilder instance
119
     */
120
    protected function loadDbalConnection($name, array $connection, ContainerBuilder $container)
121
    {
122
        $configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new ChildDefinition('doctrine.dbal.connection.configuration'));
123
        $logger        = null;
124
        if ($connection['logging']) {
125
            $logger = new Reference('doctrine.dbal.logger');
126
        }
127
        unset($connection['logging']);
128
        if ($connection['profiling']) {
129
            $profilingLoggerId = 'doctrine.dbal.logger.profiling.' . $name;
130
            $container->setDefinition($profilingLoggerId, new ChildDefinition('doctrine.dbal.logger.profiling'));
131
            $profilingLogger = new Reference($profilingLoggerId);
132
            $container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', [$name, $profilingLogger]);
133
134
            if ($logger !== null) {
135
                $chainLogger = new ChildDefinition('doctrine.dbal.logger.chain');
136
                $chainLogger->addMethodCall('addLogger', [$profilingLogger]);
137
138
                $loggerId = 'doctrine.dbal.logger.chain.' . $name;
139
                $container->setDefinition($loggerId, $chainLogger);
140
                $logger = new Reference($loggerId);
141
            } else {
142
                $logger = $profilingLogger;
143
            }
144
        }
145
        unset($connection['profiling']);
146
147
        if (isset($connection['auto_commit'])) {
148
            $configuration->addMethodCall('setAutoCommit', [$connection['auto_commit']]);
149
        }
150
151
        unset($connection['auto_commit']);
152
153
        if (isset($connection['schema_filter']) && $connection['schema_filter']) {
154
            $configuration->addMethodCall('setFilterSchemaAssetsExpression', [$connection['schema_filter']]);
155
        }
156
157
        unset($connection['schema_filter']);
158
159
        if ($logger) {
160
            $configuration->addMethodCall('setSQLLogger', [$logger]);
161
        }
162
163
        // event manager
164
        $container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new ChildDefinition('doctrine.dbal.connection.event_manager'));
165
166
        // connection
167
        $options = $this->getConnectionOptions($connection);
168
169
        $def = $container
170
            ->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new ChildDefinition('doctrine.dbal.connection'))
171
            ->setPublic(true)
172
            ->setArguments([
173
                $options,
174
                new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)),
175
                new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)),
176
                $connection['mapping_types'],
177
            ]);
178
179
        // Set class in case "wrapper_class" option was used to assist IDEs
180
        if (isset($options['wrapperClass'])) {
181
            $def->setClass($options['wrapperClass']);
182
        }
183
184
        if (! empty($connection['use_savepoints'])) {
185
            $def->addMethodCall('setNestTransactionsWithSavepoints', [$connection['use_savepoints']]);
186
        }
187
188
        // Create a shard_manager for this connection
189
        if (! isset($options['shards'])) {
190
            return;
191
        }
192
193
        $shardManagerDefinition = new Definition($options['shardManagerClass'], [new Reference(sprintf('doctrine.dbal.%s_connection', $name))]);
194
        $container->setDefinition(sprintf('doctrine.dbal.%s_shard_manager', $name), $shardManagerDefinition);
195
    }
196
197
    protected function getConnectionOptions($connection)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
198
    {
199
        $options = $connection;
200
201
        if (isset($options['platform_service'])) {
202
            $options['platform'] = new Reference($options['platform_service']);
203
            unset($options['platform_service']);
204
        }
205
        unset($options['mapping_types']);
206
207
        if (isset($options['shard_choser_service'])) {
208
            $options['shard_choser'] = new Reference($options['shard_choser_service']);
209
            unset($options['shard_choser_service']);
210
        }
211
212
        foreach ([
213
            'options' => 'driverOptions',
214
            'driver_class' => 'driverClass',
215
            'wrapper_class' => 'wrapperClass',
216
            'keep_slave' => 'keepSlave',
217
            'shard_choser' => 'shardChoser',
218
            'shard_manager_class' => 'shardManagerClass',
219
            'server_version' => 'serverVersion',
220
            'default_table_options' => 'defaultTableOptions',
221
        ] as $old => $new) {
222
            if (! isset($options[$old])) {
223
                continue;
224
            }
225
226
            $options[$new] = $options[$old];
227
            unset($options[$old]);
228
        }
229
230
        if (! empty($options['slaves']) && ! empty($options['shards'])) {
231
            throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together');
232
        }
233
234
        if (! empty($options['slaves'])) {
235
            $nonRewrittenKeys = [
236
                'driver' => true,
237
                'driverOptions' => true,
238
                'driverClass' => true,
239
                'wrapperClass' => true,
240
                'keepSlave' => true,
241
                'shardChoser' => true,
242
                'platform' => true,
243
                'slaves' => true,
244
                'master' => true,
245
                'shards' => true,
246
                'serverVersion' => true,
247
                // included by safety but should have been unset already
248
                'logging' => true,
249
                'profiling' => true,
250
                'mapping_types' => true,
251
                'platform_service' => true,
252
            ];
253 View Code Duplication
            foreach ($options as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
254
                if (isset($nonRewrittenKeys[$key])) {
255
                    continue;
256
                }
257
                $options['master'][$key] = $value;
258
                unset($options[$key]);
259
            }
260
            if (empty($options['wrapperClass'])) {
261
                // Change the wrapper class only if the user does not already forced using a custom one.
262
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Connections\\MasterSlaveConnection';
263
            }
264
        } else {
265
            unset($options['slaves']);
266
        }
267
268
        if (! empty($options['shards'])) {
269
            $nonRewrittenKeys = [
270
                'driver' => true,
271
                'driverOptions' => true,
272
                'driverClass' => true,
273
                'wrapperClass' => true,
274
                'keepSlave' => true,
275
                'shardChoser' => true,
276
                'platform' => true,
277
                'slaves' => true,
278
                'global' => true,
279
                'shards' => true,
280
                'serverVersion' => true,
281
                // included by safety but should have been unset already
282
                'logging' => true,
283
                'profiling' => true,
284
                'mapping_types' => true,
285
                'platform_service' => true,
286
            ];
287 View Code Duplication
            foreach ($options as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
288
                if (isset($nonRewrittenKeys[$key])) {
289
                    continue;
290
                }
291
                $options['global'][$key] = $value;
292
                unset($options[$key]);
293
            }
294
            if (empty($options['wrapperClass'])) {
295
                // Change the wrapper class only if the user does not already forced using a custom one.
296
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardConnection';
297
            }
298
            if (empty($options['shardManagerClass'])) {
299
                // Change the shard manager class only if the user does not already forced using a custom one.
300
                $options['shardManagerClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardManager';
301
            }
302
        } else {
303
            unset($options['shards']);
304
        }
305
306
        return $options;
307
    }
308
309
    /**
310
     * Loads the Doctrine ORM configuration.
311
     *
312
     * Usage example:
313
     *
314
     *     <doctrine:orm id="mydm" connection="myconn" />
315
     *
316
     * @param array            $config    An array of configuration settings
317
     * @param ContainerBuilder $container A ContainerBuilder instance
318
     */
319
    protected function ormLoad(array $config, ContainerBuilder $container)
320
    {
321
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
322
        $loader->load('orm.xml');
323
324
        $entityManagers = [];
325
        foreach (array_keys($config['entity_managers']) as $name) {
326
            $entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name);
327
        }
328
        $container->setParameter('doctrine.entity_managers', $entityManagers);
329
330
        if (empty($config['default_entity_manager'])) {
331
            $tmp                              = array_keys($entityManagers);
332
            $config['default_entity_manager'] = reset($tmp);
333
        }
334
        $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);
335
336
        $options = ['auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace'];
337
        foreach ($options as $key) {
338
            $container->setParameter('doctrine.orm.' . $key, $config[$key]);
339
        }
340
341
        $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager']));
342
        $container->getAlias('doctrine.orm.entity_manager')->setPublic(true);
343
344
        $config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles'));
345
346
        foreach ($config['entity_managers'] as $name => $entityManager) {
347
            $entityManager['name'] = $name;
348
            $this->loadOrmEntityManager($entityManager, $container);
349
350
            $this->loadPropertyInfoExtractor($name, $container);
351
        }
352
353
        if ($config['resolve_target_entities']) {
354
            $def = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity');
355
            foreach ($config['resolve_target_entities'] as $name => $implementation) {
356
                $def->addMethodCall('addResolveTargetEntity', [
357
                    $name,
358
                    $implementation,
359
                    [],
360
                ]);
361
            }
362
363
            $def->addTag('doctrine.event_subscriber');
364
        }
365
366
        $container->registerForAutoconfiguration(ServiceEntityRepositoryInterface::class)
367
            ->addTag(ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG);
368
369
        // If the Messenger component is installed and the doctrine transaction middleware is available, wire it:
370
        if (! interface_exists(MessageBusInterface::class) || ! class_exists(DoctrineTransactionMiddleware::class)) {
371
            return;
372
        }
373
374
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
375
        $loader->load('messenger.xml');
376
    }
377
378
    /**
379
     * Loads a configured ORM entity manager.
380
     *
381
     * @param array            $entityManager A configured ORM entity manager.
382
     * @param ContainerBuilder $container     A ContainerBuilder instance
383
     */
384
    protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $container)
385
    {
386
        $ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new ChildDefinition('doctrine.orm.configuration'));
387
388
        $this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container);
389
        $this->loadOrmCacheDrivers($entityManager, $container);
390
391
        if (isset($entityManager['entity_listener_resolver']) && $entityManager['entity_listener_resolver']) {
392
            $container->setAlias(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $entityManager['entity_listener_resolver']);
393
        } else {
394
            $definition = new Definition('%doctrine.orm.entity_listener_resolver.class%');
395
            $definition->addArgument(new Reference('service_container'));
396
            $container->setDefinition(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $definition);
397
        }
398
399
        $methods = [
400
            'setMetadataCacheImpl' => new Reference(sprintf('doctrine.orm.%s_metadata_cache', $entityManager['name'])),
401
            'setQueryCacheImpl' => new Reference(sprintf('doctrine.orm.%s_query_cache', $entityManager['name'])),
402
            'setResultCacheImpl' => new Reference(sprintf('doctrine.orm.%s_result_cache', $entityManager['name'])),
403
            'setMetadataDriverImpl' => new Reference('doctrine.orm.' . $entityManager['name'] . '_metadata_driver'),
404
            'setProxyDir' => '%doctrine.orm.proxy_dir%',
405
            'setProxyNamespace' => '%doctrine.orm.proxy_namespace%',
406
            'setAutoGenerateProxyClasses' => '%doctrine.orm.auto_generate_proxy_classes%',
407
            'setClassMetadataFactoryName' => $entityManager['class_metadata_factory_name'],
408
            'setDefaultRepositoryClassName' => $entityManager['default_repository_class'],
409
            'setNamingStrategy' => new Reference($entityManager['naming_strategy']),
410
            'setQuoteStrategy' => new Reference($entityManager['quote_strategy']),
411
            'setEntityListenerResolver' => new Reference(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name'])),
412
        ];
413
414
        $listenerId        = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $entityManager['name']);
415
        $listenerDef       = $container->setDefinition($listenerId, new Definition('%doctrine.orm.listeners.attach_entity_listeners.class%'));
416
        $listenerTagParams = ['event' => 'loadClassMetadata'];
417
        if (isset($entityManager['connection'])) {
418
            $listenerTagParams['connection'] = $entityManager['connection'];
419
        }
420
        $listenerDef->addTag('doctrine.event_listener', $listenerTagParams);
421
422
        if (isset($entityManager['second_level_cache'])) {
423
            $this->loadOrmSecondLevelCache($entityManager, $ormConfigDef, $container);
424
        }
425
426
        if ($entityManager['repository_factory']) {
427
            $methods['setRepositoryFactory'] = new Reference($entityManager['repository_factory']);
428
        }
429
430
        foreach ($methods as $method => $arg) {
431
            $ormConfigDef->addMethodCall($method, [$arg]);
432
        }
433
434
        foreach ($entityManager['hydrators'] as $name => $class) {
435
            $ormConfigDef->addMethodCall('addCustomHydrationMode', [$name, $class]);
436
        }
437
438
        if (! empty($entityManager['dql'])) {
439
            foreach ($entityManager['dql']['string_functions'] as $name => $function) {
440
                $ormConfigDef->addMethodCall('addCustomStringFunction', [$name, $function]);
441
            }
442
            foreach ($entityManager['dql']['numeric_functions'] as $name => $function) {
443
                $ormConfigDef->addMethodCall('addCustomNumericFunction', [$name, $function]);
444
            }
445
            foreach ($entityManager['dql']['datetime_functions'] as $name => $function) {
446
                $ormConfigDef->addMethodCall('addCustomDatetimeFunction', [$name, $function]);
447
            }
448
        }
449
450
        $enabledFilters    = [];
451
        $filtersParameters = [];
452
        foreach ($entityManager['filters'] as $name => $filter) {
453
            $ormConfigDef->addMethodCall('addFilter', [$name, $filter['class']]);
454
            if ($filter['enabled']) {
455
                $enabledFilters[] = $name;
456
            }
457
            if (! $filter['parameters']) {
458
                continue;
459
            }
460
461
            $filtersParameters[$name] = $filter['parameters'];
462
        }
463
464
        $managerConfiguratorName = sprintf('doctrine.orm.%s_manager_configurator', $entityManager['name']);
465
        $container
466
            ->setDefinition($managerConfiguratorName, new ChildDefinition('doctrine.orm.manager_configurator.abstract'))
467
            ->replaceArgument(0, $enabledFilters)
468
            ->replaceArgument(1, $filtersParameters);
469
470
        if (! isset($entityManager['connection'])) {
471
            $entityManager['connection'] = $this->defaultConnection;
472
        }
473
474
        $container
475
            ->setDefinition(sprintf('doctrine.orm.%s_entity_manager', $entityManager['name']), new ChildDefinition('doctrine.orm.entity_manager.abstract'))
476
            ->setPublic(true)
477
            ->setArguments([
478
                new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])),
479
                new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])),
480
            ])
481
            ->setConfigurator([new Reference($managerConfiguratorName), 'configure']);
482
483
        $container->setAlias(
484
            sprintf('doctrine.orm.%s_entity_manager.event_manager', $entityManager['name']),
485
            new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $entityManager['connection']), false)
486
        );
487
488
        if (! isset($entityManager['entity_listeners'])) {
489
            return;
490
        }
491
492
        if (! isset($listenerDef)) {
493
            throw new InvalidArgumentException('Entity listeners configuration requires doctrine-orm 2.5.0 or newer');
494
        }
495
496
        $entities = $entityManager['entity_listeners']['entities'];
497
498
        foreach ($entities as $entityListenerClass => $entity) {
499
            foreach ($entity['listeners'] as $listenerClass => $listener) {
500
                foreach ($listener['events'] as $listenerEvent) {
501
                    $listenerEventName = $listenerEvent['type'];
502
                    $listenerMethod    = $listenerEvent['method'];
503
504
                    $listenerDef->addMethodCall('addEntityListener', [
505
                        $entityListenerClass,
506
                        $listenerClass,
507
                        $listenerEventName,
508
                        $listenerMethod,
509
                    ]);
510
                }
511
            }
512
        }
513
    }
514
515
    /**
516
     * Loads an ORM entity managers bundle mapping information.
517
     *
518
     * There are two distinct configuration possibilities for mapping information:
519
     *
520
     * 1. Specify a bundle and optionally details where the entity and mapping information reside.
521
     * 2. Specify an arbitrary mapping location.
522
     *
523
     * @param array            $entityManager A configured ORM entity manager
524
     * @param Definition       $ormConfigDef  A Definition instance
525
     * @param ContainerBuilder $container     A ContainerBuilder instance
526
     *
527
     * @example
528
     *
529
     *  doctrine.orm:
530
     *     mappings:
531
     *         MyBundle1: ~
532
     *         MyBundle2: yml
533
     *         MyBundle3: { type: annotation, dir: Entities/ }
534
     *         MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping }
535
     *         MyBundle5:
536
     *             type: yml
537
     *             dir: bundle-mappings/
538
     *             alias: BundleAlias
539
     *         arbitrary_key:
540
     *             type: xml
541
     *             dir: %kernel.root_dir%/../src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entities
542
     *             prefix: DoctrineExtensions\Entities\
543
     *             alias: DExt
544
     *
545
     * In the case of bundles everything is really optional (which leads to autodetection for this bundle) but
546
     * in the mappings key everything except alias is a required argument.
547
     */
548
    protected function loadOrmEntityManagerMappingInformation(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
549
    {
550
        // reset state of drivers and alias map. They are only used by this methods and children.
551
        $this->drivers  = [];
552
        $this->aliasMap = [];
553
554
        $this->loadMappingInformation($entityManager, $container);
555
        $this->registerMappingDrivers($entityManager, $container);
556
557
        $ormConfigDef->addMethodCall('setEntityNamespaces', [$this->aliasMap]);
558
    }
559
560
    /**
561
     * Loads an ORM second level cache bundle mapping information.
562
     *
563
     * @param array            $entityManager A configured ORM entity manager
564
     * @param Definition       $ormConfigDef  A Definition instance
565
     * @param ContainerBuilder $container     A ContainerBuilder instance
566
     *
567
     * @example
568
     *  entity_managers:
569
     *      default:
570
     *          second_level_cache:
571
     *              region_cache_driver: apc
572
     *              log_enabled: true
573
     *              regions:
574
     *                  my_service_region:
575
     *                      type: service
576
     *                      service : "my_service_region"
577
     *
578
     *                  my_query_region:
579
     *                      lifetime: 300
580
     *                      cache_driver: array
581
     *                      type: filelock
582
     *
583
     *                  my_entity_region:
584
     *                      lifetime: 600
585
     *                      cache_driver:
586
     *                          type: apc
587
     */
588
    protected function loadOrmSecondLevelCache(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
589
    {
590
        $driverId = null;
591
        $enabled  = $entityManager['second_level_cache']['enabled'];
592
593
        if (isset($entityManager['second_level_cache']['region_cache_driver'])) {
594
            $driverName = 'second_level_cache.region_cache_driver';
595
            $driverMap  = $entityManager['second_level_cache']['region_cache_driver'];
596
            $driverId   = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
597
        }
598
599
        $configId   = sprintf('doctrine.orm.%s_second_level_cache.cache_configuration', $entityManager['name']);
600
        $regionsId  = sprintf('doctrine.orm.%s_second_level_cache.regions_configuration', $entityManager['name']);
601
        $driverId   = $driverId ?: sprintf('doctrine.orm.%s_second_level_cache.region_cache_driver', $entityManager['name']);
602
        $configDef  = $container->setDefinition($configId, new Definition('%doctrine.orm.second_level_cache.cache_configuration.class%'));
603
        $regionsDef = $container->setDefinition($regionsId, new Definition('%doctrine.orm.second_level_cache.regions_configuration.class%'));
604
605
        $slcFactoryId = sprintf('doctrine.orm.%s_second_level_cache.default_cache_factory', $entityManager['name']);
606
        $factoryClass = isset($entityManager['second_level_cache']['factory']) ? $entityManager['second_level_cache']['factory'] : '%doctrine.orm.second_level_cache.default_cache_factory.class%';
607
608
        $definition = new Definition($factoryClass, [new Reference($regionsId), new Reference($driverId)]);
609
610
        $slcFactoryDef = $container
611
            ->setDefinition($slcFactoryId, $definition);
612
613
        if (isset($entityManager['second_level_cache']['regions'])) {
614
            foreach ($entityManager['second_level_cache']['regions'] as $name => $region) {
615
                $regionRef  = null;
616
                $regionType = $region['type'];
617
618
                if ($regionType === 'service') {
619
                    $regionId  = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
620
                    $regionRef = new Reference($region['service']);
621
622
                    $container->setAlias($regionId, new Alias($region['service'], false));
623
                }
624
625
                if ($regionType === 'default' || $regionType === 'filelock') {
626
                    $regionId   = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
627
                    $driverName = sprintf('second_level_cache.region.%s_driver', $name);
628
                    $driverMap  = $region['cache_driver'];
629
                    $driverId   = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
630
                    $regionRef  = new Reference($regionId);
631
632
                    $container
633
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.default_region.class%'))
634
                        ->setArguments([$name, new Reference($driverId), $region['lifetime']]);
635
                }
636
637
                if ($regionType === 'filelock') {
638
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s_filelock', $entityManager['name'], $name);
639
640
                    $container
641
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.filelock_region.class%'))
642
                        ->setArguments([$regionRef, $region['lock_path'], $region['lock_lifetime']]);
643
644
                    $regionRef = new Reference($regionId);
645
                    $regionsDef->addMethodCall('getLockLifetime', [$name, $region['lock_lifetime']]);
646
                }
647
648
                $regionsDef->addMethodCall('setLifetime', [$name, $region['lifetime']]);
649
                $slcFactoryDef->addMethodCall('setRegion', [$regionRef]);
650
            }
651
        }
652
653
        if ($entityManager['second_level_cache']['log_enabled']) {
654
            $loggerChainId   = sprintf('doctrine.orm.%s_second_level_cache.logger_chain', $entityManager['name']);
655
            $loggerStatsId   = sprintf('doctrine.orm.%s_second_level_cache.logger_statistics', $entityManager['name']);
656
            $loggerChaingDef = $container->setDefinition($loggerChainId, new Definition('%doctrine.orm.second_level_cache.logger_chain.class%'));
657
            $loggerStatsDef  = $container->setDefinition($loggerStatsId, new Definition('%doctrine.orm.second_level_cache.logger_statistics.class%'));
658
659
            $loggerChaingDef->addMethodCall('setLogger', ['statistics', $loggerStatsDef]);
660
            $configDef->addMethodCall('setCacheLogger', [$loggerChaingDef]);
661
662
            foreach ($entityManager['second_level_cache']['loggers'] as $name => $logger) {
663
                $loggerId  = sprintf('doctrine.orm.%s_second_level_cache.logger.%s', $entityManager['name'], $name);
664
                $loggerRef = new Reference($logger['service']);
665
666
                $container->setAlias($loggerId, new Alias($logger['service'], false));
667
                $loggerChaingDef->addMethodCall('setLogger', [$name, $loggerRef]);
668
            }
669
        }
670
671
        $configDef->addMethodCall('setCacheFactory', [$slcFactoryDef]);
672
        $configDef->addMethodCall('setRegionsConfiguration', [$regionsDef]);
673
        $ormConfigDef->addMethodCall('setSecondLevelCacheEnabled', [$enabled]);
674
        $ormConfigDef->addMethodCall('setSecondLevelCacheConfiguration', [$configDef]);
675
    }
676
677
    /**
678
     * {@inheritDoc}
679
     */
680
    protected function getObjectManagerElementName($name)
681
    {
682
        return 'doctrine.orm.' . $name;
683
    }
684
685
    protected function getMappingObjectDefaultName()
686
    {
687
        return 'Entity';
688
    }
689
690
    /**
691
     * {@inheritDoc}
692
     */
693
    protected function getMappingResourceConfigDirectory()
694
    {
695
        return 'Resources/config/doctrine';
696
    }
697
698
    /**
699
     * {@inheritDoc}
700
     */
701
    protected function getMappingResourceExtension()
702
    {
703
        return 'orm';
704
    }
705
706
    /**
707
     * {@inheritDoc}
708
     */
709
    protected function loadCacheDriver($driverName, $entityManagerName, array $driverMap, ContainerBuilder $container)
710
    {
711
        if (! empty($driverMap['cache_provider'])) {
712
            $aliasId   = $this->getObjectManagerElementName(sprintf('%s_%s', $entityManagerName, $driverName));
713
            $serviceId = sprintf('doctrine_cache.providers.%s', $driverMap['cache_provider']);
714
715
            $container->setAlias($aliasId, new Alias($serviceId, false));
716
717
            return $aliasId;
718
        }
719
720
        return $this->adapter->loadCacheDriver($driverName, $entityManagerName, $driverMap, $container);
721
    }
722
723
    /**
724
     * Loads a configured entity managers cache drivers.
725
     *
726
     * @param array            $entityManager A configured ORM entity manager.
727
     * @param ContainerBuilder $container     A ContainerBuilder instance
728
     */
729
    protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container)
730
    {
731
        $this->loadCacheDriver('metadata_cache', $entityManager['name'], $entityManager['metadata_cache_driver'], $container);
732
        $this->loadCacheDriver('result_cache', $entityManager['name'], $entityManager['result_cache_driver'], $container);
733
        $this->loadCacheDriver('query_cache', $entityManager['name'], $entityManager['query_cache_driver'], $container);
734
    }
735
736
    /**
737
     * Loads a property info extractor for each defined entity manager.
738
     *
739
     * @param string $entityManagerName
740
     */
741
    private function loadPropertyInfoExtractor($entityManagerName, ContainerBuilder $container)
742
    {
743
        $propertyExtractorDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.property_info_extractor', $entityManagerName), DoctrineExtractor::class);
744
        if (interface_exists(PropertyInitializableExtractorInterface::class)) {
745
            $argumentId = sprintf('doctrine.orm.%s_entity_manager', $entityManagerName);
746
        } else {
747
            $argumentId = sprintf('doctrine.orm.%s_entity_manager.metadata_factory', $entityManagerName);
748
749
            $metadataFactoryDefinition = $container->register($argumentId, ClassMetadataFactory::class);
750
            $metadataFactoryDefinition->setFactory([
751
                new Reference(sprintf('doctrine.orm.%s_entity_manager', $entityManagerName)),
752
                'getMetadataFactory',
753
            ]);
754
            $metadataFactoryDefinition->setPublic(false);
755
        }
756
757
        $propertyExtractorDefinition->addArgument(new Reference($argumentId));
758
759
        $propertyExtractorDefinition->addTag('property_info.list_extractor', ['priority' => -1001]);
760
        $propertyExtractorDefinition->addTag('property_info.type_extractor', ['priority' => -999]);
761
    }
762
763
    /**
764
     * @param array  $objectManager
765
     * @param string $cacheName
766
     */
767
    public function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName)
768
    {
769
        $this->loadCacheDriver($cacheName, $objectManager['name'], $objectManager[$cacheName . '_driver'], $container);
770
    }
771
772
    /**
773
     * {@inheritDoc}
774
     */
775
    public function getXsdValidationBasePath()
776
    {
777
        return __DIR__ . '/../Resources/config/schema';
0 ignored issues
show
Bug Best Practice introduced by
The return type of return __DIR__ . '/../Resources/config/schema'; (string) is incompatible with the return type of the parent method Symfony\Component\Depend...etXsdValidationBasePath of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
778
    }
779
780
    /**
781
     * {@inheritDoc}
782
     */
783
    public function getNamespace()
784
    {
785
        return 'http://symfony.com/schema/dic/doctrine';
786
    }
787
788
    /**
789
     * {@inheritDoc}
790
     */
791
    public function getConfiguration(array $config, ContainerBuilder $container)
792
    {
793
        return new Configuration($container->getParameter('kernel.debug'));
794
    }
795
}
796