ChrisR
ChrisR

Reputation: 14447

Ignore a Doctrine2 Entity when running schema-manager update

I've got a Doctrine Entity defined that maps to a View in my database. All works fine, the Entity relations work fine as expected.

Problem now is that when running orm:schema-manager:update on the CLI a table gets created for this entity which is something I want to prevent. There already is a view for this Entity, no need to create a table for it.

Can I annotate the Entity so that a table won't be created while still keeping access to all Entity related functionality (associations, ...)?

Upvotes: 16

Views: 11308

Answers (6)

Carles
Carles

Reputation: 368

In Doctrine 2.7.0 it was introduced the new SchemaIgnoreClasses entity manager config option that basically ignores the configured classes from any schema action.

To use it with Symfony we only need to add the schema_ignore_classes key in the Doctrine entity manager configuration like this:

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

Upvotes: 10

Danilo Colasso
Danilo Colasso

Reputation: 2381

$schema->getTableNames() was not working (I don't know why).

So:

<?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());
            }
        }
    }
}

And Register a service

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

Worked fine! ;)

Upvotes: 0

ChrisR
ChrisR

Reputation: 14447

Eventually it was fairly simple, I just had to subclass the \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand into my own CLI Command. In that subclass filter the $metadatas array that's being passed to executeSchemaCommand() and then pass it on to the parent function.

Just attach this new subclassed command to the ConsoleApplication you are using in your doctrine cli script and done!

Below is the extended command, in production you'll probably want to fetch the $ignoredEntities property from you config or something, this should put you on the way.

<?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: credits go to Marco Pivetta for putting me on the right track. https://groups.google.com/forum/?fromgroups=#!topic/doctrine-user/rwWXZ7faPsA

Upvotes: 8

Based on the original alswer of ChrisR inspired in Marco Pivetta's post I'm adding here the solution if you're using Symfony2:

Looks like Symfony2 doesn't use the original Doctrine command at: \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand

Instead it uses the one in the bundle: \Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand

So basically that is the class that must be extended, ending up in having:

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);
    }
}

Upvotes: 16

Kamil Adryjanek
Kamil Adryjanek

Reputation: 3338

Quite old one but there is also worth nothing solution using Doctrine2: postGenerateSchema event listener - for me it's better than overriding Doctrine classes:

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);
            }

        }
    }
}

Also register listener:

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

No extra hooks is necessary.

Upvotes: 7

Igor
Igor

Reputation: 1

If problem is only with producing errors in db_view, when calling doctrine:schema:update command, why not simplest way:

  1. remove @ from @ORM\Entity annotation
  2. execute doctrine:schema:update
  3. add @ to ORM\Entity annotation

;-)

Upvotes: -3

Related Questions