vendor/symfony/src/Symfony/Bundle/DoctrineAbstractBundle/DependencyInjection/AbstractDoctrineExtension.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     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\DoctrineAbstractBundle\DependencyInjection;
       
    13 
       
    14 use Symfony\Component\HttpKernel\DependencyInjection\Extension;
       
    15 use Symfony\Component\DependencyInjection\ContainerBuilder;
       
    16 use Symfony\Component\DependencyInjection\Definition;
       
    17 use Symfony\Component\DependencyInjection\Reference;
       
    18 use Symfony\Component\Config\Resource\FileResource;
       
    19 
       
    20 /**
       
    21  * This abstract classes groups common code that Doctrine Object Manager extensions (ORM, MongoDB, CouchDB) need.
       
    22  */
       
    23 abstract class AbstractDoctrineExtension extends Extension
       
    24 {
       
    25     /**
       
    26      * Used inside metadata driver method to simplify aggregation of data.
       
    27      *
       
    28      * @var array
       
    29      */
       
    30     protected $aliasMap = array();
       
    31 
       
    32     /**
       
    33      * Used inside metadata driver method to simplify aggregation of data.
       
    34      *
       
    35      * @var array
       
    36      */
       
    37     protected $drivers = array();
       
    38 
       
    39     /*
       
    40      * @param array            $objectManager A configured object manager.
       
    41      * @param ContainerBuilder $container     A ContainerBuilder instance
       
    42      */
       
    43     protected function loadMappingInformation(array $objectManager, ContainerBuilder $container)
       
    44     {
       
    45         if ($objectManager['auto_mapping']) {
       
    46             // automatically register bundle mappings
       
    47             foreach (array_keys($container->getParameter('kernel.bundles')) as $bundle) {
       
    48                 if (!isset($objectManager['mappings'][$bundle])) {
       
    49                     $objectManager['mappings'][$bundle] = null;
       
    50                 }
       
    51             }
       
    52         }
       
    53 
       
    54         foreach ($objectManager['mappings'] as $mappingName => $mappingConfig) {
       
    55             if (null !== $mappingConfig && false === $mappingConfig['mapping']) {
       
    56                 continue;
       
    57             }
       
    58 
       
    59             $mappingConfig = array_replace(array(
       
    60                 'dir'    => false,
       
    61                 'type'   => false,
       
    62                 'prefix' => false,
       
    63             ), (array) $mappingConfig);
       
    64 
       
    65             $mappingConfig['dir'] = $container->getParameterBag()->resolveValue($mappingConfig['dir']);
       
    66             // a bundle configuration is detected by realizing that the specified dir is not absolute and existing
       
    67             if (!isset($mappingConfig['is_bundle'])) {
       
    68                 $mappingConfig['is_bundle'] = !file_exists($mappingConfig['dir']);
       
    69             }
       
    70 
       
    71             if ($mappingConfig['is_bundle']) {
       
    72                 $bundle = null;
       
    73                 foreach ($container->getParameter('kernel.bundles') as $name => $class) {
       
    74                     if ($mappingName === $name) {
       
    75                         $bundle = new \ReflectionClass($class);
       
    76 
       
    77                         break;
       
    78                     }
       
    79                 }
       
    80 
       
    81                 if (null === $bundle) {
       
    82                     throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled.', $mappingName));
       
    83                 }
       
    84 
       
    85                 $mappingConfig = $this->getMappingDriverBundleConfigDefaults($mappingConfig, $bundle, $container);
       
    86                 if (!$mappingConfig) {
       
    87                     continue;
       
    88                 }
       
    89             }
       
    90 
       
    91             $this->assertValidMappingConfiguration($mappingConfig, $objectManager['name']);
       
    92             $this->setMappingDriverConfig($mappingConfig, $mappingName);
       
    93             $this->setMappingDriverAlias($mappingConfig, $mappingName);
       
    94         }
       
    95     }
       
    96 
       
    97     /**
       
    98      * Register the alias for this mapping driver.
       
    99      *
       
   100      * Aliases can be used in the Query languages of all the Doctrine object managers to simplify writing tasks.
       
   101      *
       
   102      * @param array $mappingConfig
       
   103      * @param string $mappingName
       
   104      * @return void
       
   105      */
       
   106     protected function setMappingDriverAlias($mappingConfig, $mappingName)
       
   107     {
       
   108         if (isset($mappingConfig['alias'])) {
       
   109             $this->aliasMap[$mappingConfig['alias']] = $mappingConfig['prefix'];
       
   110         } else {
       
   111             $this->aliasMap[$mappingName] = $mappingConfig['prefix'];
       
   112         }
       
   113     }
       
   114 
       
   115     /**
       
   116      * Register the mapping driver configuration for later use with the object managers metadata driver chain.
       
   117      *
       
   118      * @param array  $mappingConfig
       
   119      * @param string $mappingName
       
   120      * @return void
       
   121      */
       
   122     protected function setMappingDriverConfig(array $mappingConfig, $mappingName)
       
   123     {
       
   124         if (is_dir($mappingConfig['dir'])) {
       
   125             $this->drivers[$mappingConfig['type']][$mappingConfig['prefix']] = realpath($mappingConfig['dir']);
       
   126         } else {
       
   127             throw new \InvalidArgumentException(sprintf('Invalid Doctrine mapping path given. Cannot load Doctrine mapping/bundle named "%s".', $mappingName));
       
   128         }
       
   129     }
       
   130 
       
   131     /**
       
   132      * If this is a bundle controlled mapping all the missing information can be autodetected by this method.
       
   133      *
       
   134      * Returns false when autodetection failed, an array of the completed information otherwise.
       
   135      *
       
   136      * @param array            $bundleConfig
       
   137      * @param \ReflectionClass $bundle
       
   138      * @param ContainerBuilder $container    A ContainerBuilder instance
       
   139      *
       
   140      * @return array|false
       
   141      */
       
   142     protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \ReflectionClass $bundle, ContainerBuilder $container)
       
   143     {
       
   144         $bundleDir = dirname($bundle->getFilename());
       
   145 
       
   146         if (!$bundleConfig['type']) {
       
   147             $bundleConfig['type'] = $this->detectMetadataDriver($bundleDir, $container);
       
   148         }
       
   149 
       
   150         if (!$bundleConfig['type']) {
       
   151             // skip this bundle, no mapping information was found.
       
   152             return false;
       
   153         }
       
   154 
       
   155         if (!$bundleConfig['dir']) {
       
   156             if (in_array($bundleConfig['type'], array('annotation', 'staticphp'))) {
       
   157                 $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingObjectDefaultName();
       
   158             } else {
       
   159                 $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingResourceConfigDirectory();
       
   160             }
       
   161         } else {
       
   162             $bundleConfig['dir'] = $bundleDir.'/'.$bundleConfig['dir'];
       
   163         }
       
   164 
       
   165         if (!$bundleConfig['prefix']) {
       
   166             $bundleConfig['prefix'] = $bundle->getNamespaceName().'\\'.$this->getMappingObjectDefaultName();
       
   167         }
       
   168 
       
   169         return $bundleConfig;
       
   170     }
       
   171 
       
   172     /**
       
   173      * Register all the collected mapping information with the object manager by registering the appropriate mapping drivers.
       
   174      *
       
   175      * @param array            $objectManager
       
   176      * @param ContainerBuilder $container     A ContainerBuilder instance
       
   177      */
       
   178     protected function registerMappingDrivers($objectManager, ContainerBuilder $container)
       
   179     {
       
   180         // configure metadata driver for each bundle based on the type of mapping files found
       
   181         if ($container->hasDefinition($this->getObjectManagerElementName($objectManager['name'].'_metadata_driver'))) {
       
   182             $chainDriverDef = $container->getDefinition($this->getObjectManagerElementName($objectManager['name'].'_metadata_driver'));
       
   183         } else {
       
   184             $chainDriverDef = new Definition('%'.$this->getObjectManagerElementName('metadata.driver_chain.class%'));
       
   185             $chainDriverDef->setPublic(false);
       
   186         }
       
   187 
       
   188         foreach ($this->drivers as $driverType => $driverPaths) {
       
   189             $mappingService = $this->getObjectManagerElementName($objectManager['name'].'_'.$driverType.'_metadata_driver');
       
   190             if ($container->hasDefinition($mappingService)) {
       
   191                 $mappingDriverDef = $container->getDefinition($mappingService);
       
   192                 $args = $mappingDriverDef->getArguments();
       
   193                 if ($driverType == 'annotation') {
       
   194                     $args[1] = array_merge(array_values($driverPaths), $args[1]);
       
   195                 } else {
       
   196                     $args[0] = array_merge(array_values($driverPaths), $args[0]);
       
   197                 }
       
   198                 $mappingDriverDef->setArguments($args);
       
   199             } else if ($driverType == 'annotation') {
       
   200                 $mappingDriverDef = new Definition('%'.$this->getObjectManagerElementName('metadata.'.$driverType.'.class%'), array(
       
   201                     new Reference($this->getObjectManagerElementName('metadata.annotation_reader')),
       
   202                     array_values($driverPaths)
       
   203                 ));
       
   204             } else {
       
   205                 $mappingDriverDef = new Definition('%'.$this->getObjectManagerElementName('metadata.'.$driverType.'.class%'), array(
       
   206                     array_values($driverPaths)
       
   207                 ));
       
   208             }
       
   209             $mappingDriverDef->setPublic(false);
       
   210             if (false !== strpos($mappingDriverDef->getClass(), 'yml') || false !== strpos($mappingDriverDef->getClass(), 'xml')) {
       
   211                 $mappingDriverDef->addMethodCall('setNamespacePrefixes', array(array_flip($driverPaths)));
       
   212                 $mappingDriverDef->addMethodCall('setGlobalBasename', array('mapping'));
       
   213             }
       
   214 
       
   215             $container->setDefinition($mappingService, $mappingDriverDef);
       
   216 
       
   217             foreach ($driverPaths as $prefix => $driverPath) {
       
   218                 $chainDriverDef->addMethodCall('addDriver', array(new Reference($mappingService), $prefix));
       
   219             }
       
   220         }
       
   221 
       
   222         $container->setDefinition($this->getObjectManagerElementName($objectManager['name'].'_metadata_driver'), $chainDriverDef);
       
   223     }
       
   224 
       
   225     /**
       
   226      * Assertion if the specified mapping information is valid.
       
   227      *
       
   228      * @param array  $mappingConfig
       
   229      * @param string $objectManagerName
       
   230      */
       
   231     protected function assertValidMappingConfiguration(array $mappingConfig, $objectManagerName)
       
   232     {
       
   233         if (!$mappingConfig['type'] || !$mappingConfig['dir'] || !$mappingConfig['prefix']) {
       
   234             throw new \InvalidArgumentException(sprintf('Mapping definitions for Doctrine manager "%s" require at least the "type", "dir" and "prefix" options.', $objectManagerName));
       
   235         }
       
   236 
       
   237         if (!file_exists($mappingConfig['dir'])) {
       
   238             throw new \InvalidArgumentException(sprintf('Specified non-existing directory "%s" as Doctrine mapping source.', $mappingConfig['dir']));
       
   239         }
       
   240 
       
   241         if (!in_array($mappingConfig['type'], array('xml', 'yml', 'annotation', 'php', 'staticphp'))) {
       
   242             throw new \InvalidArgumentException(sprintf('Can only configure "xml", "yml", "annotation", "php" or '.
       
   243                 '"staticphp" through the DoctrineBundle. Use your own bundle to configure other metadata drivers. '.
       
   244                 'You can register them by adding a a new driver to the '.
       
   245                 '"%s" service definition.', $this->getObjectManagerElementName($objectManagerName.'.metadata_driver')
       
   246             ));
       
   247         }
       
   248     }
       
   249 
       
   250     /**
       
   251      * Detects what metadata driver to use for the supplied directory.
       
   252      *
       
   253      * @param string           $dir       A directory path
       
   254      * @param ContainerBuilder $container A ContainerBuilder instance
       
   255      *
       
   256      * @return string|null A metadata driver short name, if one can be detected
       
   257      */
       
   258     protected function detectMetadataDriver($dir, ContainerBuilder $container)
       
   259     {
       
   260         // add the closest existing directory as a resource
       
   261         $configPath = $this->getMappingResourceConfigDirectory();
       
   262         $resource = $dir.'/'.$configPath;
       
   263         while (!is_dir($resource)) {
       
   264             $resource = dirname($resource);
       
   265         }
       
   266         $container->addResource(new FileResource($resource));
       
   267 
       
   268         $extension = $this->getMappingResourceExtension();
       
   269         if (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.xml')) && count($files)) {
       
   270             return 'xml';
       
   271         } elseif (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.yml')) && count($files)) {
       
   272             return 'yml';
       
   273         } elseif (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.php')) && count($files)) {
       
   274             return 'php';
       
   275         }
       
   276 
       
   277         // add the directory itself as a resource
       
   278         $container->addResource(new FileResource($dir));
       
   279 
       
   280         if (is_dir($dir.'/'.$this->getMappingObjectDefaultName())) {
       
   281             return 'annotation';
       
   282         }
       
   283 
       
   284         return null;
       
   285     }
       
   286 
       
   287     /**
       
   288      * Prefixes the relative dependency injection container path with the object manager prefix.
       
   289      *
       
   290      * @example $name is 'entity_manager' then the result would be 'doctrine.orm.entity_manager'
       
   291      *
       
   292      * @param string $name
       
   293      * @return string
       
   294      */
       
   295     abstract protected function getObjectManagerElementName($name);
       
   296 
       
   297     /**
       
   298      * Noun that describes the mapped objects such as Entity or Document.
       
   299      *
       
   300      * Will be used for autodetection of persistent objects directory.
       
   301      *
       
   302      * @return string
       
   303      */
       
   304     abstract protected function getMappingObjectDefaultName();
       
   305 
       
   306     /**
       
   307      * Relative path from the bundle root to the directory where mapping files reside.
       
   308      *
       
   309      * @return string
       
   310      */
       
   311     abstract protected function getMappingResourceConfigDirectory();
       
   312 
       
   313     /**
       
   314      * Extension used by the mapping files.
       
   315      *
       
   316      * @return string
       
   317      */
       
   318     abstract protected function getMappingResourceExtension();
       
   319 }