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