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