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