Issues (291)

src/TDBMSchemaAnalyzer.php (1 issue)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace TheCodingMachine\TDBM;
6
7
use Doctrine\Common\Cache\Cache;
8
use Doctrine\DBAL\Connection;
9
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
10
use Doctrine\DBAL\Schema\Schema;
11
use Doctrine\DBAL\Schema\Table;
12
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
13
14
use function hash;
15
use function serialize;
16
17
/**
18
 * This class is used to analyze the schema and return valuable information / hints.
19
 */
20
class TDBMSchemaAnalyzer
21
{
22
    private $connection;
23
24
    /**
25
     * @var Schema
26
     */
27
    private $schema;
28
29
    /**
30
     * @var string
31
     */
32
    private $cachePrefix;
33
34
    /**
35
     * @var Cache
36
     */
37
    private $cache;
38
39
    /**
40
     * @var SchemaAnalyzer
41
     */
42
    private $schemaAnalyzer;
43
44
    /**
45
     * @var SchemaLockFileDumper
46
     */
47
    private $schemaLockFileDumper;
48
49
    /**
50
     * @param Connection $connection The DBAL DB connection to use
51
     * @param Cache $cache A cache service to be used
52
     * @param SchemaAnalyzer $schemaAnalyzer The schema analyzer that will be used to find shortest paths...
53
     *                                       Will be automatically created if not passed
54
     */
55
    public function __construct(Connection $connection, Cache $cache, SchemaAnalyzer $schemaAnalyzer, SchemaLockFileDumper $schemaLockFileDumper)
56
    {
57
        $this->connection = $connection;
58
        $this->cache = $cache;
59
        $this->schemaAnalyzer = $schemaAnalyzer;
60
        $this->schemaLockFileDumper = $schemaLockFileDumper;
61
    }
62
63
    /**
64
     * Returns a unique ID for the current connection. Useful for namespacing cache entries in the current connection.
65
     *
66
     * @return string
67
     */
68
    public function getCachePrefix(): string
69
    {
70
        return $this->cachePrefix ??= hash('md4', serialize($this->connection->getParams()));
71
    }
72
73
    /**
74
     * @deprecated
75
     */
76
    public function getLockFilePath(): string
77
    {
78
        return $this->schemaLockFileDumper->getLockFilePath();
79
    }
80
81
    /**
82
     * @deprecated
83
     */
84
    public function getSchema(bool $ignoreCache = false): Schema
85
    {
86
        return $this->schemaLockFileDumper->getSchema($ignoreCache);
87
    }
88
89
    /**
90
     * @deprecated
91
     */
92
    public function generateLockFile(): void
93
    {
94
        $this->schemaLockFileDumper->generateLockFile();
95
        \chmod($this->getLockFilePath(), 0664);
0 ignored issues
show
Deprecated Code introduced by
The function TheCodingMachine\TDBM\TD...yzer::getLockFilePath() has been deprecated. ( Ignorable by Annotation )

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

95
        \chmod(/** @scrutinizer ignore-deprecated */ $this->getLockFilePath(), 0664);
Loading history...
96
    }
97
98
    /**
99
     * Returns the list of pivot tables linked to table $tableName.
100
     *
101
     * @param string $tableName
102
     *
103
     * @return string[]
104
     */
105
    public function getPivotTableLinkedToTable(string $tableName): array
106
    {
107
        $cacheKey = $this->getCachePrefix().'_pivottables_link_'.$tableName;
108
        if ($this->cache->contains($cacheKey)) {
109
            return $this->cache->fetch($cacheKey);
110
        }
111
112
        $pivotTables = [];
113
114
        $junctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
115
        foreach ($junctionTables as $table) {
116
            $fks = $table->getForeignKeys();
117
            foreach ($fks as $fk) {
118
                if ($fk->getForeignTableName() == $tableName) {
119
                    $pivotTables[] = $table->getName();
120
                    break;
121
                }
122
            }
123
        }
124
125
        $this->cache->save($cacheKey, $pivotTables);
126
127
        return $pivotTables;
128
    }
129
130
    /**
131
     * Returns the list of foreign keys pointing to the table represented by this bean, excluding foreign keys
132
     * from junction tables and from inheritance.
133
     * It will also suppress doubles if 2 foreign keys are using the same columns.
134
     *
135
     * @return ForeignKeyConstraint[]
136
     */
137
    public function getIncomingForeignKeys(string $tableName): array
138
    {
139
        $junctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
140
        $junctionTableNames = array_map(function (Table $table) {
141
            return $table->getName();
142
        }, $junctionTables);
143
        $childrenRelationships = $this->schemaAnalyzer->getChildrenRelationships($tableName);
144
145
        $fks = [];
146
        foreach ($this->schemaLockFileDumper->getSchema()->getTables() as $table) {
147
            $uniqueForeignKeys = $this->removeDuplicates($table->getForeignKeys());
148
            foreach ($uniqueForeignKeys as $fk) {
149
                if ($fk->getForeignTableName() === $tableName) {
150
                    if (in_array($fk->getLocalTableName(), $junctionTableNames)) {
151
                        continue;
152
                    }
153
                    foreach ($childrenRelationships as $childFk) {
154
                        if ($fk->getLocalTableName() === $childFk->getLocalTableName() && $fk->getUnquotedLocalColumns() === $childFk->getUnquotedLocalColumns()) {
155
                            continue 2;
156
                        }
157
                    }
158
                    $fks[] = $fk;
159
                }
160
            }
161
        }
162
163
        return $fks;
164
    }
165
166
    /**
167
     * Remove duplicate foreign keys (assumes that all foreign yes are from the same local table)
168
     *
169
     * @param ForeignKeyConstraint[] $foreignKeys
170
     * @return ForeignKeyConstraint[]
171
     */
172
    private function removeDuplicates(array $foreignKeys): array
173
    {
174
        $fks = [];
175
        foreach ($foreignKeys as $foreignKey) {
176
            $key = implode('__`__', $foreignKey->getUnquotedLocalColumns());
177
            if (!isset($fks[$key])) {
178
                $fks[$key] = $foreignKey;
179
            }
180
        }
181
182
        return array_values($fks);
183
    }
184
}
185