在Doctrine2中以编程方式修改表的模式名称?

时间:2013-06-16 15:30:57

标签: php symfony doctrine-orm

我想在运行时as is possible with the table name修改表的架构/数据库名称,但ClassMetadataInfo类似乎没有公开接口来获取/设置此属性。

如果绝对必要,我可以在运行时修改表名,但由于我们必须存储在单个模式/数据库中的表数量,它不是一个理想的解决方案。

有没有办法实现我想做的事情?提前谢谢。

注意:我需要能够在基于注释的实体映射中使用模式占位符来提供完全限定的表名(如__schema_placeholder__.table_name,用于跨数据库连接)。在运行时,我想从__schema_placeholder__.table_name =>动态重新映射实体。 real_schema_name.table_name

3 个答案:

答案 0 :(得分:26)

您可以通过与侦听器/订阅者挂钩doctrine event-system来动态调整表名(和映射)。

即。 “loadClassMetadata”是doctrine's events之一,您可以根据食谱文章How to Register Event Listeners and Subscribers中的描述创建一个侦听器/订阅者。

实施例

config.yml

services:
    mapping.listener:
        class: Acme\YourBundle\EventListener\MappingListener
        tags:
            - { name: doctrine.event_listener, event: loadClassMetadata }

MappingListener

use Doctrine\ORM\Event\LoadClassMetadataEventArgs;

class MappingListener
{
    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
    {
        $classMetadata = $eventArgs->getClassMetadata();
        $table = $classMetadata->table;

        $oldName = $table['name'];      // ... or $classMetaData->getTableName()

        // your logic here ...

        $table['name'] = 'new_table_name';

        $classMetadata->setPrimaryTable($table);

        // ... or add a field-mapping like this

        $fieldMapping = array(
          'fieldName' => 'about',
          'type' => 'string',
           'length' => 255
        );
        $classMetadata->mapField($fieldMapping);

ClassMetadata扩展ClassMetadataInfo并提供一个公共变量“table”(包含您的注释或yml提供的映射信息),您可以修改它们!

public table变量是一个包含以下条目的数组:

  • name =>
  • 架构=>
  • indices =>阵列
  • uniqueConstraints =>阵列

您可以在保存/更新之前在控制器中动态注册事件监听器/订阅者。

$mappingListener = new MappingListener();

// ... maybe even modify the listener using reflection

$evm = $this->get('doctrine')->getManager()->->getEventManager();
$evm->addEventListener('loadClassMetadata', $mappingListener);

此外,您可以引入多个数据库连接/名称,并在您的应用程序中访问它们。

应用/配置/ config.yml

doctrine:
    dbal:
        default_connection:   default
        connections:
            default:
                driver:   "%database_driver%"
                host:     "%database_host%"
                port:     "%database_port%"
                dbname:   "%database_name%"
                user:     "%database_user%"
                password: "%database_password%"
                charset:  UTF8
            customer:
                driver:   "%database_driver2%"
                host:     "%database_host2%"
                port:     "%database_port2%"
                dbname:   "%database_name2%"
                user:     "%database_user2%"
                password: "%database_password2%"
                charset:  UTF8

然后让不同的实体经理使用......

  $em = $this->get('doctrine')->getManager('default');
  $em2 = $this->get('doctrine')->getManager('customer');

或存储库

$customers = $this->get('doctrine')
    ->getRepository('AcmeCustomerBundle:Customer', 'customer')
    ->findAll()
;

...或动态add connections

$this->get('doctrine')
  ->connection('mysql://username:password@localhost/test', 'dynamic_connection');

在食谱章节How to work with Multiple Entity Managers and Connections中阅读有关该主题的更多信息。

答案 1 :(得分:1)

@nifr的回答是要走的路。我认为必须注意,你需要清除你的学说缓存才能触发loadClassMetaData!

$ app/console doctrine:cache:clear-metadata --env=test

答案 2 :(得分:0)

您可以使用@Table中未记录的'options'参数,并将变量传递给侦听器,您可以使用该变量来决定使用哪个数据库。

'options'用于在生成模式时在SQL中包括DBMS特定的选项。例如:“ charset” =“ utf8mb4”,“ engine” =“ InnoDB”等

选择变量名时,请确保它不是DBMS支持的有效选项。在下面的示例中,我选择了“模式”:

@ORM\Table(name="person", options={"schema"="readonly"})

此后,我在services.yml中创建了一个名为“ schema”的参数数组,该参数数组将通过ParameterBag传递给服务。这些值可以在yml中进行编辑,也可以从环境变量中提取。

parameters:
    env(SCHEMA_READONLY): 'readonly_database' #default if env variable has not been set
    schema:
        readonly: 'readonly_database'
        client: 'client_database'
        runtime: '%env(SCHEMA_READONLY)%' #pulled from env variables

现在我的听众看起来像这样:

class MappingListener
{
    protected $parameterBag;

    public function __construct(ParameterBagInterface $parameterBag) {
        $this->parameterBag = $parameterBag;
    }

    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs) {
        $schemaParamaters = $this->parameterBag->has('schema') ? $this->parameterBag->get('schema') : array();

        $classMetadata = $eventArgs->getClassMetadata();

        if(isset($classMetadata->table['options']['schema']) && isset($schemaParamaters[$classMetadata->table['options']['schema']])) {
            $classMetadata->setPrimaryTable(['schema' => $schemaParamaters[$classMetadata->table['options']['schema']]]);
        }
    } 
}

这将导致所有带有表选项'schema'的实体都将被设置为使用service.yml中的参数或环境变量中定义的数据库。

这意味着您可以选择设置模式或将其保留为默认模式,而无需编辑实体或任何其他类。