Completed
Push — master ( 8fb9db...dfdb22 )
by Andreas
19s
created

getMappingResourceConfigDirectory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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