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