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