Completed
Push — master ( eb068c...958ec5 )
by Andreas
03:16 queued 10s
created

DoctrineExtension::ormLoad()   C

Complexity

Conditions 10
Paths 192

Size

Total Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 63
rs 6.3272
c 0
b 0
f 0
cc 10
nc 192
nop 2

How to fix   Long Method    Complexity   

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\Dbal\RegexSchemaAssetFilter;
6
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
7
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;
8
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader;
9
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\SymfonyBridgeAdapter;
10
use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory;
11
use Doctrine\ORM\Version;
12
use LogicException;
13
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
14
use Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware;
15
use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor;
16
use Symfony\Bridge\Doctrine\Validator\DoctrineLoader;
17
use Symfony\Component\Cache\DoctrineProvider;
18
use Symfony\Component\Config\FileLocator;
19
use Symfony\Component\DependencyInjection\Alias;
20
use Symfony\Component\DependencyInjection\ChildDefinition;
21
use Symfony\Component\DependencyInjection\ContainerBuilder;
22
use Symfony\Component\DependencyInjection\Definition;
23
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
24
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
25
use Symfony\Component\DependencyInjection\Reference;
26
use Symfony\Component\Form\AbstractType;
27
use Symfony\Component\Messenger\MessageBusInterface;
28
use Symfony\Component\Messenger\Transport\Doctrine\DoctrineTransportFactory;
29
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
30
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
31
use Symfony\Component\Validator\Mapping\Loader\LoaderInterface;
32
use function class_exists;
33
use function sprintf;
34
35
/**
36
 * DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
37
 */
38
class DoctrineExtension extends AbstractDoctrineExtension
39
{
40
    /** @var string */
41
    private $defaultConnection;
42
43
    /** @var SymfonyBridgeAdapter */
44
    private $adapter;
45
46
    public function __construct(SymfonyBridgeAdapter $adapter = null)
47
    {
48
        $this->adapter = $adapter ?: new SymfonyBridgeAdapter(new CacheProviderLoader(), 'doctrine.orm', 'orm');
49
    }
50
51
    /**
52
     * {@inheritDoc}
53
     */
54
    public function load(array $configs, ContainerBuilder $container)
55
    {
56
        $configuration = $this->getConfiguration($configs, $container);
57
        $config        = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 56 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...
58
59
        $this->adapter->loadServicesConfiguration($container);
60
61
        if (! empty($config['dbal'])) {
62
            $this->dbalLoad($config['dbal'], $container);
63
        }
64
65
        if (empty($config['orm'])) {
66
            return;
67
        }
68
69
        if (empty($config['dbal'])) {
70
            throw new LogicException('Configuring the ORM layer requires to configure the DBAL layer as well.');
71
        }
72
73
        if (! class_exists(Version::class)) {
74
            throw new LogicException('To configure the ORM layer, you must first install the doctrine/orm package.');
75
        }
76
77
        $this->ormLoad($config['orm'], $container);
78
    }
79
80
    /**
81
     * Loads the DBAL configuration.
82
     *
83
     * Usage example:
84
     *
85
     *      <doctrine:dbal id="myconn" dbname="sfweb" user="root" />
86
     *
87
     * @param array            $config    An array of configuration settings
88
     * @param ContainerBuilder $container A ContainerBuilder instance
89
     */
90
    protected function dbalLoad(array $config, ContainerBuilder $container)
91
    {
92
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
93
        $loader->load('dbal.xml');
94
95
        if (empty($config['default_connection'])) {
96
            $keys                         = array_keys($config['connections']);
97
            $config['default_connection'] = reset($keys);
98
        }
99
100
        $this->defaultConnection = $config['default_connection'];
101
102
        $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection));
103
        $container->getAlias('database_connection')->setPublic(true);
104
        $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false));
105
106
        $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']);
107
108
        $connections = [];
109
110
        foreach (array_keys($config['connections']) as $name) {
111
            $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name);
112
        }
113
114
        $container->setParameter('doctrine.connections', $connections);
115
        $container->setParameter('doctrine.default_connection', $this->defaultConnection);
116
117
        foreach ($config['connections'] as $name => $connection) {
118
            $this->loadDbalConnection($name, $connection, $container);
119
        }
120
    }
121
122
    /**
123
     * Loads a configured DBAL connection.
124
     *
125
     * @param string           $name       The name of the connection
126
     * @param array            $connection A dbal connection configuration.
127
     * @param ContainerBuilder $container  A ContainerBuilder instance
128
     */
129
    protected function loadDbalConnection($name, array $connection, ContainerBuilder $container)
130
    {
131
        $configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new ChildDefinition('doctrine.dbal.connection.configuration'));
132
        $logger        = null;
133
        if ($connection['logging']) {
134
            $logger = new Reference('doctrine.dbal.logger');
135
        }
136
        unset($connection['logging']);
137
138
        if ($connection['profiling']) {
139
            $profilingAbstractId = $connection['profiling_collect_backtrace'] ?
140
                'doctrine.dbal.logger.backtrace' :
141
                'doctrine.dbal.logger.profiling';
142
143
            $profilingLoggerId = $profilingAbstractId . '.' . $name;
144
            $container->setDefinition($profilingLoggerId, new ChildDefinition($profilingAbstractId));
145
            $profilingLogger = new Reference($profilingLoggerId);
146
            $container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', [$name, $profilingLogger]);
147
148
            if ($logger !== null) {
149
                $chainLogger = new ChildDefinition('doctrine.dbal.logger.chain');
150
                $chainLogger->addMethodCall('addLogger', [$profilingLogger]);
151
152
                $loggerId = 'doctrine.dbal.logger.chain.' . $name;
153
                $container->setDefinition($loggerId, $chainLogger);
154
                $logger = new Reference($loggerId);
155
            } else {
156
                $logger = $profilingLogger;
157
            }
158
        }
159
        unset($connection['profiling'], $connection['profiling_collect_backtrace']);
160
161
        if (isset($connection['auto_commit'])) {
162
            $configuration->addMethodCall('setAutoCommit', [$connection['auto_commit']]);
163
        }
164
165
        unset($connection['auto_commit']);
166
167
        if (isset($connection['schema_filter']) && $connection['schema_filter']) {
168
            if (method_exists(\Doctrine\DBAL\Configuration::class, 'setSchemaAssetsFilter')) {
169
                $definition = new Definition(RegexSchemaAssetFilter::class, [$connection['schema_filter']]);
170
                $definition->addTag('doctrine.dbal.schema_filter', ['connection' => $name]);
171
                $container->setDefinition(sprintf('doctrine.dbal.%s_regex_schema_filter', $name), $definition);
172
            } else {
173
                // backwards compatibility with dbal < 2.9
174
                $configuration->addMethodCall('setFilterSchemaAssetsExpression', [$connection['schema_filter']]);
175
            }
176
        }
177
178
        unset($connection['schema_filter']);
179
180
        if ($logger) {
181
            $configuration->addMethodCall('setSQLLogger', [$logger]);
182
        }
183
184
        // event manager
185
        $container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new ChildDefinition('doctrine.dbal.connection.event_manager'));
186
187
        // connection
188
        $options = $this->getConnectionOptions($connection);
189
190
        $def = $container
191
            ->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new ChildDefinition('doctrine.dbal.connection'))
192
            ->setPublic(true)
193
            ->setArguments([
194
                $options,
195
                new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)),
196
                new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)),
197
                $connection['mapping_types'],
198
            ]);
199
200
        // Set class in case "wrapper_class" option was used to assist IDEs
201
        if (isset($options['wrapperClass'])) {
202
            $def->setClass($options['wrapperClass']);
203
        }
204
205
        if (! empty($connection['use_savepoints'])) {
206
            $def->addMethodCall('setNestTransactionsWithSavepoints', [$connection['use_savepoints']]);
207
        }
208
209
        // Create a shard_manager for this connection
210
        if (! isset($options['shards'])) {
211
            return;
212
        }
213
214
        $shardManagerDefinition = new Definition($options['shardManagerClass'], [new Reference(sprintf('doctrine.dbal.%s_connection', $name))]);
215
        $container->setDefinition(sprintf('doctrine.dbal.%s_shard_manager', $name), $shardManagerDefinition);
216
    }
217
218
    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...
219
    {
220
        $options = $connection;
221
222
        if (isset($options['platform_service'])) {
223
            $options['platform'] = new Reference($options['platform_service']);
224
            unset($options['platform_service']);
225
        }
226
        unset($options['mapping_types']);
227
228
        if (isset($options['shard_choser_service'])) {
229
            $options['shard_choser'] = new Reference($options['shard_choser_service']);
230
            unset($options['shard_choser_service']);
231
        }
232
233
        foreach ([
234
            'options' => 'driverOptions',
235
            'driver_class' => 'driverClass',
236
            'wrapper_class' => 'wrapperClass',
237
            'keep_slave' => 'keepSlave',
238
            'shard_choser' => 'shardChoser',
239
            'shard_manager_class' => 'shardManagerClass',
240
            'server_version' => 'serverVersion',
241
            'default_table_options' => 'defaultTableOptions',
242
        ] as $old => $new) {
243
            if (! isset($options[$old])) {
244
                continue;
245
            }
246
247
            $options[$new] = $options[$old];
248
            unset($options[$old]);
249
        }
250
251
        if (! empty($options['slaves']) && ! empty($options['shards'])) {
252
            throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together');
253
        }
254
255
        if (! empty($options['slaves'])) {
256
            $nonRewrittenKeys = [
257
                'driver' => true,
258
                'driverOptions' => true,
259
                'driverClass' => true,
260
                'wrapperClass' => true,
261
                'keepSlave' => true,
262
                'shardChoser' => true,
263
                'platform' => true,
264
                'slaves' => true,
265
                'master' => true,
266
                'shards' => true,
267
                'serverVersion' => true,
268
                'defaultTableOptions' => true,
269
                // included by safety but should have been unset already
270
                'logging' => true,
271
                'profiling' => true,
272
                'mapping_types' => true,
273
                'platform_service' => true,
274
            ];
275 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...
276
                if (isset($nonRewrittenKeys[$key])) {
277
                    continue;
278
                }
279
                $options['master'][$key] = $value;
280
                unset($options[$key]);
281
            }
282
            if (empty($options['wrapperClass'])) {
283
                // Change the wrapper class only if the user does not already forced using a custom one.
284
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Connections\\MasterSlaveConnection';
285
            }
286
        } else {
287
            unset($options['slaves']);
288
        }
289
290
        if (! empty($options['shards'])) {
291
            $nonRewrittenKeys = [
292
                'driver' => true,
293
                'driverOptions' => true,
294
                'driverClass' => true,
295
                'wrapperClass' => true,
296
                'keepSlave' => true,
297
                'shardChoser' => true,
298
                'platform' => true,
299
                'slaves' => true,
300
                'global' => true,
301
                'shards' => true,
302
                'serverVersion' => true,
303
                'defaultTableOptions' => true,
304
                // included by safety but should have been unset already
305
                'logging' => true,
306
                'profiling' => true,
307
                'mapping_types' => true,
308
                'platform_service' => true,
309
            ];
310 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...
311
                if (isset($nonRewrittenKeys[$key])) {
312
                    continue;
313
                }
314
                $options['global'][$key] = $value;
315
                unset($options[$key]);
316
            }
317
            if (empty($options['wrapperClass'])) {
318
                // Change the wrapper class only if the user does not already forced using a custom one.
319
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardConnection';
320
            }
321
            if (empty($options['shardManagerClass'])) {
322
                // Change the shard manager class only if the user does not already forced using a custom one.
323
                $options['shardManagerClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardManager';
324
            }
325
        } else {
326
            unset($options['shards']);
327
        }
328
329
        return $options;
330
    }
331
332
    /**
333
     * Loads the Doctrine ORM configuration.
334
     *
335
     * Usage example:
336
     *
337
     *     <doctrine:orm id="mydm" connection="myconn" />
338
     *
339
     * @param array            $config    An array of configuration settings
340
     * @param ContainerBuilder $container A ContainerBuilder instance
341
     */
342
    protected function ormLoad(array $config, ContainerBuilder $container)
343
    {
344
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
345
        $loader->load('orm.xml');
346
347
        if (class_exists(AbstractType::class)) {
348
            $container->getDefinition('form.type.entity')->addTag('kernel.reset', ['method' => 'reset']);
349
        }
350
351
        $entityManagers = [];
352
        foreach (array_keys($config['entity_managers']) as $name) {
353
            $entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name);
354
        }
355
        $container->setParameter('doctrine.entity_managers', $entityManagers);
356
357
        if (empty($config['default_entity_manager'])) {
358
            $tmp                              = array_keys($entityManagers);
359
            $config['default_entity_manager'] = reset($tmp);
360
        }
361
        $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);
362
363
        $options = ['auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace'];
364
        foreach ($options as $key) {
365
            $container->setParameter('doctrine.orm.' . $key, $config[$key]);
366
        }
367
368
        $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager']));
369
        $container->getAlias('doctrine.orm.entity_manager')->setPublic(true);
370
371
        $config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles'));
372
373
        $loadPropertyInfoExtractor = interface_exists(PropertyInfoExtractorInterface::class)
374
            && class_exists(DoctrineExtractor::class);
375
376
        foreach ($config['entity_managers'] as $name => $entityManager) {
377
            $entityManager['name'] = $name;
378
            $this->loadOrmEntityManager($entityManager, $container);
379
380
            if ($loadPropertyInfoExtractor) {
381
                $this->loadPropertyInfoExtractor($name, $container);
382
            }
383
384
            $this->loadValidatorLoader($name, $container);
385
        }
386
387
        if ($config['resolve_target_entities']) {
388
            $def = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity');
389
            foreach ($config['resolve_target_entities'] as $name => $implementation) {
390
                $def->addMethodCall('addResolveTargetEntity', [
391
                    $name,
392
                    $implementation,
393
                    [],
394
                ]);
395
            }
396
397
            $def->addTag('doctrine.event_subscriber');
398
        }
399
400
        $container->registerForAutoconfiguration(ServiceEntityRepositoryInterface::class)
401
            ->addTag(ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG);
402
403
        $this->loadMessengerServices($container);
404
    }
405
406
    /**
407
     * Loads a configured ORM entity manager.
408
     *
409
     * @param array            $entityManager A configured ORM entity manager.
410
     * @param ContainerBuilder $container     A ContainerBuilder instance
411
     */
412
    protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $container)
413
    {
414
        $ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new ChildDefinition('doctrine.orm.configuration'));
415
416
        $this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container);
417
        $this->loadOrmCacheDrivers($entityManager, $container);
418
419
        if (isset($entityManager['entity_listener_resolver']) && $entityManager['entity_listener_resolver']) {
420
            $container->setAlias(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $entityManager['entity_listener_resolver']);
421
        } else {
422
            $definition = new Definition('%doctrine.orm.entity_listener_resolver.class%');
423
            $definition->addArgument(new Reference('service_container'));
424
            $container->setDefinition(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $definition);
425
        }
426
427
        $methods = [
428
            'setMetadataCacheImpl' => new Reference(sprintf('doctrine.orm.%s_metadata_cache', $entityManager['name'])),
429
            'setQueryCacheImpl' => new Reference(sprintf('doctrine.orm.%s_query_cache', $entityManager['name'])),
430
            'setResultCacheImpl' => new Reference(sprintf('doctrine.orm.%s_result_cache', $entityManager['name'])),
431
            'setMetadataDriverImpl' => new Reference('doctrine.orm.' . $entityManager['name'] . '_metadata_driver'),
432
            'setProxyDir' => '%doctrine.orm.proxy_dir%',
433
            'setProxyNamespace' => '%doctrine.orm.proxy_namespace%',
434
            'setAutoGenerateProxyClasses' => '%doctrine.orm.auto_generate_proxy_classes%',
435
            'setClassMetadataFactoryName' => $entityManager['class_metadata_factory_name'],
436
            'setDefaultRepositoryClassName' => $entityManager['default_repository_class'],
437
            'setNamingStrategy' => new Reference($entityManager['naming_strategy']),
438
            'setQuoteStrategy' => new Reference($entityManager['quote_strategy']),
439
            'setEntityListenerResolver' => new Reference(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name'])),
440
        ];
441
442
        $listenerId        = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $entityManager['name']);
443
        $listenerDef       = $container->setDefinition($listenerId, new Definition('%doctrine.orm.listeners.attach_entity_listeners.class%'));
444
        $listenerTagParams = ['event' => 'loadClassMetadata'];
445
        if (isset($entityManager['connection'])) {
446
            $listenerTagParams['connection'] = $entityManager['connection'];
447
        }
448
        $listenerDef->addTag('doctrine.event_listener', $listenerTagParams);
449
450
        if (isset($entityManager['second_level_cache'])) {
451
            $this->loadOrmSecondLevelCache($entityManager, $ormConfigDef, $container);
452
        }
453
454
        if ($entityManager['repository_factory']) {
455
            $methods['setRepositoryFactory'] = new Reference($entityManager['repository_factory']);
456
        }
457
458
        foreach ($methods as $method => $arg) {
459
            $ormConfigDef->addMethodCall($method, [$arg]);
460
        }
461
462
        foreach ($entityManager['hydrators'] as $name => $class) {
463
            $ormConfigDef->addMethodCall('addCustomHydrationMode', [$name, $class]);
464
        }
465
466
        if (! empty($entityManager['dql'])) {
467
            foreach ($entityManager['dql']['string_functions'] as $name => $function) {
468
                $ormConfigDef->addMethodCall('addCustomStringFunction', [$name, $function]);
469
            }
470
            foreach ($entityManager['dql']['numeric_functions'] as $name => $function) {
471
                $ormConfigDef->addMethodCall('addCustomNumericFunction', [$name, $function]);
472
            }
473
            foreach ($entityManager['dql']['datetime_functions'] as $name => $function) {
474
                $ormConfigDef->addMethodCall('addCustomDatetimeFunction', [$name, $function]);
475
            }
476
        }
477
478
        $enabledFilters    = [];
479
        $filtersParameters = [];
480
        foreach ($entityManager['filters'] as $name => $filter) {
481
            $ormConfigDef->addMethodCall('addFilter', [$name, $filter['class']]);
482
            if ($filter['enabled']) {
483
                $enabledFilters[] = $name;
484
            }
485
            if (! $filter['parameters']) {
486
                continue;
487
            }
488
489
            $filtersParameters[$name] = $filter['parameters'];
490
        }
491
492
        $managerConfiguratorName = sprintf('doctrine.orm.%s_manager_configurator', $entityManager['name']);
493
        $container
494
            ->setDefinition($managerConfiguratorName, new ChildDefinition('doctrine.orm.manager_configurator.abstract'))
495
            ->replaceArgument(0, $enabledFilters)
496
            ->replaceArgument(1, $filtersParameters);
497
498
        if (! isset($entityManager['connection'])) {
499
            $entityManager['connection'] = $this->defaultConnection;
500
        }
501
502
        $container
503
            ->setDefinition(sprintf('doctrine.orm.%s_entity_manager', $entityManager['name']), new ChildDefinition('doctrine.orm.entity_manager.abstract'))
504
            ->setPublic(true)
505
            ->setArguments([
506
                new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])),
507
                new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])),
508
            ])
509
            ->setConfigurator([new Reference($managerConfiguratorName), 'configure']);
510
511
        $container->setAlias(
512
            sprintf('doctrine.orm.%s_entity_manager.event_manager', $entityManager['name']),
513
            new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $entityManager['connection']), false)
514
        );
515
516
        if (! isset($entityManager['entity_listeners'])) {
517
            return;
518
        }
519
520
        if (! isset($listenerDef)) {
521
            throw new InvalidArgumentException('Entity listeners configuration requires doctrine-orm 2.5.0 or newer');
522
        }
523
524
        $entities = $entityManager['entity_listeners']['entities'];
525
526
        foreach ($entities as $entityListenerClass => $entity) {
527
            foreach ($entity['listeners'] as $listenerClass => $listener) {
528
                foreach ($listener['events'] as $listenerEvent) {
529
                    $listenerEventName = $listenerEvent['type'];
530
                    $listenerMethod    = $listenerEvent['method'];
531
532
                    $listenerDef->addMethodCall('addEntityListener', [
533
                        $entityListenerClass,
534
                        $listenerClass,
535
                        $listenerEventName,
536
                        $listenerMethod,
537
                    ]);
538
                }
539
            }
540
        }
541
    }
542
543
    /**
544
     * Loads an ORM entity managers bundle mapping information.
545
     *
546
     * There are two distinct configuration possibilities for mapping information:
547
     *
548
     * 1. Specify a bundle and optionally details where the entity and mapping information reside.
549
     * 2. Specify an arbitrary mapping location.
550
     *
551
     * @param array            $entityManager A configured ORM entity manager
552
     * @param Definition       $ormConfigDef  A Definition instance
553
     * @param ContainerBuilder $container     A ContainerBuilder instance
554
     *
555
     * @example
556
     *
557
     *  doctrine.orm:
558
     *     mappings:
559
     *         MyBundle1: ~
560
     *         MyBundle2: yml
561
     *         MyBundle3: { type: annotation, dir: Entities/ }
562
     *         MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping }
563
     *         MyBundle5:
564
     *             type: yml
565
     *             dir: bundle-mappings/
566
     *             alias: BundleAlias
567
     *         arbitrary_key:
568
     *             type: xml
569
     *             dir: %kernel.root_dir%/../src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entities
570
     *             prefix: DoctrineExtensions\Entities\
571
     *             alias: DExt
572
     *
573
     * In the case of bundles everything is really optional (which leads to autodetection for this bundle) but
574
     * in the mappings key everything except alias is a required argument.
575
     */
576
    protected function loadOrmEntityManagerMappingInformation(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
577
    {
578
        // reset state of drivers and alias map. They are only used by this methods and children.
579
        $this->drivers  = [];
580
        $this->aliasMap = [];
581
582
        $this->loadMappingInformation($entityManager, $container);
583
        $this->registerMappingDrivers($entityManager, $container);
584
585
        $ormConfigDef->addMethodCall('setEntityNamespaces', [$this->aliasMap]);
586
    }
587
588
    /**
589
     * Loads an ORM second level cache bundle mapping information.
590
     *
591
     * @param array            $entityManager A configured ORM entity manager
592
     * @param Definition       $ormConfigDef  A Definition instance
593
     * @param ContainerBuilder $container     A ContainerBuilder instance
594
     *
595
     * @example
596
     *  entity_managers:
597
     *      default:
598
     *          second_level_cache:
599
     *              region_cache_driver: apc
600
     *              log_enabled: true
601
     *              regions:
602
     *                  my_service_region:
603
     *                      type: service
604
     *                      service : "my_service_region"
605
     *
606
     *                  my_query_region:
607
     *                      lifetime: 300
608
     *                      cache_driver: array
609
     *                      type: filelock
610
     *
611
     *                  my_entity_region:
612
     *                      lifetime: 600
613
     *                      cache_driver:
614
     *                          type: apc
615
     */
616
    protected function loadOrmSecondLevelCache(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
617
    {
618
        $driverId = null;
619
        $enabled  = $entityManager['second_level_cache']['enabled'];
620
621
        if (isset($entityManager['second_level_cache']['region_cache_driver'])) {
622
            $driverName = 'second_level_cache.region_cache_driver';
623
            $driverMap  = $entityManager['second_level_cache']['region_cache_driver'];
624
            $driverId   = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
625
        }
626
627
        $configId   = sprintf('doctrine.orm.%s_second_level_cache.cache_configuration', $entityManager['name']);
628
        $regionsId  = sprintf('doctrine.orm.%s_second_level_cache.regions_configuration', $entityManager['name']);
629
        $driverId   = $driverId ?: sprintf('doctrine.orm.%s_second_level_cache.region_cache_driver', $entityManager['name']);
630
        $configDef  = $container->setDefinition($configId, new Definition('%doctrine.orm.second_level_cache.cache_configuration.class%'));
631
        $regionsDef = $container->setDefinition($regionsId, new Definition('%doctrine.orm.second_level_cache.regions_configuration.class%'));
632
633
        $slcFactoryId = sprintf('doctrine.orm.%s_second_level_cache.default_cache_factory', $entityManager['name']);
634
        $factoryClass = isset($entityManager['second_level_cache']['factory']) ? $entityManager['second_level_cache']['factory'] : '%doctrine.orm.second_level_cache.default_cache_factory.class%';
635
636
        $definition = new Definition($factoryClass, [new Reference($regionsId), new Reference($driverId)]);
637
638
        $slcFactoryDef = $container
639
            ->setDefinition($slcFactoryId, $definition);
640
641
        if (isset($entityManager['second_level_cache']['regions'])) {
642
            foreach ($entityManager['second_level_cache']['regions'] as $name => $region) {
643
                $regionRef  = null;
644
                $regionType = $region['type'];
645
646
                if ($regionType === 'service') {
647
                    $regionId  = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
648
                    $regionRef = new Reference($region['service']);
649
650
                    $container->setAlias($regionId, new Alias($region['service'], false));
651
                }
652
653
                if ($regionType === 'default' || $regionType === 'filelock') {
654
                    $regionId   = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
655
                    $driverName = sprintf('second_level_cache.region.%s_driver', $name);
656
                    $driverMap  = $region['cache_driver'];
657
                    $driverId   = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
658
                    $regionRef  = new Reference($regionId);
659
660
                    $container
661
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.default_region.class%'))
662
                        ->setArguments([$name, new Reference($driverId), $region['lifetime']]);
663
                }
664
665
                if ($regionType === 'filelock') {
666
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s_filelock', $entityManager['name'], $name);
667
668
                    $container
669
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.filelock_region.class%'))
670
                        ->setArguments([$regionRef, $region['lock_path'], $region['lock_lifetime']]);
671
672
                    $regionRef = new Reference($regionId);
673
                    $regionsDef->addMethodCall('getLockLifetime', [$name, $region['lock_lifetime']]);
674
                }
675
676
                $regionsDef->addMethodCall('setLifetime', [$name, $region['lifetime']]);
677
                $slcFactoryDef->addMethodCall('setRegion', [$regionRef]);
678
            }
679
        }
680
681
        if ($entityManager['second_level_cache']['log_enabled']) {
682
            $loggerChainId   = sprintf('doctrine.orm.%s_second_level_cache.logger_chain', $entityManager['name']);
683
            $loggerStatsId   = sprintf('doctrine.orm.%s_second_level_cache.logger_statistics', $entityManager['name']);
684
            $loggerChaingDef = $container->setDefinition($loggerChainId, new Definition('%doctrine.orm.second_level_cache.logger_chain.class%'));
685
            $loggerStatsDef  = $container->setDefinition($loggerStatsId, new Definition('%doctrine.orm.second_level_cache.logger_statistics.class%'));
686
687
            $loggerChaingDef->addMethodCall('setLogger', ['statistics', $loggerStatsDef]);
688
            $configDef->addMethodCall('setCacheLogger', [$loggerChaingDef]);
689
690
            foreach ($entityManager['second_level_cache']['loggers'] as $name => $logger) {
691
                $loggerId  = sprintf('doctrine.orm.%s_second_level_cache.logger.%s', $entityManager['name'], $name);
692
                $loggerRef = new Reference($logger['service']);
693
694
                $container->setAlias($loggerId, new Alias($logger['service'], false));
695
                $loggerChaingDef->addMethodCall('setLogger', [$name, $loggerRef]);
696
            }
697
        }
698
699
        $configDef->addMethodCall('setCacheFactory', [$slcFactoryDef]);
700
        $configDef->addMethodCall('setRegionsConfiguration', [$regionsDef]);
701
        $ormConfigDef->addMethodCall('setSecondLevelCacheEnabled', [$enabled]);
702
        $ormConfigDef->addMethodCall('setSecondLevelCacheConfiguration', [$configDef]);
703
    }
704
705
    /**
706
     * {@inheritDoc}
707
     */
708
    protected function getObjectManagerElementName($name)
709
    {
710
        return 'doctrine.orm.' . $name;
711
    }
712
713
    protected function getMappingObjectDefaultName()
714
    {
715
        return 'Entity';
716
    }
717
718
    /**
719
     * {@inheritDoc}
720
     */
721
    protected function getMappingResourceConfigDirectory()
722
    {
723
        return 'Resources/config/doctrine';
724
    }
725
726
    /**
727
     * {@inheritDoc}
728
     */
729
    protected function getMappingResourceExtension()
730
    {
731
        return 'orm';
732
    }
733
734
    /**
735
     * {@inheritDoc}
736
     */
737
    protected function loadCacheDriver($driverName, $entityManagerName, array $driverMap, ContainerBuilder $container)
738
    {
739
        $serviceId = null;
740
        $aliasId   = $this->getObjectManagerElementName(sprintf('%s_%s', $entityManagerName, $driverName));
741
742
        switch ($driverMap['type']) {
743
            case 'service':
744
                $serviceId = $driverMap['id'];
745
                break;
746
747
            case 'pool':
748
                $serviceId = $this->createPoolCacheDefinition($container, $driverMap['pool']);
749
                break;
750
751
            case 'provider':
752
                $serviceId = sprintf('doctrine_cache.providers.%s', $driverMap['cache_provider']);
753
                break;
754
        }
755
756
        if ($serviceId !== null) {
757
            $container->setAlias($aliasId, new Alias($serviceId, false));
758
759
            return $aliasId;
760
        }
761
762
        return $this->adapter->loadCacheDriver($driverName, $entityManagerName, $driverMap, $container);
763
    }
764
765
    /**
766
     * Loads a configured entity managers cache drivers.
767
     *
768
     * @param array            $entityManager A configured ORM entity manager.
769
     * @param ContainerBuilder $container     A ContainerBuilder instance
770
     */
771
    protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container)
772
    {
773
        $this->loadCacheDriver('metadata_cache', $entityManager['name'], $entityManager['metadata_cache_driver'], $container);
774
        $this->loadCacheDriver('result_cache', $entityManager['name'], $entityManager['result_cache_driver'], $container);
775
        $this->loadCacheDriver('query_cache', $entityManager['name'], $entityManager['query_cache_driver'], $container);
776
    }
777
778
    /**
779
     * Loads a property info extractor for each defined entity manager.
780
     *
781
     * @param string $entityManagerName
782
     */
783
    private function loadPropertyInfoExtractor($entityManagerName, ContainerBuilder $container)
784
    {
785
        $propertyExtractorDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.property_info_extractor', $entityManagerName), DoctrineExtractor::class);
786
        if (property_exists(DoctrineExtractor::class, 'entityManager')) {
787
            $argumentId = sprintf('doctrine.orm.%s_entity_manager', $entityManagerName);
788
        } else {
789
            $argumentId = sprintf('doctrine.orm.%s_entity_manager.metadata_factory', $entityManagerName);
790
791
            $metadataFactoryDefinition = $container->register($argumentId, ClassMetadataFactory::class);
792
            $metadataFactoryDefinition->setFactory([
793
                new Reference(sprintf('doctrine.orm.%s_entity_manager', $entityManagerName)),
794
                'getMetadataFactory',
795
            ]);
796
            $metadataFactoryDefinition->setPublic(false);
797
        }
798
799
        $propertyExtractorDefinition->addArgument(new Reference($argumentId));
800
801
        $propertyExtractorDefinition->addTag('property_info.list_extractor', ['priority' => -1001]);
802
        $propertyExtractorDefinition->addTag('property_info.type_extractor', ['priority' => -999]);
803
804
        if (! is_a(DoctrineExtractor::class, PropertyAccessExtractorInterface::class, true)) {
805
            return;
806
        }
807
808
        $propertyExtractorDefinition->addTag('property_info.access_extractor', ['priority' => -999]);
809
    }
810
811
    /**
812
     * Loads a validator loader for each defined entity manager.
813
     */
814
    private function loadValidatorLoader(string $entityManagerName, ContainerBuilder $container) : void
815
    {
816
        if (! interface_exists(LoaderInterface::class) || ! class_exists(DoctrineLoader::class)) {
817
            return;
818
        }
819
820
        $validatorLoaderDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.validator_loader', $entityManagerName), DoctrineLoader::class);
821
        $validatorLoaderDefinition->addArgument(new Reference(sprintf('doctrine.orm.%s_entity_manager', $entityManagerName)));
822
823
        $validatorLoaderDefinition->addTag('validator.auto_mapper', ['priority' => -100]);
824
    }
825
826
    /**
827
     * @param array  $objectManager
828
     * @param string $cacheName
829
     */
830
    public function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName)
831
    {
832
        $this->loadCacheDriver($cacheName, $objectManager['name'], $objectManager[$cacheName . '_driver'], $container);
833
    }
834
835
    /**
836
     * {@inheritDoc}
837
     */
838
    public function getXsdValidationBasePath()
839
    {
840
        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...
841
    }
842
843
    /**
844
     * {@inheritDoc}
845
     */
846
    public function getNamespace()
847
    {
848
        return 'http://symfony.com/schema/dic/doctrine';
849
    }
850
851
    /**
852
     * {@inheritDoc}
853
     */
854
    public function getConfiguration(array $config, ContainerBuilder $container)
855
    {
856
        return new Configuration($container->getParameter('kernel.debug'));
857
    }
858
859
    private function loadMessengerServices(ContainerBuilder $container) : void
860
    {
861
        // If the Messenger component is installed and the doctrine transaction middleware is available, wire it:
862
        if (! interface_exists(MessageBusInterface::class) || ! class_exists(DoctrineTransactionMiddleware::class)) {
863
            return;
864
        }
865
866
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
867
        $loader->load('messenger.xml');
868
869
        if (! class_exists(DoctrineTransportFactory::class)) {
870
            return;
871
        }
872
873
        $transportFactoryDefinition = $container->getDefinition('messenger.transport.doctrine.factory');
874
        $transportFactoryDefinition->addTag('messenger.transport_factory');
875
    }
876
877
    private function createPoolCacheDefinition(ContainerBuilder $container, string $poolName) : string
878
    {
879
        if (! class_exists(DoctrineProvider::class)) {
880
            throw new LogicException('Using the "pool" cache type is only supported when symfony/cache is installed.');
881
        }
882
883
        $serviceId = sprintf('doctrine.orm.cache.pool.%s', $poolName);
884
885
        $definition = $container->register($serviceId, DoctrineProvider::class);
886
        $definition->addArgument(new Reference($poolName));
887
        $definition->setPrivate(true);
888
889
        return $serviceId;
890
    }
891
}
892