|
1 <?php |
|
2 |
|
3 /* |
|
4 * This file is part of the Symfony package. |
|
5 * |
|
6 * (c) Fabien Potencier <fabien@symfony.com> |
|
7 * |
|
8 * For the full copyright and license information, please view the LICENSE |
|
9 * file that was distributed with this source code. |
|
10 */ |
|
11 |
|
12 namespace Symfony\Bundle\DoctrineBundle\DependencyInjection; |
|
13 |
|
14 use Symfony\Component\DependencyInjection\Alias; |
|
15 use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; |
|
16 use Symfony\Component\DependencyInjection\ContainerBuilder; |
|
17 use Symfony\Component\DependencyInjection\Definition; |
|
18 use Symfony\Component\DependencyInjection\DefinitionDecorator; |
|
19 use Symfony\Component\DependencyInjection\Reference; |
|
20 use Symfony\Bundle\DoctrineAbstractBundle\DependencyInjection\AbstractDoctrineExtension; |
|
21 use Symfony\Component\Config\FileLocator; |
|
22 |
|
23 /** |
|
24 * DoctrineExtension is an extension for the Doctrine DBAL and ORM library. |
|
25 * |
|
26 * @author Jonathan H. Wage <jonwage@gmail.com> |
|
27 * @author Fabien Potencier <fabien@symfony.com> |
|
28 * @author Benjamin Eberlei <kontakt@beberlei.de> |
|
29 */ |
|
30 class DoctrineExtension extends AbstractDoctrineExtension |
|
31 { |
|
32 public function load(array $configs, ContainerBuilder $container) |
|
33 { |
|
34 $configuration = new Configuration($container->getParameter('kernel.debug')); |
|
35 $config = $this->processConfiguration($configuration, $configs); |
|
36 |
|
37 if (!empty($config['dbal'])) { |
|
38 $this->dbalLoad($config['dbal'], $container); |
|
39 } |
|
40 |
|
41 if (!empty($config['orm'])) { |
|
42 $this->ormLoad($config['orm'], $container); |
|
43 } |
|
44 } |
|
45 |
|
46 /** |
|
47 * Loads the DBAL configuration. |
|
48 * |
|
49 * Usage example: |
|
50 * |
|
51 * <doctrine:dbal id="myconn" dbname="sfweb" user="root" /> |
|
52 * |
|
53 * @param array $config An array of configuration settings |
|
54 * @param ContainerBuilder $container A ContainerBuilder instance |
|
55 */ |
|
56 protected function dbalLoad(array $config, ContainerBuilder $container) |
|
57 { |
|
58 $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); |
|
59 $loader->load('dbal.xml'); |
|
60 |
|
61 if (empty($config['default_connection'])) { |
|
62 $keys = array_keys($config['connections']); |
|
63 $config['default_connection'] = reset($keys); |
|
64 } |
|
65 $this->defaultConnection = $config['default_connection']; |
|
66 |
|
67 $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection)); |
|
68 $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false)); |
|
69 |
|
70 $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']); |
|
71 |
|
72 $connections = array(); |
|
73 foreach (array_keys($config['connections']) as $name) { |
|
74 $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name); |
|
75 } |
|
76 $container->setParameter('doctrine.connections', $connections); |
|
77 $container->setParameter('doctrine.default_connection', $this->defaultConnection); |
|
78 |
|
79 foreach ($config['connections'] as $name => $connection) { |
|
80 $this->loadDbalConnection($name, $connection, $container); |
|
81 } |
|
82 } |
|
83 |
|
84 /** |
|
85 * Loads a configured DBAL connection. |
|
86 * |
|
87 * @param string $name The name of the connection |
|
88 * @param array $connection A dbal connection configuration. |
|
89 * @param ContainerBuilder $container A ContainerBuilder instance |
|
90 */ |
|
91 protected function loadDbalConnection($name, array $connection, ContainerBuilder $container) |
|
92 { |
|
93 // configuration |
|
94 $configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new DefinitionDecorator('doctrine.dbal.connection.configuration')); |
|
95 if (isset($connection['logging']) && $connection['logging']) { |
|
96 $configuration->addMethodCall('setSQLLogger', array(new Reference('doctrine.dbal.logger'))); |
|
97 unset ($connection['logging']); |
|
98 } |
|
99 |
|
100 // event manager |
|
101 $def = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new DefinitionDecorator('doctrine.dbal.connection.event_manager')); |
|
102 |
|
103 // connection |
|
104 if (isset($connection['charset'])) { |
|
105 if ((isset($connection['driver']) && stripos($connection['driver'], 'mysql') !== false) || |
|
106 (isset($connection['driver_class']) && stripos($connection['driver_class'], 'mysql') !== false)) { |
|
107 $mysqlSessionInit = new Definition('%doctrine.dbal.events.mysql_session_init.class%'); |
|
108 $mysqlSessionInit->setArguments(array($connection['charset'])); |
|
109 $mysqlSessionInit->setPublic(false); |
|
110 $mysqlSessionInit->addTag('doctrine.event_subscriber', array('connection' => $name)); |
|
111 |
|
112 $container->setDefinition( |
|
113 sprintf('doctrine.dbal.%s_connection.events.mysqlsessioninit', $name), |
|
114 $mysqlSessionInit |
|
115 ); |
|
116 unset($connection['charset']); |
|
117 } |
|
118 } |
|
119 |
|
120 $options = $this->getConnectionOptions($connection); |
|
121 |
|
122 $container |
|
123 ->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new DefinitionDecorator('doctrine.dbal.connection')) |
|
124 ->setArguments(array( |
|
125 $options, |
|
126 new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)), |
|
127 new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)), |
|
128 $connection['mapping_types'], |
|
129 )) |
|
130 ; |
|
131 } |
|
132 |
|
133 protected function getConnectionOptions($connection) |
|
134 { |
|
135 $options = $connection; |
|
136 |
|
137 if (isset($options['platform_service'])) { |
|
138 $options['platform'] = new Reference($options['platform_service']); |
|
139 unset($options['platform_service']); |
|
140 } |
|
141 unset($options['mapping_types']); |
|
142 |
|
143 foreach (array( |
|
144 'options' => 'driverOptions', |
|
145 'driver_class' => 'driverClass', |
|
146 'wrapper_class' => 'wrapperClass', |
|
147 ) as $old => $new) { |
|
148 if (isset($options[$old])) { |
|
149 $options[$new] = $options[$old]; |
|
150 unset($options[$old]); |
|
151 } |
|
152 } |
|
153 |
|
154 return $options; |
|
155 } |
|
156 |
|
157 /** |
|
158 * Loads the Doctrine ORM configuration. |
|
159 * |
|
160 * Usage example: |
|
161 * |
|
162 * <doctrine:orm id="mydm" connection="myconn" /> |
|
163 * |
|
164 * @param array $config An array of configuration settings |
|
165 * @param ContainerBuilder $container A ContainerBuilder instance |
|
166 */ |
|
167 protected function ormLoad(array $config, ContainerBuilder $container) |
|
168 { |
|
169 $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); |
|
170 $loader->load('orm.xml'); |
|
171 |
|
172 $this->entityManagers = array(); |
|
173 foreach (array_keys($config['entity_managers']) as $name) { |
|
174 $this->entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name); |
|
175 } |
|
176 $container->setParameter('doctrine.entity_managers', $this->entityManagers); |
|
177 |
|
178 if (empty($config['default_entity_manager'])) { |
|
179 $tmp = array_keys($this->entityManagers); |
|
180 $config['default_entity_manager'] = reset($tmp); |
|
181 } |
|
182 $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']); |
|
183 |
|
184 $options = array('auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace'); |
|
185 foreach ($options as $key) { |
|
186 $container->setParameter('doctrine.orm.'.$key, $config[$key]); |
|
187 } |
|
188 |
|
189 $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager'])); |
|
190 |
|
191 foreach ($config['entity_managers'] as $name => $entityManager) { |
|
192 $entityManager['name'] = $name; |
|
193 $this->loadOrmEntityManager($entityManager, $container); |
|
194 } |
|
195 } |
|
196 |
|
197 /** |
|
198 * Loads a configured ORM entity manager. |
|
199 * |
|
200 * @param array $entityManager A configured ORM entity manager. |
|
201 * @param ContainerBuilder $container A ContainerBuilder instance |
|
202 */ |
|
203 protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $container) |
|
204 { |
|
205 if ($entityManager['auto_mapping'] && count($this->entityManagers) > 1) { |
|
206 throw new \LogicException('You cannot enable "auto_mapping" when several entity managers are defined.'); |
|
207 } |
|
208 |
|
209 $ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new DefinitionDecorator('doctrine.orm.configuration')); |
|
210 |
|
211 $this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container); |
|
212 $this->loadOrmCacheDrivers($entityManager, $container); |
|
213 |
|
214 $methods = array( |
|
215 'setMetadataCacheImpl' => new Reference(sprintf('doctrine.orm.%s_metadata_cache', $entityManager['name'])), |
|
216 'setQueryCacheImpl' => new Reference(sprintf('doctrine.orm.%s_query_cache', $entityManager['name'])), |
|
217 'setResultCacheImpl' => new Reference(sprintf('doctrine.orm.%s_result_cache', $entityManager['name'])), |
|
218 'setMetadataDriverImpl' => new Reference('doctrine.orm.'.$entityManager['name'].'_metadata_driver'), |
|
219 'setProxyDir' => '%doctrine.orm.proxy_dir%', |
|
220 'setProxyNamespace' => '%doctrine.orm.proxy_namespace%', |
|
221 'setAutoGenerateProxyClasses' => '%doctrine.orm.auto_generate_proxy_classes%', |
|
222 'setClassMetadataFactoryName' => $entityManager['class_metadata_factory_name'], |
|
223 ); |
|
224 foreach ($methods as $method => $arg) { |
|
225 $ormConfigDef->addMethodCall($method, array($arg)); |
|
226 } |
|
227 |
|
228 foreach ($entityManager['hydrators'] as $name => $class) { |
|
229 $ormConfigDef->addMethodCall('addCustomHydrationMode', array($name, $class)); |
|
230 } |
|
231 |
|
232 if (!empty($entityManager['dql'])) { |
|
233 foreach ($entityManager['dql']['string_functions'] as $name => $function) { |
|
234 $ormConfigDef->addMethodCall('addCustomStringFunction', array($name, $function)); |
|
235 } |
|
236 foreach ($entityManager['dql']['numeric_functions'] as $name => $function) { |
|
237 $ormConfigDef->addMethodCall('addCustomNumericFunction', array($name, $function)); |
|
238 } |
|
239 foreach ($entityManager['dql']['datetime_functions'] as $name => $function) { |
|
240 $ormConfigDef->addMethodCall('addCustomDatetimeFunction', array($name, $function)); |
|
241 } |
|
242 } |
|
243 |
|
244 if (!isset($entityManager['connection'])) { |
|
245 $entityManager['connection'] = $this->defaultConnection; |
|
246 } |
|
247 |
|
248 $container |
|
249 ->setDefinition(sprintf('doctrine.orm.%s_entity_manager', $entityManager['name']), new DefinitionDecorator('doctrine.orm.entity_manager.abstract')) |
|
250 ->setArguments(array( |
|
251 new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])), |
|
252 new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])) |
|
253 )) |
|
254 ; |
|
255 |
|
256 $container->setAlias( |
|
257 sprintf('doctrine.orm.%s_entity_manager.event_manager', $entityManager['name']), |
|
258 new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $entityManager['connection']), false) |
|
259 ); |
|
260 } |
|
261 |
|
262 /** |
|
263 * Loads an ORM entity managers bundle mapping information. |
|
264 * |
|
265 * There are two distinct configuration possibilities for mapping information: |
|
266 * |
|
267 * 1. Specify a bundle and optionally details where the entity and mapping information reside. |
|
268 * 2. Specify an arbitrary mapping location. |
|
269 * |
|
270 * @example |
|
271 * |
|
272 * doctrine.orm: |
|
273 * mappings: |
|
274 * MyBundle1: ~ |
|
275 * MyBundle2: yml |
|
276 * MyBundle3: { type: annotation, dir: Entities/ } |
|
277 * MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping } |
|
278 * MyBundle5: |
|
279 * type: yml |
|
280 * dir: [bundle-mappings1/, bundle-mappings2/] |
|
281 * alias: BundleAlias |
|
282 * arbitrary_key: |
|
283 * type: xml |
|
284 * dir: %kernel.dir%/../src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entities |
|
285 * prefix: DoctrineExtensions\Entities\ |
|
286 * alias: DExt |
|
287 * |
|
288 * In the case of bundles everything is really optional (which leads to autodetection for this bundle) but |
|
289 * in the mappings key everything except alias is a required argument. |
|
290 * |
|
291 * @param array $entityManager A configured ORM entity manager. |
|
292 * @param ContainerBuilder $container A ContainerBuilder instance |
|
293 */ |
|
294 protected function loadOrmEntityManagerMappingInformation(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container) |
|
295 { |
|
296 // reset state of drivers and alias map. They are only used by this methods and children. |
|
297 $this->drivers = array(); |
|
298 $this->aliasMap = array(); |
|
299 |
|
300 $this->loadMappingInformation($entityManager, $container); |
|
301 $this->registerMappingDrivers($entityManager, $container); |
|
302 |
|
303 $ormConfigDef->addMethodCall('setEntityNamespaces', array($this->aliasMap)); |
|
304 } |
|
305 |
|
306 protected function getObjectManagerElementName($name) |
|
307 { |
|
308 return 'doctrine.orm.'.$name; |
|
309 } |
|
310 |
|
311 protected function getMappingObjectDefaultName() |
|
312 { |
|
313 return 'Entity'; |
|
314 } |
|
315 |
|
316 protected function getMappingResourceConfigDirectory() |
|
317 { |
|
318 return 'Resources/config/doctrine'; |
|
319 } |
|
320 |
|
321 protected function getMappingResourceExtension() |
|
322 { |
|
323 return 'orm'; |
|
324 } |
|
325 |
|
326 /** |
|
327 * Loads a configured entity managers cache drivers. |
|
328 * |
|
329 * @param array $entityManager A configured ORM entity manager. |
|
330 * @param ContainerBuilder $container A ContainerBuilder instance |
|
331 */ |
|
332 protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container) |
|
333 { |
|
334 $this->loadOrmEntityManagerCacheDriver($entityManager, $container, 'metadata_cache'); |
|
335 $this->loadOrmEntityManagerCacheDriver($entityManager, $container, 'result_cache'); |
|
336 $this->loadOrmEntityManagerCacheDriver($entityManager, $container, 'query_cache'); |
|
337 } |
|
338 |
|
339 /** |
|
340 * Loads a configured entity managers metadata, query or result cache driver. |
|
341 * |
|
342 * @param array $entityManager A configured ORM entity manager. |
|
343 * @param ContainerBuilder $container A ContainerBuilder instance |
|
344 * @param string $cacheName |
|
345 */ |
|
346 protected function loadOrmEntityManagerCacheDriver(array $entityManager, ContainerBuilder $container, $cacheName) |
|
347 { |
|
348 $cacheDriverService = sprintf('doctrine.orm.%s_%s', $entityManager['name'], $cacheName); |
|
349 |
|
350 $driver = $cacheName."_driver"; |
|
351 $cacheDef = $this->getEntityManagerCacheDefinition($entityManager, $entityManager[$driver], $container); |
|
352 $container->setDefinition($cacheDriverService, $cacheDef); |
|
353 } |
|
354 |
|
355 /** |
|
356 * Gets an entity manager cache driver definition for metadata, query and result caches. |
|
357 * |
|
358 * @param array $entityManager The array configuring an entity manager. |
|
359 * @param array $cacheDriver The cache driver configuration. |
|
360 * @param ContainerBuilder $container |
|
361 * @return Definition $cacheDef |
|
362 */ |
|
363 protected function getEntityManagerCacheDefinition(array $entityManager, $cacheDriver, ContainerBuilder $container) |
|
364 { |
|
365 switch ($cacheDriver['type']) { |
|
366 case 'memcache': |
|
367 $memcacheClass = !empty($cacheDriver['class']) ? $cacheDriver['class'] : '%doctrine.orm.cache.memcache.class%'; |
|
368 $memcacheInstanceClass = !empty($cacheDriver['instance_class']) ? $cacheDriver['instance_class'] : '%doctrine.orm.cache.memcache_instance.class%'; |
|
369 $memcacheHost = !empty($cacheDriver['host']) ? $cacheDriver['host'] : '%doctrine.orm.cache.memcache_host%'; |
|
370 $memcachePort = !empty($cacheDriver['port']) ? $cacheDriver['port'] : '%doctrine.orm.cache.memcache_port%'; |
|
371 $cacheDef = new Definition($memcacheClass); |
|
372 $memcacheInstance = new Definition($memcacheInstanceClass); |
|
373 $memcacheInstance->addMethodCall('connect', array( |
|
374 $memcacheHost, $memcachePort |
|
375 )); |
|
376 $container->setDefinition(sprintf('doctrine.orm.%s_memcache_instance', $entityManager['name']), $memcacheInstance); |
|
377 $cacheDef->addMethodCall('setMemcache', array(new Reference(sprintf('doctrine.orm.%s_memcache_instance', $entityManager['name'])))); |
|
378 break; |
|
379 case 'apc': |
|
380 case 'array': |
|
381 case 'xcache': |
|
382 $cacheDef = new Definition('%'.sprintf('doctrine.orm.cache.%s.class', $cacheDriver['type']).'%'); |
|
383 break; |
|
384 default: |
|
385 throw new \InvalidArgumentException(sprintf('"%s" is an unrecognized Doctrine cache driver.', $cacheDriver['type'])); |
|
386 } |
|
387 |
|
388 $cacheDef->setPublic(false); |
|
389 // generate a unique namespace for the given application |
|
390 $namespace = 'sf2orm_'.$entityManager['name'].'_'.md5($container->getParameter('kernel.root_dir').$container->getParameter('kernel.environment')); |
|
391 $cacheDef->addMethodCall('setNamespace', array($namespace)); |
|
392 |
|
393 return $cacheDef; |
|
394 } |
|
395 |
|
396 /** |
|
397 * Returns the base path for the XSD files. |
|
398 * |
|
399 * @return string The XSD base path |
|
400 */ |
|
401 public function getXsdValidationBasePath() |
|
402 { |
|
403 return __DIR__.'/../Resources/config/schema'; |
|
404 } |
|
405 |
|
406 /** |
|
407 * Returns the namespace to be used for this extension (XML namespace). |
|
408 * |
|
409 * @return string The XML namespace |
|
410 */ |
|
411 public function getNamespace() |
|
412 { |
|
413 return 'http://symfony.com/schema/dic/doctrine'; |
|
414 } |
|
415 } |