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