Completed
Pull Request — master (#9)
by David
07:01
created

TDBMSchemaAnalyzer::getSchema()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 9
nc 3
nop 0
1
<?php
2
3
namespace TheCodingMachine\TDBM;
4
5
use Doctrine\Common\Cache\Cache;
6
use Doctrine\DBAL\Connection;
7
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
8
use Doctrine\DBAL\Schema\Schema;
9
use Doctrine\DBAL\Schema\Table;
10
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
11
12
/**
13
 * This class is used to analyze the schema and return valuable information / hints.
14
 */
15
class TDBMSchemaAnalyzer
16
{
17
    private $connection;
18
19
    /**
20
     * @var Schema
21
     */
22
    private $schema;
23
24
    /**
25
     * @var string
26
     */
27
    private $cachePrefix;
28
29
    /**
30
     * @var Cache
31
     */
32
    private $cache;
33
34
    /**
35
     * @var SchemaAnalyzer
36
     */
37
    private $schemaAnalyzer;
38
39
    /**
40
     * @param Connection     $connection     The DBAL DB connection to use
41
     * @param Cache          $cache          A cache service to be used
42
     * @param SchemaAnalyzer $schemaAnalyzer The schema analyzer that will be used to find shortest paths...
43
     *                                       Will be automatically created if not passed
44
     */
45
    public function __construct(Connection $connection, Cache $cache, SchemaAnalyzer $schemaAnalyzer)
46
    {
47
        $this->connection = $connection;
48
        $this->cache = $cache;
49
        $this->schemaAnalyzer = $schemaAnalyzer;
50
    }
51
52
    /**
53
     * Returns a unique ID for the current connection. Useful for namespacing cache entries in the current connection.
54
     *
55
     * @return string
56
     */
57
    public function getCachePrefix()
58
    {
59
        if ($this->cachePrefix === null) {
60
            $this->cachePrefix = hash('md4', $this->connection->getHost().'-'.$this->connection->getPort().'-'.$this->connection->getDatabase().'-'.$this->connection->getDriver()->getName());
61
        }
62
63
        return $this->cachePrefix;
64
    }
65
66
    /**
67
     * Returns the (cached) schema.
68
     *
69
     * @return Schema
70
     */
71
    public function getSchema()
72
    {
73
        if ($this->schema === null) {
74
            $cacheKey = $this->getCachePrefix().'_schema';
75
            if ($this->cache->contains($cacheKey)) {
76
                $this->schema = $this->cache->fetch($cacheKey);
77
            } else {
78
                $this->schema = $this->connection->getSchemaManager()->createSchema();
79
                $this->cache->save($cacheKey, $this->schema);
80
            }
81
        }
82
83
        return $this->schema;
84
    }
85
86
    /**
87
     * Returns the list of pivot tables linked to table $tableName.
88
     *
89
     * @param string $tableName
90
     *
91
     * @return array|string[]
92
     */
93
    public function getPivotTableLinkedToTable($tableName)
94
    {
95
        $cacheKey = $this->getCachePrefix().'_pivottables_link_'.$tableName;
96
        if ($this->cache->contains($cacheKey)) {
97
            return $this->cache->fetch($cacheKey);
98
        }
99
100
        $pivotTables = [];
101
102
        $junctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
103
        foreach ($junctionTables as $table) {
104
            $fks = $table->getForeignKeys();
105
            foreach ($fks as $fk) {
106
                if ($fk->getForeignTableName() == $tableName) {
107
                    $pivotTables[] = $table->getName();
108
                    break;
109
                }
110
            }
111
        }
112
113
        $this->cache->save($cacheKey, $pivotTables);
114
115
        return $pivotTables;
116
    }
117
118
    /**
119
     * Returns the list of foreign keys pointing to the table represented by this bean, excluding foreign keys
120
     * from junction tables and from inheritance.
121
     * It will also suppress doubles if 2 foreign keys are using the same columns.
122
     *
123
     * @return ForeignKeyConstraint[]
124
     */
125
    public function getIncomingForeignKeys($tableName): array
126
    {
127
        $junctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
128
        $junctionTableNames = array_map(function (Table $table) {
129
            return $table->getName();
130
        }, $junctionTables);
131
        $childrenRelationships = $this->schemaAnalyzer->getChildrenRelationships($tableName);
132
133
        $fks = [];
134
        foreach ($this->getSchema()->getTables() as $table) {
135
            $uniqueForeignKeys = $this->removeDuplicates($table->getForeignKeys());
136
            foreach ($uniqueForeignKeys as $fk) {
137
                if ($fk->getForeignTableName() === $tableName) {
138
                    if (in_array($fk->getLocalTableName(), $junctionTableNames)) {
139
                        continue;
140
                    }
141
                    foreach ($childrenRelationships as $childFk) {
142
                        if ($fk->getLocalTableName() === $childFk->getLocalTableName() && $fk->getLocalColumns() === $childFk->getLocalColumns()) {
143
                            continue 2;
144
                        }
145
                    }
146
                    $fks[] = $fk;
147
                }
148
            }
149
        }
150
151
        return $fks;
152
    }
153
154
    /**
155
     * Remove duplicate foreign keys (assumes that all foreign yes are from the same local table)
156
     *
157
     * @param ForeignKeyConstraint[] $foreignKeys
158
     * @return ForeignKeyConstraint[]
159
     */
160
    private function removeDuplicates(array $foreignKeys): array
161
    {
162
        $fks = [];
163
        foreach ($foreignKeys as $foreignKey) {
164
            $key = implode('__`__', $foreignKey->getLocalColumns());
165
            if (!isset($fks[$key])) {
166
                $fks[$key] = $foreignKey;
167
            }
168
        }
169
170
        return array_values($fks);
171
    }
172
}
173