Completed
Pull Request — master (#663)
by
unknown
02:49
created

loadOrmEntityManagerMappingInformation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 3
1
<?php
2
3
/*
4
 * This file is part of the Doctrine Bundle
5
 *
6
 * The code was originally distributed inside the Symfony framework.
7
 *
8
 * (c) Fabien Potencier <[email protected]>
9
 * (c) Doctrine Project, Benjamin Eberlei <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
16
17
use Doctrine\ORM\Version;
18
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
19
use Symfony\Component\DependencyInjection\Alias;
20
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
21
use Symfony\Component\DependencyInjection\ContainerBuilder;
22
use Symfony\Component\DependencyInjection\Definition;
23
use Symfony\Component\DependencyInjection\DefinitionDecorator;
24
use Symfony\Component\DependencyInjection\Reference;
25
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
26
use Symfony\Component\Config\FileLocator;
27
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\SymfonyBridgeAdapter;
28
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader;
29
30
/**
31
 * DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
32
 *
33
 * @author Jonathan H. Wage <[email protected]>
34
 * @author Fabien Potencier <[email protected]>
35
 * @author Benjamin Eberlei <[email protected]>
36
 * @author Fabio B. Silva <[email protected]>
37
 * @author Kinn Coelho Julião <[email protected]>
38
 */
39
class DoctrineExtension extends AbstractDoctrineExtension
40
{
41
    /**
42
     * @var string
43
     */
44
    private $defaultConnection;
45
46
    /**
47
     * @var array
48
     */
49
    private $entityManagers;
50
51
    /**
52
     * @var SymfonyBridgeAdapter
53
     */
54
    private $adapter;
55
56
    /**
57
     * @param SymfonyBridgeAdapter $adapter
0 ignored issues
show
Documentation introduced by
Should the type for parameter $adapter not be null|SymfonyBridgeAdapter?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
58
     */
59
    public function __construct(SymfonyBridgeAdapter $adapter = null)
60
    {
61
        $this->adapter = $adapter ?: new SymfonyBridgeAdapter(new CacheProviderLoader(), 'doctrine.orm', 'orm');
62
    }
63
64
    /**
65
     * {@inheritDoc}
66
     */
67
    public function load(array $configs, ContainerBuilder $container)
68
    {
69
        $configuration = $this->getConfiguration($configs, $container);
70
        $config = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 69 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...
71
72
        $this->adapter->loadServicesConfiguration($container);
73
74
        if (!empty($config['dbal'])) {
75
            $this->dbalLoad($config['dbal'], $container);
76
        }
77
78
        if (!empty($config['orm'])) {
79
            if (empty($config['dbal'])) {
80
                throw new \LogicException('Configuring the ORM layer requires to configure the DBAL layer as well.');
81
            }
82
83
            if (!class_exists('Doctrine\ORM\Version')) {
84
                throw new \LogicException('To configure the ORM layer, you must first install the doctrine/orm package.');
85
            }
86
87
            $this->ormLoad($config['orm'], $container);
88
        }
89
90
        if (PHP_VERSION_ID < 70000) {
91
            $this->addClassesToCompile(array(
92
                'Doctrine\\Common\\Annotations\\DocLexer',
93
                'Doctrine\\Common\\Annotations\\FileCacheReader',
94
                'Doctrine\\Common\\Annotations\\PhpParser',
95
                'Doctrine\\Common\\Annotations\\Reader',
96
                'Doctrine\\Common\\Lexer',
97
                'Doctrine\\Common\\Persistence\\ConnectionRegistry',
98
                'Doctrine\\Common\\Persistence\\Proxy',
99
                'Doctrine\\Common\\Util\\ClassUtils',
100
                'Doctrine\\Bundle\\DoctrineBundle\\Registry',
101
            ));
102
        }
103
    }
104
105
    /**
106
     * Loads the DBAL configuration.
107
     *
108
     * Usage example:
109
     *
110
     *      <doctrine:dbal id="myconn" dbname="sfweb" user="root" />
111
     *
112
     * @param array            $config    An array of configuration settings
113
     * @param ContainerBuilder $container A ContainerBuilder instance
114
     */
115
    protected function dbalLoad(array $config, ContainerBuilder $container)
116
    {
117
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
118
        $loader->load('dbal.xml');
119
120
        if (empty($config['default_connection'])) {
121
            $keys = array_keys($config['connections']);
122
            $config['default_connection'] = reset($keys);
123
        }
124
125
        $this->defaultConnection = $config['default_connection'];
126
127
        $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection));
128
        $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false));
129
130
        $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']);
131
132
        $connections = array();
133
134
        foreach (array_keys($config['connections']) as $name) {
135
            $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name);
136
        }
137
138
        $container->setParameter('doctrine.connections', $connections);
139
        $container->setParameter('doctrine.default_connection', $this->defaultConnection);
140
141
        foreach ($config['connections'] as $name => $connection) {
142
            $this->loadDbalConnection($name, $connection, $container);
143
        }
144
    }
145
146
    /**
147
     * Loads a configured DBAL connection.
148
     *
149
     * @param string           $name       The name of the connection
150
     * @param array            $connection A dbal connection configuration.
151
     * @param ContainerBuilder $container  A ContainerBuilder instance
152
     */
153
    protected function loadDbalConnection($name, array $connection, ContainerBuilder $container)
154
    {
155
        // configuration
156
        $configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new DefinitionDecorator('doctrine.dbal.connection.configuration'));
157
        $logger = null;
158
        if ($connection['logging']) {
159
            $logger = new Reference('doctrine.dbal.logger');
160
        }
161
        unset ($connection['logging']);
162
        if ($connection['profiling']) {
163
            $profilingLoggerId = 'doctrine.dbal.logger.profiling.'.$name;
164
            $container->setDefinition($profilingLoggerId, new DefinitionDecorator('doctrine.dbal.logger.profiling'));
165
            $profilingLogger = new Reference($profilingLoggerId);
166
            $container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', array($name, $profilingLogger));
167
168
            if (null !== $logger) {
169
                $chainLogger = new DefinitionDecorator('doctrine.dbal.logger.chain');
170
                $chainLogger->addMethodCall('addLogger', array($profilingLogger));
171
172
                $loggerId = 'doctrine.dbal.logger.chain.'.$name;
173
                $container->setDefinition($loggerId, $chainLogger);
174
                $logger = new Reference($loggerId);
175
            } else {
176
                $logger = $profilingLogger;
177
            }
178
        }
179
        unset($connection['profiling']);
180
181
        if (isset($connection['auto_commit'])) {
182
            $configuration->addMethodCall('setAutoCommit', array($connection['auto_commit']));
183
        }
184
185
        unset($connection['auto_commit']);
186
187
        if (isset($connection['schema_filter']) && $connection['schema_filter']) {
188
            $configuration->addMethodCall('setFilterSchemaAssetsExpression', array($connection['schema_filter']));
189
        }
190
191
        unset($connection['schema_filter']);
192
193
        if ($logger) {
194
            $configuration->addMethodCall('setSQLLogger', array($logger));
195
        }
196
197
        // event manager
198
        $container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new DefinitionDecorator('doctrine.dbal.connection.event_manager'));
199
200
        // connection
201
        $options = $this->getConnectionOptions($connection);
202
203
        $def = $container
204
            ->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new DefinitionDecorator('doctrine.dbal.connection'))
205
            ->setArguments(array(
206
                $options,
207
                new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)),
208
                new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)),
209
                $connection['mapping_types'],
210
            ))
211
        ;
212
213
        if (!empty($connection['use_savepoints'])) {
214
            $def->addMethodCall('setNestTransactionsWithSavepoints', array($connection['use_savepoints']));
215
        }
216
217
        // Create a shard_manager for this connection
218
        if (isset($options['shards'])) {
219
            $shardManagerDefinition = new Definition($options['shardManagerClass'], array(
220
                new Reference(sprintf('doctrine.dbal.%s_connection', $name))
221
            ));
222
            $container->setDefinition(sprintf('doctrine.dbal.%s_shard_manager', $name), $shardManagerDefinition);
223
        }
224
225
        // Set class in case "wrapper_class" option was used to assist IDEs
226
        if (isset($options['wrapperClass'])) {
227
            $def->setClass($options['wrapperClass']);
228
        }
229
    }
230
231
    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...
232
    {
233
        $options = $connection;
234
235
        if (isset($options['platform_service'])) {
236
            $options['platform'] = new Reference($options['platform_service']);
237
            unset($options['platform_service']);
238
        }
239
        unset($options['mapping_types']);
240
241
        if (isset($options['shard_choser_service'])) {
242
            $options['shard_choser'] = new Reference($options['shard_choser_service']);
243
            unset($options['shard_choser_service']);
244
        }
245
246
        foreach (array(
247
            'options' => 'driverOptions',
248
            'driver_class' => 'driverClass',
249
            'wrapper_class' => 'wrapperClass',
250
            'keep_slave' => 'keepSlave',
251
            'shard_choser' => 'shardChoser',
252
            'shard_manager_class' => 'shardManagerClass',
253
            'server_version' => 'serverVersion',
254
            'default_table_options' => 'defaultTableOptions',
255
        ) as $old => $new) {
256
            if (isset($options[$old])) {
257
                $options[$new] = $options[$old];
258
                unset($options[$old]);
259
            }
260
        }
261
262
        if (!empty($options['slaves']) && !empty($options['shards'])) {
263
            throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together');
264
        }
265
266
        if (!empty($options['slaves'])) {
267
            $nonRewrittenKeys = array(
268
                'driver' => true, 'driverOptions' => true, 'driverClass' => true,
269
                'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
270
                'platform' => true, 'slaves' => true, 'master' => true, 'shards' => true,
271
                'serverVersion' => true,
272
                // included by safety but should have been unset already
273
                'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true,
274
            );
275
            foreach ($options as $key => $value) {
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 = array(
292
                'driver' => true, 'driverOptions' => true, 'driverClass' => true,
293
                'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
294
                'platform' => true, 'slaves' => true, 'global' => true, 'shards' => true,
295
                'serverVersion' => true,
296
                // included by safety but should have been unset already
297
                'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true,
298
            );
299
            foreach ($options as $key => $value) {
300
                if (isset($nonRewrittenKeys[$key])) {
301
                    continue;
302
                }
303
                $options['global'][$key] = $value;
304
                unset($options[$key]);
305
            }
306
            if (empty($options['wrapperClass'])) {
307
                // Change the wrapper class only if the user does not already forced using a custom one.
308
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardConnection';
309
            }
310
            if (empty($options['shardManagerClass'])) {
311
                // Change the shard manager class only if the user does not already forced using a custom one.
312
                $options['shardManagerClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardManager';
313
            }
314
        } else {
315
            unset($options['shards']);
316
        }
317
318
        return $options;
319
    }
320
321
    /**
322
     * Loads the Doctrine ORM configuration.
323
     *
324
     * Usage example:
325
     *
326
     *     <doctrine:orm id="mydm" connection="myconn" />
327
     *
328
     * @param array            $config    An array of configuration settings
329
     * @param ContainerBuilder $container A ContainerBuilder instance
330
     */
331
    protected function ormLoad(array $config, ContainerBuilder $container)
332
    {
333
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
334
        $loader->load('orm.xml');
335
336
        $this->entityManagers = array();
337
        foreach (array_keys($config['entity_managers']) as $name) {
338
            $this->entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name);
339
        }
340
        $container->setParameter('doctrine.entity_managers', $this->entityManagers);
341
342
        if (empty($config['default_entity_manager'])) {
343
            $tmp = array_keys($this->entityManagers);
344
            $config['default_entity_manager'] = reset($tmp);
345
        }
346
        $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);
347
348
        $options = array('auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace');
349
        foreach ($options as $key) {
350
            $container->setParameter('doctrine.orm.'.$key, $config[$key]);
351
        }
352
353
        $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager']));
354
355
        $config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles'));
356
357
        $loadPropertyInfoExtractor = interface_exists('Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface')
358
            && class_exists('Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor');
359
360
        foreach ($config['entity_managers'] as $name => $entityManager) {
361
            $entityManager['name'] = $name;
362
            $this->loadOrmEntityManager($entityManager, $container);
363
364
            if ($loadPropertyInfoExtractor) {
365
                $this->loadPropertyInfoExtractor($name, $container);
366
            }
367
        }
368
369
        if ($config['resolve_target_entities']) {
370
            $def = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity');
371
            foreach ($config['resolve_target_entities'] as $name => $implementation) {
372
                $def->addMethodCall('addResolveTargetEntity', array(
373
                    $name, $implementation, array(),
374
                ));
375
            }
376
377
            // BC: ResolveTargetEntityListener implements the subscriber interface since
378
            // v2.5.0-beta1 (Commit 437f812)
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
379
            if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) {
380
                $def->addTag('doctrine.event_listener', array('event' => 'loadClassMetadata'));
381
            } else {
382
                $def->addTag('doctrine.event_subscriber');
383
            }
384
        }
385
    }
386
387
    /**
388
     * Loads a configured ORM entity manager.
389
     *
390
     * @param array            $entityManager A configured ORM entity manager.
391
     * @param ContainerBuilder $container     A ContainerBuilder instance
392
     */
393
    protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $container)
394
    {
395
        $ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new DefinitionDecorator('doctrine.orm.configuration'));
396
397
        $this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container);
398
        $this->loadOrmCacheDrivers($entityManager, $container);
399
400
        if (isset($entityManager['entity_listener_resolver']) && $entityManager['entity_listener_resolver']) {
401
            $container->setAlias(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $entityManager['entity_listener_resolver']);
402
        } else {
403
            $definition = new Definition('%doctrine.orm.entity_listener_resolver.class%');
404
            $definition->addArgument(new Reference('service_container'));
405
            $container->setDefinition(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $definition);
406
        }
407
408
        $methods = array(
409
            'setMetadataCacheImpl' => new Reference(sprintf('doctrine.orm.%s_metadata_cache', $entityManager['name'])),
410
            'setQueryCacheImpl' => new Reference(sprintf('doctrine.orm.%s_query_cache', $entityManager['name'])),
411
            'setResultCacheImpl' => new Reference(sprintf('doctrine.orm.%s_result_cache', $entityManager['name'])),
412
            'setMetadataDriverImpl' => new Reference('doctrine.orm.'.$entityManager['name'].'_metadata_driver'),
413
            'setProxyDir' => '%doctrine.orm.proxy_dir%',
414
            'setProxyNamespace' => '%doctrine.orm.proxy_namespace%',
415
            'setAutoGenerateProxyClasses' => '%doctrine.orm.auto_generate_proxy_classes%',
416
            'setClassMetadataFactoryName' => $entityManager['class_metadata_factory_name'],
417
            'setDefaultRepositoryClassName' => $entityManager['default_repository_class'],
418
        );
419
        // check for version to keep BC
420
        if (version_compare(Version::VERSION, "2.3.0-DEV") >= 0) {
421
            $methods = array_merge($methods, array(
422
                'setNamingStrategy' => new Reference($entityManager['naming_strategy']),
423
                'setQuoteStrategy' => new Reference($entityManager['quote_strategy']),
424
            ));
425
        }
426
427
        if (version_compare(Version::VERSION, "2.4.0-DEV") >= 0) {
428
            $methods = array_merge($methods, array(
429
                'setEntityListenerResolver' => new Reference(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name'])),
430
            ));
431
        }
432
433
        if (version_compare(Version::VERSION, "2.5.0-DEV") >= 0) {
434
            $listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $entityManager['name']);
435
            $listenerDef = $container->setDefinition($listenerId, new Definition('%doctrine.orm.listeners.attach_entity_listeners.class%'));
436
            $listenerTagParams = array('event' => 'loadClassMetadata');
437
            if (isset($entityManager['connection'])) {
438
                $listenerTagParams['connection'] = $entityManager['connection'];
439
            }
440
            $listenerDef->addTag('doctrine.event_listener', $listenerTagParams);
441
        }
442
443
        if (isset($entityManager['second_level_cache'])) {
444
            $this->loadOrmSecondLevelCache($entityManager, $ormConfigDef, $container);
445
        }
446
447
        if ($entityManager['repository_factory']) {
448
            $methods['setRepositoryFactory'] = new Reference($entityManager['repository_factory']);
449
        }
450
451
        foreach ($methods as $method => $arg) {
452
            $ormConfigDef->addMethodCall($method, array($arg));
453
        }
454
455
        foreach ($entityManager['hydrators'] as $name => $class) {
456
            $ormConfigDef->addMethodCall('addCustomHydrationMode', array($name, $class));
457
        }
458
459
        if (!empty($entityManager['dql'])) {
460
            foreach ($entityManager['dql']['string_functions'] as $name => $function) {
461
                $ormConfigDef->addMethodCall('addCustomStringFunction', array($name, $function));
462
            }
463
            foreach ($entityManager['dql']['numeric_functions'] as $name => $function) {
464
                $ormConfigDef->addMethodCall('addCustomNumericFunction', array($name, $function));
465
            }
466
            foreach ($entityManager['dql']['datetime_functions'] as $name => $function) {
467
                $ormConfigDef->addMethodCall('addCustomDatetimeFunction', array($name, $function));
468
            }
469
        }
470
471
        $enabledFilters = array();
472
        $filtersParameters = array();
473
        foreach ($entityManager['filters'] as $name => $filter) {
474
            $ormConfigDef->addMethodCall('addFilter', array($name, $filter['class']));
475
            if ($filter['enabled']) {
476
                $enabledFilters[] = $name;
477
            }
478
            if ($filter['parameters']) {
479
                $filtersParameters[$name] = $filter['parameters'];
480
            }
481
        }
482
483
        $managerConfiguratorName = sprintf('doctrine.orm.%s_manager_configurator', $entityManager['name']);
484
        $container
485
            ->setDefinition($managerConfiguratorName, new DefinitionDecorator('doctrine.orm.manager_configurator.abstract'))
486
            ->replaceArgument(0, $enabledFilters)
487
            ->replaceArgument(1, $filtersParameters)
488
        ;
489
490
        if (!isset($entityManager['connection'])) {
491
            $entityManager['connection'] = $this->defaultConnection;
492
        }
493
494
        $container
495
            ->setDefinition(sprintf('doctrine.orm.%s_entity_manager', $entityManager['name']), new DefinitionDecorator('doctrine.orm.entity_manager.abstract'))
496
            ->setArguments(array(
497
                new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])),
498
                new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])),
499
            ))
500
            ->setConfigurator(array(new Reference($managerConfiguratorName), 'configure'))
501
        ;
502
503
        $container->setAlias(
504
            sprintf('doctrine.orm.%s_entity_manager.event_manager', $entityManager['name']),
505
            new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $entityManager['connection']), false)
506
        );
507
508
        if (isset($entityManager['entity_listeners'])) {
509
            if (!isset($listenerDef)) {
510
                throw new InvalidArgumentException('Entity listeners configuration requires doctrine-orm 2.5.0 or newer');
511
            }
512
513
            $entities = $entityManager['entity_listeners']['entities'];
514
515
            foreach ($entities as $entityListenerClass => $entity) {
516
                foreach ($entity['listeners'] as $listenerClass => $listener) {
517
                    foreach ($listener['events'] as $listenerEvent) {
518
                        $listenerEventName = $listenerEvent['type'];
519
                        $listenerMethod = $listenerEvent['method'];
520
521
                        $listenerDef->addMethodCall('addEntityListener', array(
522
                            $entityListenerClass, $listenerClass, $listenerEventName, $listenerMethod,
523
                        ));
524
                    }
525
                }
526
            }
527
528
        }
529
    }
530
531
    /**
532
     * Loads an ORM entity managers bundle mapping information.
533
     *
534
     * There are two distinct configuration possibilities for mapping information:
535
     *
536
     * 1. Specify a bundle and optionally details where the entity and mapping information reside.
537
     * 2. Specify an arbitrary mapping location.
538
     *
539
     * @example
540
     *
541
     *  doctrine.orm:
542
     *     mappings:
543
     *         MyBundle1: ~
544
     *         MyBundle2: yml
545
     *         MyBundle3: { type: annotation, dir: Entities/ }
546
     *         MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping }
547
     *         MyBundle5:
548
     *             type: yml
549
     *             dir: bundle-mappings/
550
     *             alias: BundleAlias
551
     *         arbitrary_key:
552
     *             type: xml
553
     *             dir: %kernel.root_dir%/../src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entities
554
     *             prefix: DoctrineExtensions\Entities\
555
     *             alias: DExt
556
     *
557
     * In the case of bundles everything is really optional (which leads to autodetection for this bundle) but
558
     * in the mappings key everything except alias is a required argument.
559
     *
560
     * @param array            $entityManager A configured ORM entity manager
561
     * @param Definition       $ormConfigDef  A Definition instance
562
     * @param ContainerBuilder $container     A ContainerBuilder instance
563
     */
564
    protected function loadOrmEntityManagerMappingInformation(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
565
    {
566
        // reset state of drivers and alias map. They are only used by this methods and children.
567
        $this->drivers = array();
568
        $this->aliasMap = array();
569
570
        $this->loadMappingInformation($entityManager, $container);
571
        $this->registerMappingDrivers($entityManager, $container);
572
573
        $ormConfigDef->addMethodCall('setEntityNamespaces', array($this->aliasMap));
574
    }
575
576
    /**
577
     * Loads an ORM second level cache bundle mapping information.
578
     *
579
     * @example
580
     *  entity_managers:
581
     *      default:
582
     *          second_level_cache:
583
     *              region_cache_driver: apc
584
     *              log_enabled: true
585
     *              regions:
586
     *                  my_service_region:
587
     *                      type: service
588
     *                      service : "my_service_region"
589
     *
590
     *                  my_query_region:
591
     *                      lifetime: 300
592
     *                      cache_driver: array
593
     *                      type: filelock
594
     *
595
     *                  my_entity_region:
596
     *                      lifetime: 600
597
     *                      cache_driver:
598
     *                          type: apc
599
     *
600
     * @param array            $entityManager A configured ORM entity manager
601
     * @param Definition       $ormConfigDef  A Definition instance
602
     * @param ContainerBuilder $container     A ContainerBuilder instance
603
     */
604
    protected function loadOrmSecondLevelCache(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container)
605
    {
606
        if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) {
607
            throw new \InvalidArgumentException('Second-level cache requires doctrine-orm 2.5.0 or newer');
608
        }
609
610
        $driverId = null;
611
        $enabled = $entityManager['second_level_cache']['enabled'];
612
613
        if (isset($entityManager['second_level_cache']['region_cache_driver'])) {
614
            $driverName = 'second_level_cache.region_cache_driver';
615
            $driverMap = $entityManager['second_level_cache']['region_cache_driver'];
616
            $driverId = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
617
        }
618
619
        $configId = sprintf('doctrine.orm.%s_second_level_cache.cache_configuration', $entityManager['name']);
620
        $regionsId = sprintf('doctrine.orm.%s_second_level_cache.regions_configuration', $entityManager['name']);
621
        $driverId = $driverId ?: sprintf('doctrine.orm.%s_second_level_cache.region_cache_driver', $entityManager['name']);
622
        $configDef = $container->setDefinition($configId, new Definition('%doctrine.orm.second_level_cache.cache_configuration.class%'));
623
        $regionsDef = $container->setDefinition($regionsId, new Definition('%doctrine.orm.second_level_cache.regions_configuration.class%'));
624
625
        $slcFactoryId = sprintf('doctrine.orm.%s_second_level_cache.default_cache_factory', $entityManager['name']);
626
        $factoryClass = isset($entityManager['second_level_cache']['factory']) ? $entityManager['second_level_cache']['factory'] : '%doctrine.orm.second_level_cache.default_cache_factory.class%';
627
628
        $definition = new Definition($factoryClass, array(new Reference($regionsId), new Reference($driverId)));
629
630
        $slcFactoryDef = $container
631
            ->setDefinition($slcFactoryId, $definition);
632
633
        if (isset($entityManager['second_level_cache']['regions'])) {
634
            foreach ($entityManager['second_level_cache']['regions'] as $name => $region) {
635
                $regionRef = null;
636
                $regionType = $region['type'];
637
638
                if ($regionType === 'service') {
639
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
640
                    $regionRef = new Reference($region['service']);
641
642
                    $container->setAlias($regionId, new Alias($region['service'], false));
643
                }
644
645
                if ($regionType === 'default' || $regionType === 'filelock') {
646
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name);
647
                    $driverName = sprintf('second_level_cache.region.%s_driver', $name);
648
                    $driverMap = $region['cache_driver'];
649
                    $driverId = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container);
650
                    $regionRef = new Reference($regionId);
651
652
                    $container
653
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.default_region.class%'))
654
                        ->setArguments(array($name, new Reference($driverId), $region['lifetime']));
655
                }
656
657
                if ($regionType === 'filelock') {
658
                    $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s_filelock', $entityManager['name'], $name);
659
660
                    $container
661
                        ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.filelock_region.class%'))
662
                        ->setArguments(array($regionRef, $region['lock_path'], $region['lock_lifetime']));
663
664
                    $regionRef = new Reference($regionId);
665
                    $regionsDef->addMethodCall('getLockLifetime', array($name, $region['lock_lifetime']));
666
                }
667
668
                $regionsDef->addMethodCall('setLifetime', array($name, $region['lifetime']));
669
                $slcFactoryDef->addMethodCall('setRegion', array($regionRef));
670
            }
671
        }
672
673
        if ($entityManager['second_level_cache']['log_enabled']) {
674
            $loggerChainId = sprintf('doctrine.orm.%s_second_level_cache.logger_chain', $entityManager['name']);
675
            $loggerStatsId = sprintf('doctrine.orm.%s_second_level_cache.logger_statistics', $entityManager['name']);
676
            $loggerChaingDef = $container->setDefinition($loggerChainId, new Definition('%doctrine.orm.second_level_cache.logger_chain.class%'));
677
            $loggerStatsDef = $container->setDefinition($loggerStatsId, new Definition('%doctrine.orm.second_level_cache.logger_statistics.class%'));
678
679
            $loggerChaingDef->addMethodCall('setLogger', array('statistics', $loggerStatsDef));
680
            $configDef->addMethodCall('setCacheLogger', array($loggerChaingDef));
681
682
            foreach ($entityManager['second_level_cache']['loggers'] as $name => $logger) {
683
                $loggerId = sprintf('doctrine.orm.%s_second_level_cache.logger.%s', $entityManager['name'], $name);
684
                $loggerRef = new Reference($logger['service']);
685
686
                $container->setAlias($loggerId, new Alias($logger['service'], false));
687
                $loggerChaingDef->addMethodCall('setLogger', array($name, $loggerRef));
688
            }
689
        }
690
691
        $configDef->addMethodCall('setCacheFactory', array($slcFactoryDef));
692
        $configDef->addMethodCall('setRegionsConfiguration', array($regionsDef));
693
        $ormConfigDef->addMethodCall('setSecondLevelCacheEnabled', array($enabled));
694
        $ormConfigDef->addMethodCall('setSecondLevelCacheConfiguration', array($configDef));
695
    }
696
697
    /**
698
     * {@inheritDoc}
699
     */
700
    protected function getObjectManagerElementName($name)
701
    {
702
        return 'doctrine.orm.'.$name;
703
    }
704
705
    protected function getMappingObjectDefaultName()
706
    {
707
        return 'Entity';
708
    }
709
710
    /**
711
     * {@inheritDoc}
712
     */
713
    protected function getMappingResourceConfigDirectory()
714
    {
715
        return 'Resources/config/doctrine';
716
    }
717
718
    /**
719
     * {@inheritDoc}
720
     */
721
    protected function getMappingResourceExtension()
722
    {
723
        return 'orm';
724
    }
725
726
    /**
727
     * {@inheritDoc}
728
     */
729
    protected function loadCacheDriver($driverName, $entityManagerName, array $driverMap, ContainerBuilder $container)
730
    {
731
        if (!empty($driverMap['cache_provider'])) {
732
            $aliasId = $this->getObjectManagerElementName(sprintf('%s_%s', $entityManagerName, $driverName));
733
            $serviceId = sprintf('doctrine_cache.providers.%s', $driverMap['cache_provider']);
734
735
            $container->setAlias($aliasId, new Alias($serviceId, false));
736
737
            return $aliasId;
738
        }
739
740
        return $this->adapter->loadCacheDriver($driverName, $entityManagerName, $driverMap, $container);
741
    }
742
743
    /**
744
     * Loads a configured entity managers cache drivers.
745
     *
746
     * @param array            $entityManager A configured ORM entity manager.
747
     * @param ContainerBuilder $container     A ContainerBuilder instance
748
     */
749
    protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container)
750
    {
751
        $this->loadCacheDriver('metadata_cache', $entityManager['name'], $entityManager['metadata_cache_driver'], $container);
752
        $this->loadCacheDriver('result_cache', $entityManager['name'], $entityManager['result_cache_driver'], $container);
753
        $this->loadCacheDriver('query_cache', $entityManager['name'], $entityManager['query_cache_driver'], $container);
754
    }
755
756
    /**
757
     * Loads a property info extractor for each defined entity manager.
758
     *
759
     * @param string           $entityManagerName
760
     * @param ContainerBuilder $container
761
     */
762
    private function loadPropertyInfoExtractor($entityManagerName, ContainerBuilder $container)
763
    {
764
        $metadataFactoryService = sprintf('doctrine.orm.%s_entity_manager.metadata_factory', $entityManagerName);
765
766
        $metadataFactoryDefinition = $container->register($metadataFactoryService, 'Doctrine\Common\Persistence\Mapping\ClassMetadataFactory');
767
        $metadataFactoryDefinition->setFactory(array(
768
            new Reference(sprintf('doctrine.orm.%s_entity_manager', $entityManagerName)),
769
            'getMetadataFactory'
770
        ));
771
        $metadataFactoryDefinition->setPublic(false);
772
773
        $propertyExtractorDefinition = $container->register(sprintf('doctrine.orm.%s_entity_manager.property_info_extractor', $entityManagerName), 'Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor');
774
        $propertyExtractorDefinition->addArgument(new Reference($metadataFactoryService));
775
        $propertyExtractorDefinition->addTag('property_info.list_extractor', array('priority' => -1001));
776
        $propertyExtractorDefinition->addTag('property_info.type_extractor', array('priority' => -999));
777
    }
778
779
    /**
780
     * @param array            $objectManager
781
     * @param ContainerBuilder $container
782
     * @param string           $cacheName
783
     */
784
    public function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName)
785
    {
786
        $this->loadCacheDriver($cacheName, $objectManager['name'], $objectManager[$cacheName.'_driver'], $container);
787
    }
788
789
    /**
790
     * {@inheritDoc}
791
     */
792
    public function getXsdValidationBasePath()
793
    {
794
        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...
795
    }
796
797
    /**
798
     * {@inheritDoc}
799
     */
800
    public function getNamespace()
801
    {
802
        return 'http://symfony.com/schema/dic/doctrine';
803
    }
804
805
    /**
806
     * {@inheritDoc}
807
     */
808
    public function getConfiguration(array $config, ContainerBuilder $container)
809
    {
810
        return new Configuration($container->getParameter('kernel.debug'));
811
    }
812
}
813