Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationLatte
      • ApplicationTracy
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsLatte
      • Framework
      • HttpTracy
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy

Classes

  • ActiveRow
  • GroupedSelection
  • Selection
  • SqlBuilder

Interfaces

  • IRow
  • IRowContainer
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  • Nette homepage
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Database\Table;
  9: 
 10: use Nette;
 11: use Nette\Database\ISupplementalDriver;
 12: 
 13: 
 14: /**
 15:  * Filtered table representation.
 16:  * Selection is based on the great library NotORM http://www.notorm.com written by Jakub Vrana.
 17:  *
 18:  * @author     Jakub Vrana
 19:  * @author     Jan Skrasek
 20:  *
 21:  * @property-read string $sql
 22:  */
 23: class Selection extends Nette\Object implements \Iterator, IRowContainer, \ArrayAccess, \Countable
 24: {
 25:     /** @var Nette\Database\Connection */
 26:     protected $connection;
 27: 
 28:     /** @var Nette\Database\IReflection */
 29:     protected $reflection;
 30: 
 31:     /** @var Nette\Caching\Cache */
 32:     protected $cache;
 33: 
 34:     /** @var SqlBuilder */
 35:     protected $sqlBuilder;
 36: 
 37:     /** @var string table name */
 38:     protected $name;
 39: 
 40:     /** @var string primary key field name */
 41:     protected $primary;
 42: 
 43:     /** @var string|bool primary column sequence name, FALSE for autodetection */
 44:     protected $primarySequence = FALSE;
 45: 
 46:     /** @var IRow[] data read from database in [primary key => IRow] format */
 47:     protected $rows;
 48: 
 49:     /** @var IRow[] modifiable data in [primary key => IRow] format */
 50:     protected $data;
 51: 
 52:     /** @var bool */
 53:     protected $dataRefreshed = FALSE;
 54: 
 55:     /** @var mixed cache array of Selection and GroupedSelection prototypes */
 56:     protected $globalRefCache;
 57: 
 58:     /** @var mixed */
 59:     protected $refCache;
 60: 
 61:     /** @var string */
 62:     protected $generalCacheKey;
 63: 
 64:     /** @var string */
 65:     protected $specificCacheKey;
 66: 
 67:     /** @var array of [conditions => [key => IRow]]; used by GroupedSelection */
 68:     protected $aggregation = array();
 69: 
 70:     /** @var array of touched columns */
 71:     protected $accessedColumns;
 72: 
 73:     /** @var array of earlier touched columns */
 74:     protected $previousAccessedColumns;
 75: 
 76:     /** @var bool should instance observe accessed columns caching */
 77:     protected $observeCache = FALSE;
 78: 
 79:     /** @var array of primary key values */
 80:     protected $keys = array();
 81: 
 82: 
 83:     /**
 84:      * Creates filtered table representation.
 85:      * @param  Nette\Database\Connection
 86:      * @param  string  database table name
 87:      */
 88:     public function __construct(Nette\Database\Connection $connection, $table, Nette\Database\IReflection $reflection, Nette\Caching\IStorage $cacheStorage = NULL)
 89:     {
 90:         $this->name = $table;
 91:         $this->connection = $connection;
 92:         $this->reflection = $reflection;
 93:         $this->cache = $cacheStorage ? new Nette\Caching\Cache($cacheStorage, 'Nette.Database.' . md5($connection->getDsn())) : NULL;
 94:         $this->primary = $reflection->getPrimary($table);
 95:         $this->sqlBuilder = new SqlBuilder($table, $connection, $reflection);
 96:         $this->refCache = & $this->getRefTable($refPath)->globalRefCache[$refPath];
 97:     }
 98: 
 99: 
100:     public function __destruct()
101:     {
102:         $this->saveCacheState();
103:     }
104: 
105: 
106:     public function __clone()
107:     {
108:         $this->sqlBuilder = clone $this->sqlBuilder;
109:     }
110: 
111: 
112:     /**
113:      * @return Nette\Database\Connection
114:      */
115:     public function getConnection()
116:     {
117:         return $this->connection;
118:     }
119: 
120: 
121:     /**
122:      * @return Nette\Database\IReflection
123:      */
124:     public function getDatabaseReflection()
125:     {
126:         return $this->reflection;
127:     }
128: 
129: 
130:     /**
131:      * @return string
132:      */
133:     public function getName()
134:     {
135:         return $this->name;
136:     }
137: 
138: 
139:     /**
140:      * @param  bool
141:      * @return string|array
142:      */
143:     public function getPrimary($need = TRUE)
144:     {
145:         if ($this->primary === NULL && $need) {
146:             throw new \LogicException("Table '{$this->name}' does not have a primary key.");
147:         }
148:         return $this->primary;
149:     }
150: 
151: 
152:     /**
153:      * @return string
154:      */
155:     public function getPrimarySequence()
156:     {
157:         if ($this->primarySequence === FALSE) {
158:             $this->primarySequence = NULL;
159:             $driver = $this->connection->getSupplementalDriver();
160:             if ($driver->isSupported(ISupplementalDriver::SUPPORT_SEQUENCE) && $this->primary !== NULL) {
161:                 foreach ($driver->getColumns($this->name) as $column) {
162:                     if ($column['name'] === $this->primary) {
163:                         $this->primarySequence = $column['vendor']['sequence'];
164:                         break;
165:                     }
166:                 }
167:             }
168:         }
169: 
170:         return $this->primarySequence;
171:     }
172: 
173: 
174:     /**
175:      * @param  string
176:      * @return self
177:      */
178:     public function setPrimarySequence($sequence)
179:     {
180:         $this->primarySequence = $sequence;
181:         return $this;
182:     }
183: 
184: 
185:     /**
186:      * @return string
187:      */
188:     public function getSql()
189:     {
190:         return $this->sqlBuilder->buildSelectQuery($this->getPreviousAccessedColumns());
191:     }
192: 
193: 
194:     /**
195:      * Loads cache of previous accessed columns and returns it.
196:      * @internal
197:      * @return array|false
198:      */
199:     public function getPreviousAccessedColumns()
200:     {
201:         if ($this->cache && $this->previousAccessedColumns === NULL) {
202:             $this->accessedColumns = $this->previousAccessedColumns = $this->cache->load($this->getGeneralCacheKey());
203:             if ($this->previousAccessedColumns === NULL) {
204:                 $this->previousAccessedColumns = array();
205:             }
206:         }
207: 
208:         return array_keys(array_filter((array) $this->previousAccessedColumns));
209:     }
210: 
211: 
212:     /**
213:      * @internal
214:      * @return SqlBuilder
215:      */
216:     public function getSqlBuilder()
217:     {
218:         return $this->sqlBuilder;
219:     }
220: 
221: 
222:     /********************* quick access ****************d*g**/
223: 
224: 
225:     /**
226:      * Returns row specified by primary key.
227:      * @param  mixed primary key
228:      * @return IRow or FALSE if there is no such row
229:      */
230:     public function get($key)
231:     {
232:         $clone = clone $this;
233:         return $clone->wherePrimary($key)->fetch();
234:     }
235: 
236: 
237:     /**
238:      * @inheritDoc
239:      */
240:     public function fetch()
241:     {
242:         $this->execute();
243:         $return = current($this->data);
244:         next($this->data);
245:         return $return;
246:     }
247: 
248: 
249:     /**
250:      * @inheritDoc
251:      */
252:     public function fetchPairs($key = NULL, $value = NULL)
253:     {
254:         return Nette\Database\Helpers::toPairs(iterator_to_array($this), $key, $value);
255:     }
256: 
257: 
258:     /**
259:      * @inheritDoc
260:      */
261:     public function fetchAll()
262:     {
263:         return iterator_to_array($this);
264:     }
265: 
266: 
267:     /********************* sql selectors ****************d*g**/
268: 
269: 
270:     /**
271:      * Adds select clause, more calls appends to the end.
272:      * @param  string for example "column, MD5(column) AS column_md5"
273:      * @return self
274:      */
275:     public function select($columns)
276:     {
277:         $this->emptyResultSet();
278:         call_user_func_array(array($this->sqlBuilder, 'addSelect'), func_get_args());
279:         return $this;
280:     }
281: 
282: 
283:     /**
284:      * Adds condition for primary key.
285:      * @param  mixed
286:      * @return self
287:      */
288:     public function wherePrimary($key)
289:     {
290:         if (is_array($this->primary) && Nette\Utils\Arrays::isList($key)) {
291:             if (isset($key[0]) && is_array($key[0])) {
292:                 $this->where($this->primary, $key);
293:             } else {
294:                 foreach ($this->primary as $i => $primary) {
295:                     $this->where($this->name . '.' . $primary, $key[$i]);
296:                 }
297:             }
298:         } elseif (is_array($key) && !Nette\Utils\Arrays::isList($key)) { // key contains column names
299:             $this->where($key);
300:         } else {
301:             $this->where($this->name . '.' . $this->getPrimary(), $key);
302:         }
303: 
304:         return $this;
305:     }
306: 
307: 
308:     /**
309:      * Adds where condition, more calls appends with AND.
310:      * @param  string condition possibly containing ?
311:      * @param  mixed
312:      * @param  mixed ...
313:      * @return self
314:      */
315:     public function where($condition, $parameters = array())
316:     {
317:         if (is_array($condition) && $parameters === array()) { // where(array('column1' => 1, 'column2 > ?' => 2))
318:             foreach ($condition as $key => $val) {
319:                 if (is_int($key)) {
320:                     $this->where($val); // where('full condition')
321:                 } else {
322:                     $this->where($key, $val); // where('column', 1)
323:                 }
324:             }
325:             return $this;
326:         }
327: 
328:         $this->emptyResultSet();
329:         call_user_func_array(array($this->sqlBuilder, 'addWhere'), func_get_args());
330:         return $this;
331:     }
332: 
333: 
334:     /**
335:      * Adds order clause, more calls appends to the end.
336:      * @param  string for example 'column1, column2 DESC'
337:      * @return self
338:      */
339:     public function order($columns)
340:     {
341:         $this->emptyResultSet();
342:         call_user_func_array(array($this->sqlBuilder, 'addOrder'), func_get_args());
343:         return $this;
344:     }
345: 
346: 
347:     /**
348:      * Sets limit clause, more calls rewrite old values.
349:      * @param  int
350:      * @param  int
351:      * @return self
352:      */
353:     public function limit($limit, $offset = NULL)
354:     {
355:         $this->emptyResultSet();
356:         $this->sqlBuilder->setLimit($limit, $offset);
357:         return $this;
358:     }
359: 
360: 
361:     /**
362:      * Sets offset using page number, more calls rewrite old values.
363:      * @param  int
364:      * @param  int
365:      * @return self
366:      */
367:     public function page($page, $itemsPerPage, & $numOfPages = NULL)
368:     {
369:         if (func_num_args() > 2) {
370:             $numOfPages = (int) ceil($this->count('*') / $itemsPerPage);
371:         }
372:         return $this->limit($itemsPerPage, ($page - 1) * $itemsPerPage);
373:     }
374: 
375: 
376:     /**
377:      * Sets group clause, more calls rewrite old value.
378:      * @param  string
379:      * @return self
380:      */
381:     public function group($columns)
382:     {
383:         $this->emptyResultSet();
384:         call_user_func_array(array($this->sqlBuilder, 'setGroup'), func_get_args());
385:         return $this;
386:     }
387: 
388: 
389:     /**
390:      * Sets having clause, more calls rewrite old value.
391:      * @param  string
392:      * @return self
393:      */
394:     public function having($having)
395:     {
396:         $this->emptyResultSet();
397:         call_user_func_array(array($this->sqlBuilder, 'setHaving'), func_get_args());
398:         return $this;
399:     }
400: 
401: 
402:     /********************* aggregations ****************d*g**/
403: 
404: 
405:     /**
406:      * Executes aggregation function.
407:      * @param  string select call in "FUNCTION(column)" format
408:      * @return string
409:      */
410:     public function aggregation($function)
411:     {
412:         $selection = $this->createSelectionInstance();
413:         $selection->getSqlBuilder()->importConditions($this->getSqlBuilder());
414:         $selection->select($function);
415:         foreach ($selection->fetch() as $val) {
416:             return $val;
417:         }
418:     }
419: 
420: 
421:     /**
422:      * Counts number of rows.
423:      * @param  string  if it is not provided returns count of result rows, otherwise runs new sql counting query
424:      * @return int
425:      */
426:     public function count($column = NULL)
427:     {
428:         if (!$column) {
429:             $this->execute();
430:             return count($this->data);
431:         }
432:         return $this->aggregation("COUNT($column)");
433:     }
434: 
435: 
436:     /**
437:      * Returns minimum value from a column.
438:      * @param  string
439:      * @return int
440:      */
441:     public function min($column)
442:     {
443:         return $this->aggregation("MIN($column)");
444:     }
445: 
446: 
447:     /**
448:      * Returns maximum value from a column.
449:      * @param  string
450:      * @return int
451:      */
452:     public function max($column)
453:     {
454:         return $this->aggregation("MAX($column)");
455:     }
456: 
457: 
458:     /**
459:      * Returns sum of values in a column.
460:      * @param  string
461:      * @return int
462:      */
463:     public function sum($column)
464:     {
465:         return $this->aggregation("SUM($column)");
466:     }
467: 
468: 
469:     /********************* internal ****************d*g**/
470: 
471: 
472:     protected function execute()
473:     {
474:         if ($this->rows !== NULL) {
475:             return;
476:         }
477: 
478:         $this->observeCache = $this;
479: 
480:         if ($this->primary === NULL && $this->sqlBuilder->getSelect() === NULL) {
481:             throw new Nette\InvalidStateException('Table with no primary key requires an explicit select clause.');
482:         }
483: 
484:         try {
485:             $result = $this->query($this->getSql());
486: 
487:         } catch (\PDOException $exception) {
488:             if (!$this->sqlBuilder->getSelect() && $this->previousAccessedColumns) {
489:                 $this->previousAccessedColumns = FALSE;
490:                 $this->accessedColumns = array();
491:                 $result = $this->query($this->getSql());
492:             } else {
493:                 throw $exception;
494:             }
495:         }
496: 
497:         $this->rows = array();
498:         $usedPrimary = TRUE;
499:         foreach ($result->getPdoStatement() as $key => $row) {
500:             $row = $this->createRow($result->normalizeRow($row));
501:             $primary = $row->getSignature(FALSE);
502:             $usedPrimary = $usedPrimary && $primary;
503:             $this->rows[$primary ?: $key] = $row;
504:         }
505:         $this->data = $this->rows;
506: 
507:         if ($usedPrimary && $this->accessedColumns !== FALSE) {
508:             foreach ((array) $this->primary as $primary) {
509:                 $this->accessedColumns[$primary] = TRUE;
510:             }
511:         }
512:     }
513: 
514: 
515:     protected function createRow(array $row)
516:     {
517:         return new ActiveRow($row, $this);
518:     }
519: 
520: 
521:     public function createSelectionInstance($table = NULL)
522:     {
523:         return new self($this->connection, $table ?: $this->name, $this->reflection, $this->cache ? $this->cache->getStorage() : NULL);
524:     }
525: 
526: 
527:     protected function createGroupedSelectionInstance($table, $column)
528:     {
529:         return new GroupedSelection($this, $table, $column);
530:     }
531: 
532: 
533:     protected function query($query)
534:     {
535:         return $this->connection->queryArgs($query, $this->sqlBuilder->getParameters());
536:     }
537: 
538: 
539:     protected function emptyResultSet($saveCache = TRUE)
540:     {
541:         if ($this->rows !== NULL && $saveCache) {
542:             $this->saveCacheState();
543:         }
544: 
545:         $this->rows = NULL;
546:         $this->specificCacheKey = NULL;
547:         $this->generalCacheKey = NULL;
548:         $this->refCache['referencingPrototype'] = array();
549:     }
550: 
551: 
552:     protected function saveCacheState()
553:     {
554:         if ($this->observeCache === $this && $this->cache && !$this->sqlBuilder->getSelect() && $this->accessedColumns !== $this->previousAccessedColumns) {
555:             $previousAccessed = $this->cache->load($this->getGeneralCacheKey());
556:             $accessed = $this->accessedColumns;
557:             $needSave = is_array($accessed) && is_array($previousAccessed)
558:                 ? array_intersect_key($accessed, $previousAccessed) !== $accessed
559:                 : $accessed !== $previousAccessed;
560: 
561:             if ($needSave) {
562:                 $save = is_array($accessed) && is_array($previousAccessed) ? $previousAccessed + $accessed : $accessed;
563:                 $this->cache->save($this->getGeneralCacheKey(), $save);
564:                 $this->previousAccessedColumns = NULL;
565:             }
566:         }
567:     }
568: 
569: 
570:     /**
571:      * Returns Selection parent for caching.
572:      * @return Selection
573:      */
574:     protected function getRefTable(& $refPath)
575:     {
576:         return $this;
577:     }
578: 
579: 
580:     /**
581:      * Loads refCache references
582:      */
583:     protected function loadRefCache()
584:     {
585:     }
586: 
587: 
588:     /**
589:      * Returns general cache key independent on query parameters or sql limit
590:      * Used e.g. for previously accessed columns caching
591:      * @return string
592:      */
593:     protected function getGeneralCacheKey()
594:     {
595:         if ($this->generalCacheKey) {
596:             return $this->generalCacheKey;
597:         }
598: 
599:         return $this->generalCacheKey = md5(serialize(array(__CLASS__, $this->name, $this->sqlBuilder->getConditions())));
600:     }
601: 
602: 
603:     /**
604:      * Returns object specific cache key dependent on query parameters
605:      * Used e.g. for reference memory caching
606:      * @return string
607:      */
608:     protected function getSpecificCacheKey()
609:     {
610:         if ($this->specificCacheKey) {
611:             return $this->specificCacheKey;
612:         }
613: 
614:         return $this->specificCacheKey = md5($this->getSql() . json_encode($this->sqlBuilder->getParameters()));
615:     }
616: 
617: 
618:     /**
619:      * @internal
620:      * @param  string|NULL column name or NULL to reload all columns
621:      * @param  bool
622:      */
623:     public function accessColumn($key, $selectColumn = TRUE)
624:     {
625:         if (!$this->cache) {
626:             return;
627:         }
628: 
629:         if ($key === NULL) {
630:             $this->accessedColumns = FALSE;
631:             $currentKey = key((array) $this->data);
632:         } elseif ($this->accessedColumns !== FALSE) {
633:             $this->accessedColumns[$key] = $selectColumn;
634:         }
635: 
636:         if ($selectColumn && !$this->sqlBuilder->getSelect() && $this->previousAccessedColumns && ($key === NULL || !isset($this->previousAccessedColumns[$key]))) {
637:             $this->previousAccessedColumns = array();
638: 
639:             if ($this->sqlBuilder->getLimit()) {
640:                 $generalCacheKey = $this->generalCacheKey;
641:                 $sqlBuilder = $this->sqlBuilder;
642: 
643:                 $primaryValues = array();
644:                 foreach ((array) $this->rows as $row) {
645:                     $primary = $row->getPrimary();
646:                     $primaryValues[] = is_array($primary) ? array_values($primary) : $primary;
647:                 }
648: 
649:                 $this->emptyResultSet(FALSE);
650:                 $this->sqlBuilder = clone $this->sqlBuilder;
651:                 $this->sqlBuilder->setLimit(NULL, NULL);
652:                 $this->wherePrimary($primaryValues);
653: 
654:                 $this->generalCacheKey = $generalCacheKey;
655:                 $this->execute();
656:                 $this->sqlBuilder = $sqlBuilder;
657:             } else {
658:                 $this->emptyResultSet(FALSE);
659:                 $this->execute();
660:             }
661: 
662:             $this->dataRefreshed = TRUE;
663: 
664:             // move iterator to specific key
665:             if (isset($currentKey)) {
666:                 while (key($this->data) !== $currentKey) {
667:                     next($this->data);
668:                 }
669:             }
670:         }
671:     }
672: 
673: 
674:     /**
675:      * @internal
676:      * @param  string
677:      */
678:     public function removeAccessColumn($key)
679:     {
680:         if ($this->cache && is_array($this->accessedColumns)) {
681:             $this->accessedColumns[$key] = FALSE;
682:         }
683:     }
684: 
685: 
686:     /**
687:      * Returns if selection requeried for more columns.
688:      * @return bool
689:      */
690:     public function getDataRefreshed()
691:     {
692:         return $this->dataRefreshed;
693:     }
694: 
695: 
696:     /********************* manipulation ****************d*g**/
697: 
698: 
699:     /**
700:      * Inserts row in a table.
701:      * @param  array|\Traversable|Selection array($column => $value)|\Traversable|Selection for INSERT ... SELECT
702:      * @return IRow|int|bool Returns IRow or number of affected rows for Selection or table without primary key
703:      */
704:     public function insert($data)
705:     {
706:         if ($data instanceof self) {
707:             $data = new Nette\Database\SqlLiteral($data->getSql(), $data->getSqlBuilder()->getParameters());
708: 
709:         } elseif ($data instanceof \Traversable) {
710:             $data = iterator_to_array($data);
711:         }
712: 
713:         $return = $this->connection->query($this->sqlBuilder->buildInsertQuery(), $data);
714:         $this->loadRefCache();
715: 
716:         if ($data instanceof Nette\Database\SqlLiteral || $this->primary === NULL) {
717:             unset($this->refCache['referencing'][$this->getGeneralCacheKey()][$this->getSpecificCacheKey()]);
718:             return $return->getRowCount();
719:         }
720: 
721:         $primaryKey = $this->connection->getInsertId($this->getPrimarySequence());
722:         if ($primaryKey === FALSE) {
723:             unset($this->refCache['referencing'][$this->getGeneralCacheKey()][$this->getSpecificCacheKey()]);
724:             return $return->getRowCount();
725:         }
726: 
727:         if (is_array($this->getPrimary())) {
728:             $primaryKey = array();
729: 
730:             foreach ((array) $this->getPrimary() as $key) {
731:                 if (!isset($data[$key])) {
732:                     return $data;
733:                 }
734: 
735:                 $primaryKey[$key] = $data[$key];
736:             }
737:             if (count($primaryKey) === 1) {
738:                 $primaryKey = reset($primaryKey);
739:             }
740:         }
741: 
742:         $row = $this->createSelectionInstance()
743:             ->select('*')
744:             ->wherePrimary($primaryKey)
745:             ->fetch();
746: 
747:         if ($this->rows !== NULL) {
748:             if ($signature = $row->getSignature(FALSE)) {
749:                 $this->rows[$signature] = $row;
750:                 $this->data[$signature] = $row;
751:             } else {
752:                 $this->rows[] = $row;
753:                 $this->data[] = $row;
754:             }
755:         }
756: 
757:         return $row;
758:     }
759: 
760: 
761:     /**
762:      * Updates all rows in result set.
763:      * Joins in UPDATE are supported only in MySQL
764:      * @param  array|\Traversable ($column => $value)
765:      * @return int number of affected rows
766:      */
767:     public function update($data)
768:     {
769:         if ($data instanceof \Traversable) {
770:             $data = iterator_to_array($data);
771: 
772:         } elseif (!is_array($data)) {
773:             throw new Nette\InvalidArgumentException;
774:         }
775: 
776:         if (!$data) {
777:             return 0;
778:         }
779: 
780:         return $this->connection->queryArgs(
781:             $this->sqlBuilder->buildUpdateQuery(),
782:             array_merge(array($data), $this->sqlBuilder->getParameters())
783:         )->getRowCount();
784:     }
785: 
786: 
787:     /**
788:      * Deletes all rows in result set.
789:      * @return int number of affected rows
790:      */
791:     public function delete()
792:     {
793:         return $this->query($this->sqlBuilder->buildDeleteQuery())->getRowCount();
794:     }
795: 
796: 
797:     /********************* references ****************d*g**/
798: 
799: 
800:     /**
801:      * Returns referenced row.
802:      * @param  string
803:      * @param  string
804:      * @param  mixed   primary key to check for $table and $column references
805:      * @return Selection or array() if the row does not exist
806:      */
807:     public function getReferencedTable($table, $column, $checkPrimaryKey)
808:     {
809:         $referenced = & $this->refCache['referenced'][$this->getSpecificCacheKey()]["$table.$column"];
810:         $selection = & $referenced['selection'];
811:         $cacheKeys = & $referenced['cacheKeys'];
812:         if ($selection === NULL || ($checkPrimaryKey !== NULL && !isset($cacheKeys[$checkPrimaryKey]))) {
813:             $this->execute();
814:             $cacheKeys = array();
815:             foreach ($this->rows as $row) {
816:                 if ($row[$column] === NULL) {
817:                     continue;
818:                 }
819: 
820:                 $key = $row[$column];
821:                 $cacheKeys[$key] = TRUE;
822:             }
823: 
824:             if ($cacheKeys) {
825:                 $selection = $this->createSelectionInstance($table);
826:                 $selection->where($selection->getPrimary(), array_keys($cacheKeys));
827:             } else {
828:                 $selection = array();
829:             }
830:         }
831: 
832:         return $selection;
833:     }
834: 
835: 
836:     /**
837:      * Returns referencing rows.
838:      * @param  string
839:      * @param  string
840:      * @param  int primary key
841:      * @return GroupedSelection
842:      */
843:     public function getReferencingTable($table, $column, $active = NULL)
844:     {
845:         $prototype = & $this->refCache['referencingPrototype'][$this->getSpecificCacheKey()]["$table.$column"];
846:         if (!$prototype) {
847:             $prototype = $this->createGroupedSelectionInstance($table, $column);
848:             $prototype->where("$table.$column", array_keys((array) $this->rows));
849:         }
850: 
851:         $clone = clone $prototype;
852:         $clone->setActive($active);
853:         return $clone;
854:     }
855: 
856: 
857:     /********************* interface Iterator ****************d*g**/
858: 
859: 
860:     public function rewind()
861:     {
862:         $this->execute();
863:         $this->keys = array_keys($this->data);
864:         reset($this->keys);
865:     }
866: 
867: 
868:     /** @return IRow */
869:     public function current()
870:     {
871:         if (($key = current($this->keys)) !== FALSE) {
872:             return $this->data[$key];
873:         } else {
874:             return FALSE;
875:         }
876:     }
877: 
878: 
879:     /**
880:      * @return string row ID
881:      */
882:     public function key()
883:     {
884:         return current($this->keys);
885:     }
886: 
887: 
888:     public function next()
889:     {
890:         next($this->keys);
891:     }
892: 
893: 
894:     public function valid()
895:     {
896:         return current($this->keys) !== FALSE;
897:     }
898: 
899: 
900:     /********************* interface ArrayAccess ****************d*g**/
901: 
902: 
903:     /**
904:      * Mimic row.
905:      * @param  string row ID
906:      * @param  IRow
907:      * @return NULL
908:      */
909:     public function offsetSet($key, $value)
910:     {
911:         $this->execute();
912:         $this->rows[$key] = $value;
913:     }
914: 
915: 
916:     /**
917:      * Returns specified row.
918:      * @param  string row ID
919:      * @return IRow or NULL if there is no such row
920:      */
921:     public function offsetGet($key)
922:     {
923:         $this->execute();
924:         return $this->rows[$key];
925:     }
926: 
927: 
928:     /**
929:      * Tests if row exists.
930:      * @param  string row ID
931:      * @return bool
932:      */
933:     public function offsetExists($key)
934:     {
935:         $this->execute();
936:         return isset($this->rows[$key]);
937:     }
938: 
939: 
940:     /**
941:      * Removes row from result set.
942:      * @param  string row ID
943:      * @return NULL
944:      */
945:     public function offsetUnset($key)
946:     {
947:         $this->execute();
948:         unset($this->rows[$key], $this->data[$key]);
949:     }
950: 
951: }
952: 
Nette 2.2 API documentation generated by ApiGen 2.8.0