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