1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Database\Table;
9:
10: use Nette;
11: use Nette\Database\Reflection\MissingReferenceException;
12:
13:
14: 15: 16: 17: 18: 19: 20:
21: class ActiveRow implements \IteratorAggregate, IRow
22: {
23:
24: private $table;
25:
26:
27: private $data;
28:
29:
30: private $dataRefreshed = FALSE;
31:
32:
33: public function __construct(array $data, Selection $table)
34: {
35: $this->data = $data;
36: $this->table = $table;
37: }
38:
39:
40: 41: 42: 43:
44: public function setTable(Selection $table)
45: {
46: $this->table = $table;
47: }
48:
49:
50: 51: 52:
53: public function getTable()
54: {
55: return $this->table;
56: }
57:
58:
59: public function __toString()
60: {
61: try {
62: return (string) $this->getPrimary();
63: } catch (\Throwable $e) {
64: } catch (\Exception $e) {
65: }
66: if (isset($e)) {
67: if (func_num_args()) {
68: throw $e;
69: }
70: trigger_error("Exception in " . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR);
71: }
72: }
73:
74:
75: 76: 77:
78: public function toArray()
79: {
80: $this->accessColumn(NULL);
81: return $this->data;
82: }
83:
84:
85: 86: 87: 88: 89:
90: public function getPrimary($need = TRUE)
91: {
92: $primary = $this->table->getPrimary($need);
93: if ($primary === NULL) {
94: return NULL;
95:
96: } elseif (!is_array($primary)) {
97: if (isset($this->data[$primary])) {
98: return $this->data[$primary];
99: } elseif ($need) {
100: throw new Nette\InvalidStateException("Row does not contain primary $primary column data.");
101: } else {
102: return NULL;
103: }
104:
105: } else {
106: $primaryVal = array();
107: foreach ($primary as $key) {
108: if (!isset($this->data[$key])) {
109: if ($need) {
110: throw new Nette\InvalidStateException("Row does not contain primary $key column data.");
111: } else {
112: return NULL;
113: }
114: }
115: $primaryVal[$key] = $this->data[$key];
116: }
117: return $primaryVal;
118: }
119: }
120:
121:
122: 123: 124: 125: 126:
127: public function getSignature($need = TRUE)
128: {
129: return implode('|', (array) $this->getPrimary($need));
130: }
131:
132:
133: 134: 135: 136: 137: 138:
139: public function ref($key, $throughColumn = NULL)
140: {
141: if (!$throughColumn) {
142: list($key, $throughColumn) = $this->table->getDatabaseReflection()->getBelongsToReference($this->table->getName(), $key);
143: }
144:
145: return $this->getReference($key, $throughColumn);
146: }
147:
148:
149: 150: 151: 152: 153: 154:
155: public function related($key, $throughColumn = NULL)
156: {
157: if (strpos($key, '.') !== FALSE) {
158: list($key, $throughColumn) = explode('.', $key);
159: } elseif (!$throughColumn) {
160: list($key, $throughColumn) = $this->table->getDatabaseReflection()->getHasManyReference($this->table->getName(), $key);
161: }
162:
163: return $this->table->getReferencingTable($key, $throughColumn, $this[$this->table->getPrimary()]);
164: }
165:
166:
167: 168: 169: 170: 171:
172: public function update($data)
173: {
174: $selection = $this->table->createSelectionInstance()
175: ->wherePrimary($this->getPrimary());
176:
177: if ($selection->update($data)) {
178: $selection->select('*');
179: if (($row = $selection->fetch()) === FALSE) {
180: throw new Nette\InvalidStateException('Database refetch failed; row does not exist!');
181: }
182: $this->data = $row->data;
183: return TRUE;
184: } else {
185: return FALSE;
186: }
187: }
188:
189:
190: 191: 192: 193:
194: public function delete()
195: {
196: $res = $this->table->createSelectionInstance()
197: ->wherePrimary($this->getPrimary())
198: ->delete();
199:
200: if ($res > 0 && ($signature = $this->getSignature(FALSE))) {
201: unset($this->table[$signature]);
202: }
203:
204: return $res;
205: }
206:
207:
208:
209:
210:
211: public function getIterator()
212: {
213: $this->accessColumn(NULL);
214: return new \ArrayIterator($this->data);
215: }
216:
217:
218:
219:
220:
221: 222: 223: 224: 225: 226:
227: public function offsetSet($key, $value)
228: {
229: $this->__set($key, $value);
230: }
231:
232:
233: 234: 235: 236: 237:
238: public function offsetGet($key)
239: {
240: return $this->__get($key);
241: }
242:
243:
244: 245: 246: 247: 248:
249: public function offsetExists($key)
250: {
251: return $this->__isset($key);
252: }
253:
254:
255: 256: 257: 258: 259:
260: public function offsetUnset($key)
261: {
262: $this->__unset($key);
263: }
264:
265:
266: public function __set($key, $value)
267: {
268: throw new Nette\DeprecatedException('ActiveRow is read-only; use update() method instead.');
269: }
270:
271:
272: public function &__get($key)
273: {
274: $this->accessColumn($key);
275: if (array_key_exists($key, $this->data)) {
276: return $this->data[$key];
277: }
278:
279: try {
280: list($table, $column) = $this->table->getDatabaseReflection()->getBelongsToReference($this->table->getName(), $key);
281: $referenced = $this->getReference($table, $column);
282: if ($referenced !== FALSE) {
283: $this->accessColumn($key, FALSE);
284: return $referenced;
285: }
286: } catch(MissingReferenceException $e) {}
287:
288: $this->removeAccessColumn($key);
289: throw new Nette\MemberAccessException("Cannot read an undeclared column '$key'.");
290: }
291:
292:
293: public function __isset($key)
294: {
295: $this->accessColumn($key);
296: if (array_key_exists($key, $this->data)) {
297: return isset($this->data[$key]);
298: }
299: $this->removeAccessColumn($key);
300: return FALSE;
301: }
302:
303:
304: public function __unset($key)
305: {
306: throw new Nette\DeprecatedException('ActiveRow is read-only.');
307: }
308:
309:
310: protected function accessColumn($key, $selectColumn = TRUE)
311: {
312: $this->table->accessColumn($key, $selectColumn);
313: if ($this->table->getDataRefreshed() && !$this->dataRefreshed) {
314: $this->data = $this->table[$this->getSignature()]->data;
315: $this->dataRefreshed = TRUE;
316: }
317: }
318:
319:
320: protected function removeAccessColumn($key)
321: {
322: $this->table->removeAccessColumn($key);
323: }
324:
325:
326: protected function getReference($table, $column)
327: {
328: $this->accessColumn($column);
329: if (array_key_exists($column, $this->data)) {
330: $value = $this->data[$column];
331: $referenced = $this->table->getReferencedTable($table, $column, $value);
332: return isset($referenced[$value]) ? $referenced[$value] : NULL;
333: }
334:
335: return FALSE;
336: }
337:
338: }
339: