Passed
Push — trunk ( 97fbe0...f153cd )
by Christian
28:35 queued 16:34
created

CustomEntitySchemaUpdater::cleanup()   B

Complexity

Conditions 7
Paths 11

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 10
nc 11
nop 1
dl 0
loc 18
rs 8.8333
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace Shopware\Core\System\CustomEntity\Schema;
4
5
use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\Exception;
7
use Doctrine\DBAL\Platforms\AbstractPlatform;
8
use Doctrine\DBAL\Schema\Comparator;
0 ignored issues
show
Bug introduced by
The type Doctrine\DBAL\Schema\Comparator was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use Doctrine\DBAL\Schema\Schema;
10
use Shopware\Core\Framework\Log\Package;
11
use Symfony\Component\Lock\LockFactory;
12
13
/**
14
 * @internal
15
 *
16
 * @phpstan-import-type CustomEntityField from SchemaUpdater
17
 */
18
#[Package('core')]
19
class CustomEntitySchemaUpdater
20
{
21
    private const COMMENT = 'custom-entity-element';
22
23
    public function __construct(
24
        private readonly Connection $connection,
25
        private readonly LockFactory $lockFactory,
26
        private readonly SchemaUpdater $schemaUpdater
27
    ) {
28
    }
29
30
    public function update(): void
31
    {
32
        $this->lock(function (): void {
33
            /** @var list<array{name: string, fields: string}> $tables */
34
            $tables = $this->connection->fetchAllAssociative('SELECT name, fields FROM custom_entity');
35
36
            $schema = $this->connection->createSchemaManager()->introspectSchema();
37
38
            $this->cleanup($schema);
39
40
            $this->schemaUpdater->applyCustomEntities($schema, $tables);
0 ignored issues
show
Bug introduced by
$tables of type Shopware\Core\System\CustomEntity\Schema\list is incompatible with the type array expected by parameter $customEntities of Shopware\Core\System\Cus...::applyCustomEntities(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

40
            $this->schemaUpdater->applyCustomEntities($schema, /** @scrutinizer ignore-type */ $tables);
Loading history...
41
42
            $this->applyNewSchema($schema);
43
        });
44
    }
45
46
    private function lock(\Closure $closure): void
47
    {
48
        $lock = $this->lockFactory->createLock('custom-entity::schema-update', 30);
49
50
        if ($lock->acquire(true)) {
51
            $closure();
52
53
            $lock->release();
54
        }
55
    }
56
57
    private function applyNewSchema(Schema $update): void
58
    {
59
        $baseSchema = $this->connection->createSchemaManager()->introspectSchema();
60
        $queries = $this->getPlatform()->getAlterSchemaSQL((new Comparator())->compareSchemas($baseSchema, $update));
61
62
        foreach ($queries as $query) {
63
            try {
64
                $this->connection->executeStatement($query);
65
            } catch (Exception $e) {
66
                // there seems to be a timing issue in sql when dropping a foreign key which relates to an index.
67
                // Sometimes the index exists no more when doctrine tries to drop it after dropping the foreign key.
68
                if (!\str_contains($e->getMessage(), 'An exception occurred while executing \'DROP INDEX IDX_')) {
69
                    throw $e;
70
                }
71
            }
72
        }
73
    }
74
75
    private function getPlatform(): AbstractPlatform
76
    {
77
        $platform = $this->connection->getDatabasePlatform();
78
        if (!$platform instanceof AbstractPlatform) {
0 ignored issues
show
introduced by
$platform is always a sub-type of Doctrine\DBAL\Platforms\AbstractPlatform.
Loading history...
79
            throw new \RuntimeException('Database platform can not be detected');
80
        }
81
82
        return $platform;
83
    }
84
85
    private function cleanup(Schema $schema): void
86
    {
87
        foreach ($schema->getTables() as $table) {
88
            if ($table->getComment() === self::COMMENT) {
89
                $schema->dropTable($table->getName());
90
91
                continue;
92
            }
93
94
            foreach ($table->getForeignKeys() as $foreignKey) {
95
                if (\str_starts_with($foreignKey->getName(), 'fk_ce_')) {
96
                    $table->removeForeignKey($foreignKey->getName());
97
                }
98
            }
99
100
            foreach ($table->getColumns() as $column) {
101
                if ($column->getComment() === self::COMMENT) {
102
                    $table->dropColumn($column->getName());
103
                }
104
            }
105
        }
106
    }
107
}
108