当运行schema-manager更新时忽略Doctrine2实体

13

我定义了一个Doctrine实体,用于映射到我的数据库中的视图。一切正常,实体关系按预期工作。

现在的问题是,当在CLI上运行orm:schema-manager:update时,将为此实体创建一个表,这是我想要避免的。已经有一个视图用于这个实体,没有必要再创建一个表。

我能否注释实体,以便不创建表,同时仍然保持对所有实体相关功能(关联等)的访问权?


看起来这个问题在Doctrine邮件列表上出现了一段时间,但一直没有得到答复。https://groups.google.com/forum/?fromgroups=#!topic/doctrine-user/qrlvGx7HxdQ - ChrisR
6个回答

16

基于ChrisR的原始答案,受Marco Pivetta的帖子启发,如果您正在使用Symfony2,则在此处添加解决方案:

看起来Symfony2没有使用原始Doctrine命令: \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand

相反,它使用了bundle中的命令: \Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand

因此,基本上必须扩展该类,最终得到:

src/Acme/CoreBundle/Command/DoctrineUpdateCommand.php

<?php

namespace App\Command;

use Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Tools\SchemaTool;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class DoctrineUpdateCommand extends UpdateSchemaDoctrineCommand
{
    protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): ?int
    {
        $ignoredEntities = [
            'App\Entity\EntityToIgnore',
        ];
        $metadatas = array_filter($metadatas, static function (ClassMetadata $classMetadata) use ($ignoredEntities) {
            return !in_array($classMetadata->getName(), $ignoredEntities, true);
        });
        return parent::executeSchemaCommand($input, $output, $schemaTool, $metadatas, $ui);
    }
}

1
你可以使用 return parent::executeSchemaCommand($input, $output, $schemaTool, $newMetadatas); 来取得更好的效果。 - Jugali Lakota

8
最终,这很简单,我只需要将\Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand子类化为自己的CLI命令。在该子类中过滤传递给executeSchemaCommand()$metadatas数组,然后将其传递给父函数。

只需将此新的子类命令附加到您在Doctrine CLI脚本中使用的ConsoleApplication上,就完成了!

下面是扩展命令,在生产环境中,您可能希望从配置文件或其他地方获取$ignoredEntities属性,这应该能帮助您。

<?php

use Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand;
use Doctrine\ORM\Tools\SchemaTool;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class My_Doctrine_Tools_UpdateCommand extends UpdateCommand
{
    protected $name = 'orm:schema-tool:myupdate';

    protected $ignoredEntities = array(
        'Entity\Asset\Name'
    );

    protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui)
    {
        /** @var $metadata \Doctrine\ORM\Mapping\ClassMetadata */
        $newMetadata = [];
        foreach ($metadatas as $metadata) {
            if (!in_array($metadata->getName(), $this->ignoredEntities)) {
                $newMetadata[] = $metadata;
            }
        }
        return parent::executeSchemaCommand($input, $output, $schemaTool, $newMetadata, $ui);
    }
}

PS:感谢Marco Pivetta让我走上了正确的道路。https://groups.google.com/forum/?fromgroups=#!topic/doctrine-user/rwWXZ7faPsA


2
此解决方案可以防止将外键指向被忽略的实体。 - Sylvain
谢谢Sylvain,我还没有遇到过这个问题,但是知道这点很好! - ChrisR
能提供一个在Symfony2中实现它的好例子就太好了,我已经将该命令添加到我的CoreBundle命令中,但运行时出现了一些错误:“助手“em”未定义。” - 我想我错过了将该命令添加到“您在Doctrine CLI脚本中使用的ConsoleApplication”的部分 - 不知道如何在Symfony2中实现。 - Alexandru Trandafir Catalin
1
你可以使用 return parent::executeSchemaCommand($input, $output, $schemaTool, $newMetadatas); 来取得更好的效果。 - Jugali Lakota
我找到了适用于我的情况的解决方案,如果我将实体的映射设置为MappedSuperClass,则在更新工具栏/验证时它将被忽略。 - Kart Av1k

7

虽然比较老,但也有一种值得注意的解决方案,使用 Doctrine2postGenerateSchema 事件监听器 - 对我来说,这比重写 Doctrine 类更好:

namespace App\Doctrine\Listener;

use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;

/**
 * IgnoreTablesListener class
 */
class IgnoreTablesListener
{
    private $ignoredTables = [
        'table_name_to_ignore',
    ];

    public function postGenerateSchema(GenerateSchemaEventArgs $args)
    {
        $schema = $args->getSchema();
        $tableNames = $schema->getTableNames();
        foreach ($tableNames as $tableName) {
            if (in_array($tableName, $this->ignoredTables)) {
                // remove table from schema
                $schema->dropTable($tableName);
            }

        }
    }
}

同时注册监听器:

# config/services.yaml
services:
    ignore_tables_listener:
        class: App\Doctrine\Listener\IgnoreTablesListener
        tags:
            - {name: doctrine.event_listener, event: postGenerateSchema }

不需要额外的钩子。

这里的缺点是,模式被生成后,您删除一个表格,对我来说似乎不是最优解,因为我更喜欢根本不创建表格。但最终结果是相同的,所以对于某些人来说,这可能已经足够了。 - ChrisR
2
是的,但只创建(生成)了Schema实例 - 没有执行。在这种情况下,dropTable表示从Schema实例中取消设置表。如果您调用:d:s:u --dump-sql,则会收到已生成模式SQL的忽略表。 - Kamil Adryjanek
2
如果您的模式中没有错误,那么这个解决方案会更好。它允许在忽略的内容上指向外键。但是我需要忽略一些特定实体的原因是有一些特定错误,所以我想跳过它们。PS:我看到了您的博客@KamilAdryjanek,您应该删除实体管理器的依赖调用。它会产生循环引用错误。相反,$args参数已经有了getEntityManager方法。 - Jugali Lakota
我尝试了所有的解决方案,但它仍然生成实体文件并抛出错误“未为实体指定标识符/主键”,这意味着它没有跳过实体。请提出其他建议。 - aniruddha

5
在Doctrine 2.7.0中,引入了新的SchemaIgnoreClasses实体管理器配置选项,该选项基本上会忽略任何模式操作中配置的类。
要与Symfony一起使用它,我们只需要在Doctrine实体管理器配置中添加schema_ignore_classes键,像这样:
doctrine:
    dbal:
        # your dbal configuration
    orm:
        default_entity_manager: default
        entity_managers:
            default:
                connection: default
                mappings:
                    Main:
                        is_bundle: false
                        type: annotation
                        dir: '%kernel.project_dir%/src/Entity/Main'
                        prefix: 'App\Entity\Main'
                        alias: Main
                schema_ignore_classes:
                    - Reference\To\My\Class
                    - Reference\To\My\OtherClass


0

$schema->getTableNames() 没有起作用(我不知道为什么)。

所以:

<?php

namespace AppBundle\EventListener;

use Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;

class IgnoreTablesListener extends UpdateSchemaDoctrineCommand
{

    private $ignoredEntities = [
        'YourBundle\Entity\EntityYouWantToIgnore',
    ];

    /**
     * Remove ignored tables /entities from Schema
     *
     * @param GenerateSchemaEventArgs $args
     */
    public function postGenerateSchema(GenerateSchemaEventArgs $args)
    {
        $schema = $args->getSchema();
        $em = $args->getEntityManager();


        $ignoredTables = [];
        foreach ($this->ignoredEntities as $entityName) {
            $ignoredTables[] = $em->getClassMetadata($entityName)->getTableName();
        }


        foreach ($schema->getTables() as $table) {

            if (in_array($table->getName(), $ignoredTables, true)) {
                // remove table from schema
                $schema->dropTable($table->getName());
            }
        }
    }
}

并注册一个服务

# config/services.yaml
services:
    ignore_tables_listener:
        class: AppBundle\EventListener\IgnoreTablesListener
        tags:
            - {name: doctrine.event_listener, event: postGenerateSchema }

工作得很好! ;)


-3
如果问题只出现在调用doctrine:schema:update命令时,在db_view中产生错误,为什么不采用最简单的方法呢:
  1. 从@ORM\Entity注释中删除@
  2. 执行doctrine:schema:update
  3. 将@添加到ORM\Entity注释中
;-)

1
非常糟糕的想法。如果您正在处理预生产或生产环境,该如何进行?通常情况下,您无法访问代码。 - nboulfroy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接