Completed
Pull Request — master (#528)
by
unknown
03:31
created

getMappingResourceConfigDirectory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
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);
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
            $this->ormLoad($config['orm'], $container);
84
        }
85
86
        $this->addClassesToCompile(array(
87
            'Doctrine\\Common\\Annotations\\DocLexer',
88
            'Doctrine\\Common\\Annotations\\FileCacheReader',
89
            'Doctrine\\Common\\Annotations\\PhpParser',
90
            'Doctrine\\Common\\Annotations\\Reader',
91
            'Doctrine\\Common\\Lexer',
92
            'Doctrine\\Common\\Persistence\\ConnectionRegistry',
93
            'Doctrine\\Common\\Persistence\\Proxy',
94
            'Doctrine\\Common\\Util\\ClassUtils',
95
            'Doctrine\\Bundle\\DoctrineBundle\\Registry',
96
        ));
97
    }
98
99
    /**
100
     * Loads the DBAL configuration.
101
     *
102
     * Usage example:
103
     *
104
     *      <doctrine:dbal id="myconn" dbname="sfweb" user="root" />
105
     *
106
     * @param array            $config    An array of configuration settings
107
     * @param ContainerBuilder $container A ContainerBuilder instance
108
     */
109
    protected function dbalLoad(array $config, ContainerBuilder $container)
110
    {
111
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
112
        $loader->load('dbal.xml');
113
114
        if (empty($config['default_connection'])) {
115
            $keys = array_keys($config['connections']);
116
            $config['default_connection'] = reset($keys);
117
        }
118
119
        $this->defaultConnection = $config['default_connection'];
120
121
        $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection));
122
        $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false));
123
124
        $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']);
125
126
        $connections = array();
127
128
        foreach (array_keys($config['connections']) as $name) {
129
            $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name);
130
        }
131
132
        $container->setParameter('doctrine.connections', $connections);
133
        $container->setParameter('doctrine.default_connection', $this->defaultConnection);
134
135
        $def = $container->getDefinition('doctrine.dbal.connection');
136
        if (method_exists($def, 'setFactory')) {
137
            // to be inlined in dbal.xml when dependency on Symfony DependencyInjection is bumped to 2.6
138
            $def->setFactory(array(new Reference('doctrine.dbal.connection_factory'), 'createConnection'));
139
        } else {
140
            // to be removed when dependency on Symfony DependencyInjection is bumped to 2.6
141
            $def->setFactoryService('doctrine.dbal.connection_factory');
0 ignored issues
show
Bug introduced by
The method setFactoryService() does not exist on Symfony\Component\DependencyInjection\Definition. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
142
            $def->setFactoryMethod('createConnection');
0 ignored issues
show
Bug introduced by
The method setFactoryMethod() does not exist on Symfony\Component\DependencyInjection\Definition. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
143
        }
144
145
        foreach ($config['connections'] as $name => $connection) {
146
            $this->loadDbalConnection($name, $connection, $container);
147
        }
148
    }
149
150
    /**
151
     * Loads a configured DBAL connection.
152
     *
153
     * @param string           $name       The name of the connection
154
     * @param array            $connection A dbal connection configuration.
155
     * @param ContainerBuilder $container  A ContainerBuilder instance
156
     */
157
    protected function loadDbalConnection($name, array $connection, ContainerBuilder $container)
158
    {
159
        // configuration
160
        $configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new DefinitionDecorator('doctrine.dbal.connection.configuration'));
161
        $logger = null;
162
        if ($connection['logging']) {
163
            $logger = new Reference('doctrine.dbal.logger');
164
        }
165
        unset ($connection['logging']);
166
        if ($connection['profiling']) {
167
            $profilingLoggerId = 'doctrine.dbal.logger.profiling.'.$name;
168
            $container->setDefinition($profilingLoggerId, new DefinitionDecorator('doctrine.dbal.logger.profiling'));
169
            $profilingLogger = new Reference($profilingLoggerId);
170
            $container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', array($name, $profilingLogger));
171
172
            if (null !== $logger) {
173
                $chainLogger = new DefinitionDecorator('doctrine.dbal.logger.chain');
174
                $chainLogger->addMethodCall('addLogger', array($profilingLogger));
175
176
                $loggerId = 'doctrine.dbal.logger.chain.'.$name;
177
                $container->setDefinition($loggerId, $chainLogger);
178
                $logger = new Reference($loggerId);
179
            } else {
180
                $logger = $profilingLogger;
181
            }
182
        }
183
        unset($connection['profiling']);
184
185
        if (isset($connection['auto_commit'])) {
186
            $configuration->addMethodCall('setAutoCommit', array($connection['auto_commit']));
187
        }
188
189
        unset($connection['auto_commit']);
190
191
        if (isset($connection['schema_filter']) && $connection['schema_filter']) {
192
            $configuration->addMethodCall('setFilterSchemaAssetsExpression', array($connection['schema_filter']));
193
        }
194
195
        unset($connection['schema_filter']);
196
197
        if ($logger) {
198
            $configuration->addMethodCall('setSQLLogger', array($logger));
199
        }
200
201
        // event manager
202
        $container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new DefinitionDecorator('doctrine.dbal.connection.event_manager'));
203
204
        // connection
205
        // PDO ignores the charset property before 5.3.6 so the init listener has to be used instead.
206
        if (isset($connection['charset']) && version_compare(PHP_VERSION, '5.3.6', '<')) {
207
            if ((isset($connection['driver']) && stripos($connection['driver'], 'mysql') !== false) ||
208
                 (isset($connection['driver_class']) && stripos($connection['driver_class'], 'mysql') !== false)) {
209
                $mysqlSessionInit = new Definition('%doctrine.dbal.events.mysql_session_init.class%');
210
                $mysqlSessionInit->setArguments(array($connection['charset']));
211
                $mysqlSessionInit->setPublic(false);
212
                $mysqlSessionInit->addTag('doctrine.event_subscriber', array('connection' => $name));
213
214
                $container->setDefinition(
215
                    sprintf('doctrine.dbal.%s_connection.events.mysqlsessioninit', $name),
216
                    $mysqlSessionInit
217
                );
218
                unset($connection['charset']);
219
            }
220
        }
221
222
        $options = $this->getConnectionOptions($connection);
223
224
        $def = $container
225
            ->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new DefinitionDecorator('doctrine.dbal.connection'))
226
            ->setArguments(array(
227
                $options,
228
                new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)),
229
                new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)),
230
                $connection['mapping_types'],
231
            ))
232
        ;
233
234
        if (!empty($connection['use_savepoints'])) {
235
            $def->addMethodCall('setNestTransactionsWithSavepoints', array($connection['use_savepoints']));
236
        }
237
    }
238
239
    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...
240
    {
241
        $options = $connection;
242
243
        if (isset($options['platform_service'])) {
244
            $options['platform'] = new Reference($options['platform_service']);
245
            unset($options['platform_service']);
246
        }
247
        unset($options['mapping_types']);
248
249
        if (isset($options['shard_choser_service'])) {
250
            $options['shard_choser'] = new Reference($options['shard_choser_service']);
251
            unset($options['shard_choser_service']);
252
        }
253
254
        foreach (array(
255
            'options' => 'driverOptions',
256
            'driver_class' => 'driverClass',
257
            'wrapper_class' => 'wrapperClass',
258
            'keep_slave' => 'keepSlave',
259
            'shard_choser' => 'shardChoser',
260
            'server_version' => 'serverVersion',
261
            'default_table_options' => 'defaultTableOptions',
262
        ) as $old => $new) {
263
            if (isset($options[$old])) {
264
                $options[$new] = $options[$old];
265
                unset($options[$old]);
266
            }
267
        }
268
269
        if (!empty($options['slaves']) && !empty($options['shards'])) {
270
            throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together');
271
        }
272
273 View Code Duplication
        if (!empty($options['slaves'])) {
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...
274
            $nonRewrittenKeys = array(
275
                'driver' => true, 'driverOptions' => true, 'driverClass' => true,
276
                'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
277
                'platform' => true, 'slaves' => true, 'master' => true, 'shards' => true,
278
                'serverVersion' => true,
279
                // included by safety but should have been unset already
280
                'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true,
281
            );
282
            foreach ($options as $key => $value) {
283
                if (isset($nonRewrittenKeys[$key])) {
284
                    continue;
285
                }
286
                $options['master'][$key] = $value;
287
                unset($options[$key]);
288
            }
289
            if (empty($options['wrapperClass'])) {
290
                // Change the wrapper class only if the user does not already forced using a custom one.
291
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Connections\\MasterSlaveConnection';
292
            }
293
        } else {
294
            unset($options['slaves']);
295
        }
296
297 View Code Duplication
        if (!empty($options['shards'])) {
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...
298
            $nonRewrittenKeys = array(
299
                'driver' => true, 'driverOptions' => true, 'driverClass' => true,
300
                'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true,
301
                'platform' => true, 'slaves' => true, 'global' => true, 'shards' => true,
302
                // included by safety but should have been unset already
303
                'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true,
304
            );
305
            foreach ($options as $key => $value) {
306
                if (isset($nonRewrittenKeys[$key])) {
307
                    continue;
308
                }
309
                $options['global'][$key] = $value;
310
                unset($options[$key]);
311
            }
312
            if (empty($options['wrapperClass'])) {
313
                // Change the wrapper class only if the user does not already forced using a custom one.
314
                $options['wrapperClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardConnection';
315
            }
316
        } else {
317
            unset($options['shards']);
318
        }
319
320
        return $options;
321
    }
322
323
    /**
324
     * Loads the Doctrine ORM configuration.
325
     *
326
     * Usage example:
327
     *
328
     *     <doctrine:orm id="mydm" connection="myconn" />
329
     *
330
     * @param array            $config    An array of configuration settings
331
     * @param ContainerBuilder $container A ContainerBuilder instance
332
     */
333
    protected function ormLoad(array $config, ContainerBuilder $container)
334
    {
335
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
336
        $loader->load('orm.xml');
337
338
        $this->entityManagers = array();
339
        foreach (array_keys($config['entity_managers']) as $name) {
340
            $this->entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name);
341
        }
342
        $container->setParameter('doctrine.entity_managers', $this->entityManagers);
343
344
        if (empty($config['default_entity_manager'])) {
345
            $tmp = array_keys($this->entityManagers);
346
            $config['default_entity_manager'] = reset($tmp);
347
        }
348
        $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);
349
350
        $options = array('auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace');
351
        foreach ($options as $key) {
352
            $container->setParameter('doctrine.orm.'.$key, $config[$key]);
353
        }
354
355
        $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager']));
356
357
        // BC logic to handle DoctrineBridge < 2.6
358
        if (!method_exists($this, 'fixManagersAutoMappings')) {
359
            foreach ($config['entity_managers'] as $entityManager) {
360
                if ($entityManager['auto_mapping'] && count($config['entity_managers']) > 1) {
361
                    throw new \LogicException('You cannot enable "auto_mapping" when several entity managers are defined.');
362
                }
363
            }
364
        } else {
365
            $config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles'));
366
        }
367
368
        $def = $container->getDefinition('doctrine.orm.entity_manager.abstract');
369
        if (method_exists($def, 'setFactory')) {
370
            // to be inlined in dbal.xml when dependency on Symfony DependencyInjection is bumped to 2.6
371
            $def->setFactory(array('%doctrine.orm.entity_manager.class%', 'create'));
372
        } else {
373
            // to be removed when dependency on Symfony DependencyInjection is bumped to 2.6
374
            $def->setFactoryClass('%doctrine.orm.entity_manager.class%');
0 ignored issues
show
Bug introduced by
The method setFactoryClass() does not exist on Symfony\Component\DependencyInjection\Definition. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
375
            $def->setFactoryMethod('create');
0 ignored issues
show
Bug introduced by
The method setFactoryMethod() does not exist on Symfony\Component\DependencyInjection\Definition. Did you maybe mean setFactory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

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