1: <?php
  2: 
  3:   4:   5:   6: 
  7: 
  8: namespace Nette\Database\Table;
  9: 
 10: use Nette;
 11: use 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:     public function __construct(array $data, Selection $table)
 34:     {
 35:         $this->data = $data;
 36:         $this->table = $table;
 37:     }
 38: 
 39: 
 40:      41:  42:  43: 
 44:     public function setTable(Selection $table)
 45:     {
 46:         $this->table = $table;
 47:     }
 48: 
 49: 
 50:      51:  52: 
 53:     public function getTable()
 54:     {
 55:         return $this->table;
 56:     }
 57: 
 58: 
 59:     public function __toString()
 60:     {
 61:         try {
 62:             return (string) $this->getPrimary();
 63:         } catch (\Exception $e) {
 64:             if (func_num_args()) {
 65:                 throw $e;
 66:             }
 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->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->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)
170:     {
171:         $selection = $this->table->createSelectionInstance()
172:             ->wherePrimary($this->getPrimary());
173: 
174:         if ($selection->update($data)) {
175:             $selection->select('*');
176:             if (($row = $selection->fetch()) === FALSE) {
177:                 throw new Nette\InvalidStateException('Database refetch failed; row does not exist!');
178:             }
179:             $this->data = $row->data;
180:             return TRUE;
181:         } else {
182:             return FALSE;
183:         }
184:     }
185: 
186: 
187:     188: 189: 190: 
191:     public function delete()
192:     {
193:         $res = $this->table->createSelectionInstance()
194:             ->wherePrimary($this->getPrimary())
195:             ->delete();
196: 
197:         if ($res > 0 && ($signature = $this->getSignature(FALSE))) {
198:             unset($this->table[$signature]);
199:         }
200: 
201:         return $res;
202:     }
203: 
204: 
205:     
206: 
207: 
208:     public function getIterator()
209:     {
210:         $this->accessColumn(NULL);
211:         return new \ArrayIterator($this->data);
212:     }
213: 
214: 
215:     
216: 
217: 
218:     219: 220: 221: 222: 223: 
224:     public function offsetSet($key, $value)
225:     {
226:         $this->__set($key, $value);
227:     }
228: 
229: 
230:     231: 232: 233: 234: 
235:     public function offsetGet($key)
236:     {
237:         return $this->__get($key);
238:     }
239: 
240: 
241:     242: 243: 244: 245: 
246:     public function offsetExists($key)
247:     {
248:         return $this->__isset($key);
249:     }
250: 
251: 
252:     253: 254: 255: 256: 
257:     public function offsetUnset($key)
258:     {
259:         $this->__unset($key);
260:     }
261: 
262: 
263:     public function __set($key, $value)
264:     {
265:         throw new Nette\DeprecatedException('ActiveRow is read-only; use update() method instead.');
266:     }
267: 
268: 
269:     public function &__get($key)
270:     {
271:         $this->accessColumn($key);
272:         if (array_key_exists($key, $this->data)) {
273:             return $this->data[$key];
274:         }
275: 
276:         try {
277:             list($table, $column) = $this->table->getDatabaseReflection()->getBelongsToReference($this->table->getName(), $key);
278:             $referenced = $this->getReference($table, $column);
279:             if ($referenced !== FALSE) {
280:                 $this->accessColumn($key, FALSE);
281:                 return $referenced;
282:             }
283:         } catch(MissingReferenceException $e) {}
284: 
285:         $this->removeAccessColumn($key);
286:         throw new Nette\MemberAccessException("Cannot read an undeclared column '$key'.");
287:     }
288: 
289: 
290:     public function __isset($key)
291:     {
292:         $this->accessColumn($key);
293:         if (array_key_exists($key, $this->data)) {
294:             return isset($this->data[$key]);
295:         }
296:         $this->removeAccessColumn($key);
297:         return FALSE;
298:     }
299: 
300: 
301:     public function __unset($key)
302:     {
303:         throw new Nette\DeprecatedException('ActiveRow is read-only.');
304:     }
305: 
306: 
307:     protected function accessColumn($key, $selectColumn = TRUE)
308:     {
309:         $this->table->accessColumn($key, $selectColumn);
310:         if ($this->table->getDataRefreshed() && !$this->dataRefreshed) {
311:             $this->data = $this->table[$this->getSignature()]->data;
312:             $this->dataRefreshed = TRUE;
313:         }
314:     }
315: 
316: 
317:     protected function removeAccessColumn($key)
318:     {
319:         $this->table->removeAccessColumn($key);
320:     }
321: 
322: 
323:     protected function getReference($table, $column)
324:     {
325:         $this->accessColumn($column);
326:         if (array_key_exists($column, $this->data)) {
327:             $value = $this->data[$column];
328:             $referenced = $this->table->getReferencedTable($table, $column, $value);
329:             return isset($referenced[$value]) ? $referenced[$value] : NULL; 
330:         }
331: 
332:         return FALSE;
333:     }
334: 
335: }
336: