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