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