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