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