1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Nette\Database\Reflection;
13:
14: use Nette;
15:
16:
17:
18: 19: 20: 21: 22: 23:
24: class DiscoveredReflection extends Nette\Object implements Nette\Database\IReflection
25: {
26:
27: protected $cache;
28:
29:
30: protected $cacheStorage;
31:
32:
33: protected $connection;
34:
35:
36: protected $structure = array();
37:
38:
39:
40: 41: 42:
43: public function __construct(Nette\Caching\IStorage $storage = NULL)
44: {
45: $this->cacheStorage = $storage;
46: }
47:
48:
49:
50: public function setConnection(Nette\Database\Connection $connection)
51: {
52: $this->connection = $connection;
53: if ($this->cacheStorage) {
54: $this->cache = new Nette\Caching\Cache($this->cacheStorage, 'Nette.Database.' . md5($connection->getDsn()));
55: $this->structure = $this->cache->load('structure') ?: $this->structure;
56: }
57: }
58:
59:
60:
61: public function __destruct()
62: {
63: if ($this->cache) {
64: $this->cache->save('structure', $this->structure);
65: }
66: }
67:
68:
69:
70: public function getPrimary($table)
71: {
72: $primary = & $this->structure['primary'][strtolower($table)];
73: if (isset($primary)) {
74: return empty($primary) ? NULL : $primary;
75: }
76:
77: $columns = $this->connection->getSupplementalDriver()->getColumns($table);
78: $primaryCount = 0;
79: foreach ($columns as $column) {
80: if ($column['primary']) {
81: $primary = $column['name'];
82: $primaryCount++;
83: }
84: }
85:
86: if ($primaryCount !== 1) {
87: $primary = '';
88: return NULL;
89: }
90:
91: return $primary;
92: }
93:
94:
95:
96: public function getHasManyReference($table, $key, $refresh = TRUE)
97: {
98: $reference = & $this->structure['hasMany'][strtolower($table)];
99: if (!empty($reference)) {
100: $candidates = $columnCandidates = array();
101: foreach ($reference as $targetPair) {
102: list($targetColumn, $targetTable) = $targetPair;
103: if (stripos($targetTable, $key) === FALSE)
104: continue;
105:
106: $candidates[] = array($targetTable, $targetColumn);
107: if (stripos($targetColumn, $table) !== FALSE) {
108: $columnCandidates[] = $candidate = array($targetTable, $targetColumn);
109: if (strtolower($targetTable) === strtolower($key))
110: return $candidate;
111: }
112: }
113:
114: if (count($columnCandidates) === 1) {
115: return reset($columnCandidates);
116: } elseif (count($candidates) === 1) {
117: return reset($candidates);
118: }
119:
120: foreach ($candidates as $candidate) {
121: list($targetTable, $targetColumn) = $candidate;
122: if (strtolower($targetTable) === strtolower($key))
123: return $candidate;
124: }
125:
126: if (!$refresh && !empty($candidates)) {
127: throw new \PDOException('Ambiguous joining column in related call.');
128: }
129: }
130:
131: if (!$refresh) {
132: throw new \PDOException("No reference found for \${$table}->related({$key}).");
133: }
134:
135: $this->reloadAllForeignKeys();
136: return $this->getHasManyReference($table, $key, FALSE);
137: }
138:
139:
140:
141: public function getBelongsToReference($table, $key, $refresh = TRUE)
142: {
143: $reference = & $this->structure['belongsTo'][strtolower($table)];
144: if (!empty($reference)) {
145: foreach ($reference as $column => $targetTable) {
146: if (stripos($column, $key) !== FALSE) {
147: return array(
148: $targetTable,
149: $column,
150: );
151: }
152: }
153: }
154:
155: if (!$refresh) {
156: throw new \PDOException("No reference found for \${$table}->{$key}.");
157: }
158:
159: $this->reloadForeignKeys($table);
160: return $this->getBelongsToReference($table, $key, FALSE);
161: }
162:
163:
164:
165: protected function reloadAllForeignKeys()
166: {
167: foreach ($this->connection->getSupplementalDriver()->getTables() as $table) {
168: if ($table['view'] == FALSE) {
169: $this->reloadForeignKeys($table['name']);
170: }
171: }
172:
173: foreach (array_keys($this->structure['hasMany']) as $table) {
174: uksort($this->structure['hasMany'][$table], function($a, $b) {
175: return strlen($a) - strlen($b);
176: });
177: }
178: }
179:
180:
181:
182: protected function reloadForeignKeys($table)
183: {
184: foreach ($this->connection->getSupplementalDriver()->getForeignKeys($table) as $row) {
185: $this->structure['belongsTo'][strtolower($table)][$row['local']] = $row['table'];
186: $this->structure['hasMany'][strtolower($row['table'])][$row['local'] . $table] = array($row['local'], $table);
187: }
188:
189: if (isset($this->structure['belongsTo'][$table])) {
190: uksort($this->structure['belongsTo'][$table], function($a, $b) {
191: return strlen($a) - strlen($b);
192: });
193: }
194: }
195:
196: }
197: