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