seikai
2/17/2013 - 10:36 AM

DoctrineOrmServiceProvider

DoctrineOrmServiceProvider

<?php

use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
use Doctrine\DBAL\Types\Type;
use Doctrine\Common\Cache\ArrayCache;
use Doctrine\Common\Cache\MemcacheCache;
use Doctrine\Common\Cache\ApcCache;
use Doctrine\Common\Cache\RedisCache;
use Doctrine\Common\Cache\XcacheCache;
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain;
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Mapping\Driver\YamlDriver;
use Doctrine\ORM\Mapping\Driver\XmlDriver;
use Doctrine\Common\Annotations\SimpleAnnotationReader;
use Doctrine\Common\Annotations\CachedReader;

use \Silex\Application;
use \Silex\ServiceProviderInterface;

class DoctrineOrmServiceProvider implements ServiceProviderInterface
{

    public function register(Application $app)
    {
        if (!$app['db'] instanceof \Doctrine\DBAL\Connection) {
            throw new \InvalidArgumentException('$app[\'db\'] must be an instance of \Doctrine\DBAL\Connection');
        }

        $app['db.orm.em'] = $app->share(function() use($app) {
            return EntityManagerUtils::getEntityManager($app['db'], $app['db.orm.options']);
        });
    }

    public function boot(Application $app) { }

}


class EntityManagerUtils {

    const CACHE_TYPE_APC = 'apc';

    const CACHE_TYPE_XCACHE = 'xcache';

    const CACHE_TYPE_MEMCACHE = 'memcache';

    const CACHE_TYPE_REDIS = 'redis';

    const CACHE_TYPE_ARRAY = 'array';


    /**
     * @param mixed $dbal
     * @param $ormOptions
     * @return \Doctrine\ORM\EntityManager
     */
    public static function getEntityManager($dbal, $ormOptions)
    {
        $config = self::getOrmConfiguration($ormOptions);

        $em = EntityManager::create($dbal, $config);

        $platform = $em->getConnection()->getDatabasePlatform();

        if(array_key_exists('additional_mapping_type', $ormOptions)) {
            foreach($ormOptions['additional_mapping_type'] as $type => $cast) {
                $platform->registerDoctrineTypeMapping($type, $cast);
            }
        }

        return $em;
    }

    /**
     * TODO キャッシュの設定は専用に設定値を持たせる
     * 
     * @param $type
     * @return \Doctrine\Common\Cache\Cache
     */
    private static function getCache($type)
    {

        $cache = null;

        $type = strtolower($type);

        if ($type === self::CACHE_TYPE_APC && extension_loaded('apc')) {
            $cache = new \Doctrine\Common\Cache\ApcCache();
        } else if ($type === self::CACHE_TYPE_XCACHE && extension_loaded('xcache')) {
            $cache = new \Doctrine\Common\Cache\XcacheCache();
        } else if ($type === self::CACHE_TYPE_MEMCACHE && extension_loaded('memcache')) {
            $memcache = new \Memcache();
            $memcache->connect('127.0.0.1');
            $cache = new \Doctrine\Common\Cache\MemcacheCache();
            $cache->setMemcache($memcache);
        } else if ($type === self::CACHE_TYPE_REDIS && extension_loaded('redis')) {
            $redis = new \Redis();
            $redis->connect('127.0.0.1');
            $cache = new \Doctrine\Common\Cache\RedisCache();
            $cache->setRedis($redis);
        } else {
            $cache = new ArrayCache();
        }

        return $cache;
    }


    /**
     * @param $type
     * @param $path
     * @return \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver
     * @throws \InvalidArgumentException
     */
    private static function getDriver($type, array $path)
    {
        switch ($type) {
            case 'default':
            case 'annotation':
                $reader = new SimpleAnnotationReader();
                $reader->addNamespace('Doctrine\ORM\Mapping');
                $cachedReader = new CachedReader($reader, new ArrayCache());
                $driver = new AnnotationDriver($cachedReader, $path);
                break;
            case 'yml':
                $driver = new YamlDriver($path);
                break;
            case 'xml':
                $driver = new XmlDriver($path);
                break;
            default:
                throw new \InvalidArgumentException(sprintf('"%s" is not a recognized driver', $type));
                break;
        }
        return $driver;
    }

    /**
     * @param $ormOptions
     * @return \Doctrine\ORM\Configuration
     */
    private static function getOrmConfiguration($ormOptions)
    {

        $cache = self::getCache($ormOptions['cache']);

        $config = new \Doctrine\ORM\Configuration();
        $config->setMetadataCacheImpl($cache);
        $config->setQueryCacheImpl($cache);

        $chain = new MappingDriverChain;
        foreach ((array)$ormOptions['entities'] as $entity) {
            $driver = self::getDriver($entity['type'], (array)$entity['path']);
            $chain->addDriver($driver, $entity['namespace']);
        }
        $config->setMetadataDriverImpl($chain);
        
        if(array_key_exists('custom_types', $ormOptions)) {
            foreach($ormOptions['custom_types'] as $type => $class) {
                Type::addType($type, $class);
            }
        }
        //Numericオンリーではまずいのでこのあたりは後ほど検討
        if(array_key_exists('custom_functions', $ormOptions)) {
            foreach($ormOptions['custom_functions'] as $funcName => $class) {
                $config->addCustomNumericFunction($funcName, $class);
            }
        }

        $config->setProxyDir($ormOptions['proxies_dir']);
        $config->setProxyNamespace($ormOptions['proxies_namespace']);
        $config->setAutoGenerateProxyClasses($ormOptions['auto_generate_proxies']);

        return $config;
    }

}
---
all:
  dbal:
    wrapperClass: Doctrine\DBAL\Connections\MasterSlaveConnection
    driver:        pdo_mysql
  orm:
    proxies_dir: /path/to/project/tmp/doctrine/proxy
    proxies_namespace: DoctrineProxies
    cache: null
    auto_generate_proxies: true
    entities:
      - type: yml
        path: /path/to/project/config/doctrine/meta
        namespace: Hoge\ORM\Entity
    custom_types:
      point: Hoge\Doctrine\Type\PointType
    custom_functions:
      GEOMFROMTEXT: Hoge\Doctrine\Type\GeomFromText
      DISTANCE:     Hoge\Doctrine\Type\Distance
      YEAR:         DoctrineExtensions\Query\Mysql\Year
      MONTH:        DoctrineExtensions\Query\Mysql\Month
      DAY:          DoctrineExtensions\Query\Mysql\Day
    additional_mapping_type:
      point: point
      geometry: point
      enum: string
prod:
  dbal:
    master:
      user:      username
      password:  password
      dbname:    dbname
      host:      127.0.0.1
    slaves:
      - user:      username
        password:  password
        host:      127.0.0.1
        dbname:    test
      - user:      username
        password:  password
        host:      127.0.0.1
        dbname:    dbname
<?php

$app = new Application();

$app->register(new \Silex\Provider\DoctrineServiceProvider(), array(
    'db.options' => Config::get('dbal')
));

$app->register(new \DoctrineOrmServiceProvider(), array(
    'db.orm.options' => Config::get('orm')
));