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: if (isset($this->structure['hasMany'][strtolower($table)])) {
102: $candidates = $columnCandidates = array();
103: foreach ($this->structure['hasMany'][strtolower($table)] as $targetPair) {
104: list($targetColumn, $targetTable) = $targetPair;
105: if (stripos($targetTable, $key) === FALSE) {
106: continue;
107: }
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:
118: if (count($columnCandidates) === 1) {
119: return reset($columnCandidates);
120: } elseif (count($candidates) === 1) {
121: return reset($candidates);
122: }
123:
124: foreach ($candidates as $candidate) {
125: list($targetTable, $targetColumn) = $candidate;
126: if (strtolower($targetTable) === strtolower($key)) {
127: return $candidate;
128: }
129: }
130: }
131:
132: if ($refresh) {
133: $this->reloadAllForeignKeys();
134: return $this->getHasManyReference($table, $key, FALSE);
135: }
136:
137: if (empty($candidates)) {
138: throw new MissingReferenceException("No reference found for \${$table}->related({$key}).");
139: } else {
140: throw new AmbiguousReferenceKeyException('Ambiguous joining column in related call.');
141: }
142: }
143:
144:
145:
146: public function getBelongsToReference($table, $key, $refresh = TRUE)
147: {
148: if (isset($this->structure['belongsTo'][strtolower($table)])) {
149: foreach ($this->structure['belongsTo'][strtolower($table)] as $column => $targetTable) {
150: if (stripos($column, $key) !== FALSE) {
151: return array($targetTable, $column);
152: }
153: }
154: }
155:
156: if ($refresh) {
157: $this->reloadForeignKeys($table);
158: return $this->getBelongsToReference($table, $key, FALSE);
159: }
160:
161: throw new MissingReferenceException("No reference found for \${$table}->{$key}.");
162: }
163:
164:
165:
166: protected function reloadAllForeignKeys()
167: {
168: foreach ($this->connection->getSupplementalDriver()->getTables() as $table) {
169: if ($table['view'] == FALSE) {
170: $this->reloadForeignKeys($table['name']);
171: }
172: }
173:
174: foreach ($this->structure['hasMany'] as & $table) {
175: uksort($table, function($a, $b) {
176: return strlen($a) - strlen($b);
177: });
178: }
179: }
180:
181:
182:
183: protected function reloadForeignKeys($table)
184: {
185: foreach ($this->connection->getSupplementalDriver()->getForeignKeys($table) as $row) {
186: $this->structure['belongsTo'][strtolower($table)][$row['local']] = $row['table'];
187: $this->structure['hasMany'][strtolower($row['table'])][$row['local'] . $table] = array($row['local'], $table);
188: }
189:
190: if (isset($this->structure['belongsTo'][$table])) {
191: uksort($this->structure['belongsTo'][$table], function($a, $b) {
192: return strlen($a) - strlen($b);
193: });
194: }
195: }
196:
197: }
198: