DoctrineExtension::loadOrmEntityManager()   F
last analyzed

Complexity

Conditions 21
Paths 7680

Size

Total Lines 130

Duplication

Lines 0
Ratio 0 %

Importance

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

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
745
        $aliasId   = $this->getObjectManagerElementName(sprintf('%s_%s', $objectManagerName, $cacheName));
746
747
        switch ($cacheDriver['type'] ?? 'pool') {
748
            case 'service':
749
                $serviceId = $cacheDriver['id'];
750
                break;
751
752
            case 'pool':
753
                $serviceId = $this->createPoolCacheDefinition($container, $cacheDriver['pool'] ?? $this->createArrayAdapterCachePool($container, $objectManagerName, $cacheName));
754
                break;
755
756
            default:
757
                throw new \InvalidArgumentException(sprintf(
758
                    'Unknown cache of type "%s" configured for cache "%s" in entity manager "%s".',
759
                    $cacheDriver['type'],
760
                    $cacheName,
761
                    $objectManagerName
762
                ));
763
        }
764
765
        $container->setAlias($aliasId, new Alias($serviceId, false));
766
767
        return $aliasId;
768
    }
769
770
    /**
771
     * Loads a configured entity managers cache drivers.
772
     *
773
     * @param array            $entityManager A configured ORM entity manager.
774
     * @param ContainerBuilder $container     A ContainerBuilder instance
775
     */
776
    protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container)
777
    {
778
        $this->loadCacheDriver('metadata_cache', $entityManager['name'], $entityManager['metadata_cache_driver'], $container);
779
        $this->loadCacheDriver('result_cache', $entityManager['name'], $entityManager['result_cache_driver'], $container);
780
        $this->loadCacheDriver('query_cache', $entityManager['name'], $entityManager['query_cache_driver'], $container);
781
    }
782
783
    /**
784
     * Loads a property info extractor for each defined entity manager.
785
     */
786
    private function loadPropertyInfoExtractor(string $entityManagerName, ContainerBuilder $container) : void
787
    {
788
        $propertyExtractorDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.property_info_extractor', $entityManagerName), DoctrineExtractor::class);
789
        $argumentId                  = sprintf('doctrine.orm.%s_entity_manager', $entityManagerName);
790
791
        $propertyExtractorDefinition->addArgument(new Reference($argumentId));
792
793
        $propertyExtractorDefinition->addTag('property_info.list_extractor', ['priority' => -1001]);
794
        $propertyExtractorDefinition->addTag('property_info.type_extractor', ['priority' => -999]);
795
        $propertyExtractorDefinition->addTag('property_info.access_extractor', ['priority' => -999]);
796
    }
797
798
    /**
799
     * Loads a validator loader for each defined entity manager.
800
     */
801
    private function loadValidatorLoader(string $entityManagerName, ContainerBuilder $container) : void
802
    {
803
        $validatorLoaderDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.validator_loader', $entityManagerName), DoctrineLoader::class);
804
        $validatorLoaderDefinition->addArgument(new Reference(sprintf('doctrine.orm.%s_entity_manager', $entityManagerName)));
805
806
        $validatorLoaderDefinition->addTag('validator.auto_mapper', ['priority' => -100]);
807
    }
808
809
    /**
810
     * @param array  $objectManager
811
     * @param string $cacheName
812
     */
813
    public function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName)
814
    {
815
        $this->loadCacheDriver($cacheName, $objectManager['name'], $objectManager[$cacheName . '_driver'], $container);
816
    }
817
818
    /**
819
     * {@inheritDoc}
820
     */
821
    public function getXsdValidationBasePath() : string
822
    {
823
        return __DIR__ . '/../Resources/config/schema';
824
    }
825
826
    /**
827
     * {@inheritDoc}
828
     */
829
    public function getNamespace() : string
830
    {
831
        return 'http://symfony.com/schema/dic/doctrine';
832
    }
833
834
    /**
835
     * {@inheritDoc}
836
     */
837
    public function getConfiguration(array $config, ContainerBuilder $container) : Configuration
838
    {
839
        return new Configuration($container->getParameter('kernel.debug'));
840
    }
841
842
    protected function getMetadataDriverClass(string $driverType) : string
843
    {
844
        return '%' . $this->getObjectManagerElementName('metadata.' . $driverType . '.class%');
845
    }
846
847
    private function loadMessengerServices(ContainerBuilder $container) : void
848
    {
849
        // If the Messenger component is installed and the doctrine transaction middleware is available, wire it:
850
        if (! interface_exists(MessageBusInterface::class) || ! class_exists(DoctrineTransactionMiddleware::class)) {
851
            return;
852
        }
853
854
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
855
        $loader->load('messenger.xml');
856
857
        if (! class_exists(DoctrineClearEntityManagerWorkerSubscriber::class)) {
858
            $container->removeDefinition('doctrine.orm.messenger.event_subscriber.doctrine_clear_entity_manager');
859
        }
860
861
        // available in Symfony 5.1 and higher
862
        if (! class_exists(MessengerTransportDoctrineSchemaSubscriber::class)) {
863
            $container->removeDefinition('doctrine.orm.messenger.doctrine_schema_subscriber');
864
        }
865
866
        $transportFactoryDefinition = $container->getDefinition('messenger.transport.doctrine.factory');
867
        if (! class_exists(DoctrineTransportFactory::class)) {
868
            // If symfony/messenger < 5.1
869
            if (! class_exists(\Symfony\Component\Messenger\Transport\Doctrine\DoctrineTransportFactory::class)) {
870
                // Dont add the tag
871
                return;
872
            }
873
874
            $transportFactoryDefinition->setClass(\Symfony\Component\Messenger\Transport\Doctrine\DoctrineTransportFactory::class);
875
        }
876
877
        $transportFactoryDefinition->addTag('messenger.transport_factory');
878
    }
879
880
    private function createPoolCacheDefinition(ContainerBuilder $container, string $poolName) : string
881
    {
882
        $serviceId = sprintf('doctrine.orm.cache.provider.%s', $poolName);
883
884
        $definition = $container->register($serviceId, DoctrineProvider::class);
885
        $definition->addArgument(new Reference($poolName));
886
        $definition->setPrivate(true);
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...efinition::setPrivate() has been deprecated with message: since Symfony 5.2, use setPublic() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
887
888
        return $serviceId;
889
    }
890
891
    private function createArrayAdapterCachePool(ContainerBuilder $container, string $objectManagerName, string $cacheName) : string
892
    {
893
        $id = sprintf('cache.doctrine.orm.%s.%s', $objectManagerName, str_replace('_cache', '', $cacheName));
894
895
        $poolDefinition = $container->register($id, ArrayAdapter::class);
896
        $poolDefinition->addTag('cache.pool');
897
        $container->setDefinition($id, $poolDefinition);
898
899
        return $id;
900
    }
901
}
902