Issues (291)

src/TDBMSchemaAnalyzer.php (2 issues)

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()) {
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Schema\For...nt::getLocalTableName() has been deprecated: Use the table that contains the foreign key as part of its {@see Table::$_fkConstraints} instead. ( Ignorable by Annotation )

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

154
                        if ($fk->getLocalTableName() === /** @scrutinizer ignore-deprecated */ $childFk->getLocalTableName() && $fk->getUnquotedLocalColumns() === $childFk->getUnquotedLocalColumns()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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