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: } catch (\Throwable $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($throw = true)
86: {
87: $primary = $this->table->getPrimary($throw);
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 ($throw) {
95: throw new Nette\InvalidStateException("Row does not contain primary $primary column data.");
96: } else {
97: return null;
98: }
99:
100: } else {
101: $primaryVal = [];
102: foreach ($primary as $key) {
103: if (!isset($this->data[$key])) {
104: if ($throw) {
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($throw = true)
123: {
124: return implode('|', (array) $this->getPrimary($throw));
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->getName()}->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->getName()}->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 = [$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($column, $value)
236: {
237: $this->__set($column, $value);
238: }
239:
240:
241: 242: 243: 244: 245:
246: public function offsetGet($column)
247: {
248: return $this->__get($column);
249: }
250:
251:
252: 253: 254: 255: 256:
257: public function offsetExists($column)
258: {
259: return $this->__isset($column);
260: }
261:
262:
263: 264: 265: 266: 267:
268: public function offsetUnset($column)
269: {
270: $this->__unset($column);
271: }
272:
273:
274: public function __set($column, $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:
309: $referenced = $this->table->getReferencedTable($this, $key);
310: if ($referenced !== false) {
311: $this->accessColumn($key, false);
312: return (bool) $referenced;
313: }
314:
315: $this->removeAccessColumn($key);
316: return false;
317: }
318:
319:
320: public function __unset($key)
321: {
322: throw new Nette\DeprecatedException('ActiveRow is read-only.');
323: }
324:
325:
326: 327: 328:
329: public function accessColumn($key, $selectColumn = true)
330: {
331: if ($this->table->accessColumn($key, $selectColumn) && !$this->dataRefreshed) {
332: if (!isset($this->table[$this->getSignature()])) {
333: throw new Nette\InvalidStateException("Database refetch failed; row with signature '{$this->getSignature()}' does not exist!");
334: }
335: $this->data = $this->table[$this->getSignature()]->data;
336: $this->dataRefreshed = true;
337: }
338: return isset($this->data[$key]) || array_key_exists($key, $this->data);
339: }
340:
341:
342: protected function removeAccessColumn($key)
343: {
344: $this->table->removeAccessColumn($key);
345: }
346: }
347: