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:
21: class ActiveRow implements \IteratorAggregate, IRow
22: {
23:
24: private $table;
25:
26:
27: private $data;
28:
29:
30: private $dataRefreshed = FALSE;
31:
32:
33: private $isModified = FALSE;
34:
35:
36: public function __construct(array $data, Selection $table)
37: {
38: $this->data = $data;
39: $this->table = $table;
40: }
41:
42:
43: 44: 45: 46:
47: public function setTable(Selection $table)
48: {
49: $this->table = $table;
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->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->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)
173: {
174: $selection = $this->table->createSelectionInstance()
175: ->wherePrimary($this->getPrimary());
176:
177: if ($selection->update($data)) {
178: $this->isModified = TRUE;
179: $selection->select('*');
180: if (($row = $selection->fetch()) === FALSE) {
181: throw new Nette\InvalidStateException('Database refetch failed; row does not exist!');
182: }
183: $this->data = $row->data;
184: return TRUE;
185: } else {
186: return FALSE;
187: }
188: }
189:
190:
191: 192: 193: 194:
195: public function delete()
196: {
197: $res = $this->table->createSelectionInstance()
198: ->wherePrimary($this->getPrimary())
199: ->delete();
200:
201: if ($res > 0 && ($signature = $this->getSignature(FALSE))) {
202: unset($this->table[$signature]);
203: }
204:
205: return $res;
206: }
207:
208:
209:
210:
211:
212: public function getIterator()
213: {
214: $this->accessColumn(NULL);
215: return new \ArrayIterator($this->data);
216: }
217:
218:
219:
220:
221:
222: 223: 224: 225: 226: 227:
228: public function offsetSet($key, $value)
229: {
230: $this->__set($key, $value);
231: }
232:
233:
234: 235: 236: 237: 238:
239: public function offsetGet($key)
240: {
241: return $this->__get($key);
242: }
243:
244:
245: 246: 247: 248: 249:
250: public function offsetExists($key)
251: {
252: return $this->__isset($key);
253: }
254:
255:
256: 257: 258: 259: 260:
261: public function offsetUnset($key)
262: {
263: $this->__unset($key);
264: }
265:
266:
267: public function __set($key, $value)
268: {
269: throw new Nette\DeprecatedException('ActiveRow is read-only; use update() method instead.');
270: }
271:
272:
273: public function &__get($key)
274: {
275: $this->accessColumn($key);
276: if (array_key_exists($key, $this->data)) {
277: return $this->data[$key];
278: }
279:
280: try {
281: list($table, $column) = $this->table->getDatabaseReflection()->getBelongsToReference($this->table->getName(), $key);
282: $referenced = $this->getReference($table, $column);
283: if ($referenced !== FALSE) {
284: $this->accessColumn($key, FALSE);
285: return $referenced;
286: }
287: } catch(MissingReferenceException $e) {}
288:
289: $this->removeAccessColumn($key);
290: throw new Nette\MemberAccessException("Cannot read an undeclared column '$key'.");
291: }
292:
293:
294: public function __isset($key)
295: {
296: $this->accessColumn($key);
297: if (array_key_exists($key, $this->data)) {
298: return isset($this->data[$key]);
299: }
300: $this->removeAccessColumn($key);
301: return FALSE;
302: }
303:
304:
305: public function __unset($key)
306: {
307: throw new Nette\DeprecatedException('ActiveRow is read-only.');
308: }
309:
310:
311: protected function accessColumn($key, $selectColumn = TRUE)
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: $referenced = $this->table->getReferencedTable($table, $column, $value);
333: return isset($referenced[$value]) ? $referenced[$value] : NULL;
334: }
335:
336: return FALSE;
337: }
338:
339: }
340: