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