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