Namespaces

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Diagnostics
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
      • Diagnostics
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • PhpGenerator
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
  • NetteModule
  • none

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 (https://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:      * @deprecated
285:      */
286:     public function find($key)
287:     {
288:         trigger_error(__METHOD__ . '() is deprecated; use $selection->wherePrimary() instead.', E_USER_DEPRECATED);
289:         return $this->wherePrimary($key);
290:     }
291: 
292: 
293:     /**
294:      * Adds condition for primary key.
295:      * @param  mixed
296:      * @return self
297:      */
298:     public function wherePrimary($key)
299:     {
300:         if (is_array($this->primary) && Nette\Utils\Arrays::isList($key)) {
301:             if (isset($key[0]) && is_array($key[0])) {
302:                 $this->where($this->primary, $key);
303:             } else {
304:                 foreach ($this->primary as $i => $primary) {
305:                     $this->where($this->name . '.' . $primary, $key[$i]);
306:                 }
307:             }
308:         } elseif (is_array($key) && !Nette\Utils\Arrays::isList($key)) { // key contains column names
309:             $this->where($key);
310:         } else {
311:             $this->where($this->name . '.' . $this->getPrimary(), $key);
312:         }
313: 
314:         return $this;
315:     }
316: 
317: 
318:     /**
319:      * Adds where condition, more calls appends with AND.
320:      * @param  string condition possibly containing ?
321:      * @param  mixed
322:      * @param  mixed ...
323:      * @return self
324:      */
325:     public function where($condition, $parameters = array())
326:     {
327:         if (is_array($condition) && $parameters === array()) { // where(array('column1' => 1, 'column2 > ?' => 2))
328:             foreach ($condition as $key => $val) {
329:                 if (is_int($key)) {
330:                     $this->where($val); // where('full condition')
331:                 } else {
332:                     $this->where($key, $val); // where('column', 1)
333:                 }
334:             }
335:             return $this;
336:         }
337: 
338:         $this->emptyResultSet();
339:         call_user_func_array(array($this->sqlBuilder, 'addWhere'), func_get_args());
340:         return $this;
341:     }
342: 
343: 
344:     /**
345:      * Adds order clause, more calls appends to the end.
346:      * @param  string for example 'column1, column2 DESC'
347:      * @return self
348:      */
349:     public function order($columns)
350:     {
351:         $this->emptyResultSet();
352:         call_user_func_array(array($this->sqlBuilder, 'addOrder'), func_get_args());
353:         return $this;
354:     }
355: 
356: 
357:     /**
358:      * Sets limit clause, more calls rewrite old values.
359:      * @param  int
360:      * @param  int
361:      * @return self
362:      */
363:     public function limit($limit, $offset = NULL)
364:     {
365:         $this->emptyResultSet();
366:         $this->sqlBuilder->setLimit($limit, $offset);
367:         return $this;
368:     }
369: 
370: 
371:     /**
372:      * Sets offset using page number, more calls rewrite old values.
373:      * @param  int
374:      * @param  int
375:      * @return self
376:      */
377:     public function page($page, $itemsPerPage, & $numOfPages = NULL)
378:     {
379:         if (func_num_args() > 2) {
380:             $numOfPages = (int) ceil($this->count('*') / $itemsPerPage);
381:         }
382:         return $this->limit($itemsPerPage, ($page - 1) * $itemsPerPage);
383:     }
384: 
385: 
386:     /**
387:      * Sets group clause, more calls rewrite old value.
388:      * @param  string
389:      * @return self
390:      */
391:     public function group($columns)
392:     {
393:         $this->emptyResultSet();
394:         if (func_num_args() === 2 && strpos($columns, '?') === FALSE) {
395:             trigger_error('Calling ' . __METHOD__ . '() with second argument is deprecated; use $selection->having() instead.', E_USER_DEPRECATED);
396:             $this->having(func_get_arg(1));
397:             $this->sqlBuilder->setGroup($columns);
398:         } else {
399:             call_user_func_array(array($this->sqlBuilder, 'setGroup'), func_get_args());
400:         }
401:         return $this;
402:     }
403: 
404: 
405:     /**
406:      * Sets having clause, more calls rewrite old value.
407:      * @param  string
408:      * @return self
409:      */
410:     public function having($having)
411:     {
412:         $this->emptyResultSet();
413:         call_user_func_array(array($this->sqlBuilder, 'setHaving'), func_get_args());
414:         return $this;
415:     }
416: 
417: 
418:     /********************* aggregations ****************d*g**/
419: 
420: 
421:     /**
422:      * Executes aggregation function.
423:      * @param  string select call in "FUNCTION(column)" format
424:      * @return string
425:      */
426:     public function aggregation($function)
427:     {
428:         $selection = $this->createSelectionInstance();
429:         $selection->getSqlBuilder()->importConditions($this->getSqlBuilder());
430:         $selection->select($function);
431:         foreach ($selection->fetch() as $val) {
432:             return $val;
433:         }
434:     }
435: 
436: 
437:     /**
438:      * Counts number of rows.
439:      * @param  string  if it is not provided returns count of result rows, otherwise runs new sql counting query
440:      * @return int
441:      */
442:     public function count($column = NULL)
443:     {
444:         if (!$column) {
445:             $this->execute();
446:             return count($this->data);
447:         }
448:         return $this->aggregation("COUNT($column)");
449:     }
450: 
451: 
452:     /**
453:      * Returns minimum value from a column.
454:      * @param  string
455:      * @return int
456:      */
457:     public function min($column)
458:     {
459:         return $this->aggregation("MIN($column)");
460:     }
461: 
462: 
463:     /**
464:      * Returns maximum value from a column.
465:      * @param  string
466:      * @return int
467:      */
468:     public function max($column)
469:     {
470:         return $this->aggregation("MAX($column)");
471:     }
472: 
473: 
474:     /**
475:      * Returns sum of values in a column.
476:      * @param  string
477:      * @return int
478:      */
479:     public function sum($column)
480:     {
481:         return $this->aggregation("SUM($column)");
482:     }
483: 
484: 
485:     /********************* internal ****************d*g**/
486: 
487: 
488:     protected function execute()
489:     {
490:         if ($this->rows !== NULL) {
491:             return;
492:         }
493: 
494:         $this->observeCache = $this;
495: 
496:         if ($this->primary === NULL && $this->sqlBuilder->getSelect() === NULL) {
497:             throw new Nette\InvalidStateException('Table with no primary key requires an explicit select clause.');
498:         }
499: 
500:         try {
501:             $result = $this->query($this->getSql());
502: 
503:         } catch (\PDOException $exception) {
504:             if (!$this->sqlBuilder->getSelect() && $this->previousAccessedColumns) {
505:                 $this->previousAccessedColumns = FALSE;
506:                 $this->accessedColumns = array();
507:                 $result = $this->query($this->getSql());
508:             } else {
509:                 throw $exception;
510:             }
511:         }
512: 
513:         $this->rows = array();
514:         $usedPrimary = TRUE;
515:         foreach ($result->getPdoStatement() as $key => $row) {
516:             $row = $this->createRow($result->normalizeRow($row));
517:             $primary = $row->getSignature(FALSE);
518:             $usedPrimary = $usedPrimary && $primary;
519:             $this->rows[$primary ?: $key] = $row;
520:         }
521:         $this->data = $this->rows;
522: 
523:         if ($usedPrimary && $this->accessedColumns !== FALSE) {
524:             foreach ((array) $this->primary as $primary) {
525:                 $this->accessedColumns[$primary] = TRUE;
526:             }
527:         }
528:     }
529: 
530: 
531:     protected function createRow(array $row)
532:     {
533:         return new ActiveRow($row, $this);
534:     }
535: 
536: 
537:     public function createSelectionInstance($table = NULL)
538:     {
539:         return new Selection($this->connection, $table ?: $this->name, $this->reflection, $this->cache ? $this->cache->getStorage() : NULL);
540:     }
541: 
542: 
543:     protected function createGroupedSelectionInstance($table, $column)
544:     {
545:         return new GroupedSelection($this, $table, $column);
546:     }
547: 
548: 
549:     protected function query($query)
550:     {
551:         return $this->connection->queryArgs($query, $this->sqlBuilder->getParameters());
552:     }
553: 
554: 
555:     protected function emptyResultSet($saveCache = TRUE)
556:     {
557:         if ($this->rows !== NULL && $saveCache) {
558:             $this->saveCacheState();
559:         }
560: 
561:         $this->rows = NULL;
562:         $this->specificCacheKey = NULL;
563:         $this->generalCacheKey = NULL;
564:         $this->refCache['referencingPrototype'] = array();
565:     }
566: 
567: 
568:     protected function saveCacheState()
569:     {
570:         if ($this->observeCache === $this && $this->cache && !$this->sqlBuilder->getSelect() && $this->accessedColumns !== $this->previousAccessedColumns) {
571:             $previousAccessed = $this->cache->load($this->getGeneralCacheKey());
572:             $accessed = $this->accessedColumns;
573:             $needSave = is_array($accessed) && is_array($previousAccessed)
574:                 ? array_intersect_key($accessed, $previousAccessed) !== $accessed
575:                 : $accessed !== $previousAccessed;
576: 
577:             if ($needSave) {
578:                 $save = is_array($accessed) && is_array($previousAccessed) ? $previousAccessed + $accessed : $accessed;
579:                 $this->cache->save($this->getGeneralCacheKey(), $save);
580:                 $this->previousAccessedColumns = NULL;
581:             }
582:         }
583:     }
584: 
585: 
586:     /**
587:      * Returns Selection parent for caching.
588:      * @return Selection
589:      */
590:     protected function getRefTable(& $refPath)
591:     {
592:         return $this;
593:     }
594: 
595: 
596:     /**
597:      * Loads refCache references
598:      */
599:     protected function loadRefCache()
600:     {
601:     }
602: 
603: 
604:     /**
605:      * Returns general cache key independent on query parameters or sql limit
606:      * Used e.g. for previously accessed columns caching
607:      * @return string
608:      */
609:     protected function getGeneralCacheKey()
610:     {
611:         if ($this->generalCacheKey) {
612:             return $this->generalCacheKey;
613:         }
614: 
615:         return $this->generalCacheKey = md5(serialize(array(__CLASS__, $this->name, $this->sqlBuilder->getConditions())));
616:     }
617: 
618: 
619:     /**
620:      * Returns object specific cache key dependent on query parameters
621:      * Used e.g. for reference memory caching
622:      * @return string
623:      */
624:     protected function getSpecificCacheKey()
625:     {
626:         if ($this->specificCacheKey) {
627:             return $this->specificCacheKey;
628:         }
629: 
630:         return $this->specificCacheKey = md5($this->getSql() . json_encode($this->sqlBuilder->getParameters()));
631:     }
632: 
633: 
634:     /**
635:      * @internal
636:      * @param  string|NULL column name or NULL to reload all columns
637:      * @param  bool
638:      */
639:     public function accessColumn($key, $selectColumn = TRUE)
640:     {
641:         if (!$this->cache) {
642:             return;
643:         }
644: 
645:         if ($key === NULL) {
646:             $this->accessedColumns = FALSE;
647:             $currentKey = key((array) $this->data);
648:         } elseif ($this->accessedColumns !== FALSE) {
649:             $this->accessedColumns[$key] = $selectColumn;
650:         }
651: 
652:         if ($selectColumn && !$this->sqlBuilder->getSelect() && $this->previousAccessedColumns && ($key === NULL || !isset($this->previousAccessedColumns[$key]))) {
653:             $this->previousAccessedColumns = array();
654: 
655:             if ($this->sqlBuilder->getLimit()) {
656:                 $generalCacheKey = $this->generalCacheKey;
657:                 $sqlBuilder = $this->sqlBuilder;
658: 
659:                 $primaryValues = array();
660:                 foreach ((array) $this->rows as $row) {
661:                     $primary = $row->getPrimary();
662:                     $primaryValues[] = is_array($primary) ? array_values($primary) : $primary;
663:                 }
664: 
665:                 $this->emptyResultSet(FALSE);
666:                 $this->sqlBuilder = clone $this->sqlBuilder;
667:                 $this->sqlBuilder->setLimit(NULL, NULL);
668:                 $this->wherePrimary($primaryValues);
669: 
670:                 $this->generalCacheKey = $generalCacheKey;
671:                 $this->execute();
672:                 $this->sqlBuilder = $sqlBuilder;
673:             } else {
674:                 $this->emptyResultSet(FALSE);
675:                 $this->execute();
676:             }
677: 
678:             $this->dataRefreshed = TRUE;
679: 
680:             // move iterator to specific key
681:             if (isset($currentKey)) {
682:                 while (key($this->data) !== $currentKey) {
683:                     next($this->data);
684:                 }
685:             }
686:         }
687:     }
688: 
689: 
690:     /**
691:      * @internal
692:      * @param  string
693:      */
694:     public function removeAccessColumn($key)
695:     {
696:         if ($this->cache && is_array($this->accessedColumns)) {
697:             $this->accessedColumns[$key] = FALSE;
698:         }
699:     }
700: 
701: 
702:     /**
703:      * Returns if selection requeried for more columns.
704:      * @return bool
705:      */
706:     public function getDataRefreshed()
707:     {
708:         return $this->dataRefreshed;
709:     }
710: 
711: 
712:     /********************* manipulation ****************d*g**/
713: 
714: 
715:     /**
716:      * Inserts row in a table.
717:      * @param  array|\Traversable|Selection array($column => $value)|\Traversable|Selection for INSERT ... SELECT
718:      * @return IRow|int|bool Returns IRow or number of affected rows for Selection or table without primary key
719:      */
720:     public function insert($data)
721:     {
722:         if ($data instanceof Selection) {
723:             $data = new Nette\Database\SqlLiteral($data->getSql(), $data->getSqlBuilder()->getParameters());
724: 
725:         } elseif ($data instanceof \Traversable) {
726:             $data = iterator_to_array($data);
727:         }
728: 
729:         $return = $this->connection->query($this->sqlBuilder->buildInsertQuery(), $data);
730:         $this->loadRefCache();
731: 
732:         if ($data instanceof Nette\Database\SqlLiteral || $this->primary === NULL) {
733:             unset($this->refCache['referencing'][$this->getGeneralCacheKey()][$this->getSpecificCacheKey()]);
734:             return $return->getRowCount();
735:         }
736: 
737:         $primaryKey = $this->connection->getInsertId($this->getPrimarySequence());
738:         if ($primaryKey === FALSE) {
739:             unset($this->refCache['referencing'][$this->getGeneralCacheKey()][$this->getSpecificCacheKey()]);
740:             return $return->getRowCount();
741:         }
742: 
743:         if (is_array($this->getPrimary())) {
744:             $primaryKey = array();
745: 
746:             foreach ((array) $this->getPrimary() as $key) {
747:                 if (!isset($data[$key])) {
748:                     return $data;
749:                 }
750: 
751:                 $primaryKey[$key] = $data[$key];
752:             }
753:             if (count($primaryKey) === 1) {
754:                 $primaryKey = reset($primaryKey);
755:             }
756:         }
757: 
758:         $row = $this->createSelectionInstance()
759:             ->select('*')
760:             ->wherePrimary($primaryKey)
761:             ->fetch();
762: 
763:         if ($this->rows !== NULL) {
764:             if ($signature = $row->getSignature(FALSE)) {
765:                 $this->rows[$signature] = $row;
766:                 $this->data[$signature] = $row;
767:             } else {
768:                 $this->rows[] = $row;
769:                 $this->data[] = $row;
770:             }
771:         }
772: 
773:         return $row;
774:     }
775: 
776: 
777:     /**
778:      * Updates all rows in result set.
779:      * Joins in UPDATE are supported only in MySQL
780:      * @param  array|\Traversable ($column => $value)
781:      * @return int number of affected rows
782:      */
783:     public function update($data)
784:     {
785:         if ($data instanceof \Traversable) {
786:             $data = iterator_to_array($data);
787: 
788:         } elseif (!is_array($data)) {
789:             throw new Nette\InvalidArgumentException;
790:         }
791: 
792:         if (!$data) {
793:             return 0;
794:         }
795: 
796:         return $this->connection->queryArgs(
797:             $this->sqlBuilder->buildUpdateQuery(),
798:             array_merge(array($data), $this->sqlBuilder->getParameters())
799:         )->getRowCount();
800:     }
801: 
802: 
803:     /**
804:      * Deletes all rows in result set.
805:      * @return int number of affected rows
806:      */
807:     public function delete()
808:     {
809:         return $this->query($this->sqlBuilder->buildDeleteQuery())->getRowCount();
810:     }
811: 
812: 
813:     /********************* references ****************d*g**/
814: 
815: 
816:     /**
817:      * Returns referenced row.
818:      * @param  string
819:      * @param  string
820:      * @param  mixed   primary key to check for $table and $column references
821:      * @return Selection or array() if the row does not exist
822:      */
823:     public function getReferencedTable($table, $column, $checkPrimaryKey)
824:     {
825:         $referenced = & $this->refCache['referenced'][$this->getSpecificCacheKey()]["$table.$column"];
826:         $selection = & $referenced['selection'];
827:         $cacheKeys = & $referenced['cacheKeys'];
828:         if ($selection === NULL || !isset($cacheKeys[$checkPrimaryKey])) {
829:             $this->execute();
830:             $cacheKeys = array();
831:             foreach ($this->rows as $row) {
832:                 if ($row[$column] === NULL) {
833:                     continue;
834:                 }
835: 
836:                 $key = $row[$column];
837:                 $cacheKeys[$key] = TRUE;
838:             }
839: 
840:             if ($cacheKeys) {
841:                 $selection = $this->createSelectionInstance($table);
842:                 $selection->where($selection->getPrimary(), array_keys($cacheKeys));
843:             } else {
844:                 $selection = array();
845:             }
846:         }
847: 
848:         return $selection;
849:     }
850: 
851: 
852:     /**
853:      * Returns referencing rows.
854:      * @param  string
855:      * @param  string
856:      * @param  int primary key
857:      * @return GroupedSelection
858:      */
859:     public function getReferencingTable($table, $column, $active = NULL)
860:     {
861:         $prototype = & $this->refCache['referencingPrototype']["$table.$column"];
862:         if (!$prototype) {
863:             $prototype = $this->createGroupedSelectionInstance($table, $column);
864:             $prototype->where("$table.$column", array_keys((array) $this->rows));
865:         }
866: 
867:         $clone = clone $prototype;
868:         $clone->setActive($active);
869:         return $clone;
870:     }
871: 
872: 
873:     /********************* interface Iterator ****************d*g**/
874: 
875: 
876:     public function rewind()
877:     {
878:         $this->execute();
879:         $this->keys = array_keys($this->data);
880:         reset($this->keys);
881:     }
882: 
883: 
884:     /** @return IRow */
885:     public function current()
886:     {
887:         if (($key = current($this->keys)) !== FALSE) {
888:             return $this->data[$key];
889:         } else {
890:             return FALSE;
891:         }
892:     }
893: 
894: 
895:     /**
896:      * @return string row ID
897:      */
898:     public function key()
899:     {
900:         return current($this->keys);
901:     }
902: 
903: 
904:     public function next()
905:     {
906:         next($this->keys);
907:     }
908: 
909: 
910:     public function valid()
911:     {
912:         return current($this->keys) !== FALSE;
913:     }
914: 
915: 
916:     /********************* interface ArrayAccess ****************d*g**/
917: 
918: 
919:     /**
920:      * Mimic row.
921:      * @param  string row ID
922:      * @param  IRow
923:      * @return NULL
924:      */
925:     public function offsetSet($key, $value)
926:     {
927:         $this->execute();
928:         $this->rows[$key] = $value;
929:     }
930: 
931: 
932:     /**
933:      * Returns specified row.
934:      * @param  string row ID
935:      * @return IRow or NULL if there is no such row
936:      */
937:     public function offsetGet($key)
938:     {
939:         $this->execute();
940:         return $this->rows[$key];
941:     }
942: 
943: 
944:     /**
945:      * Tests if row exists.
946:      * @param  string row ID
947:      * @return bool
948:      */
949:     public function offsetExists($key)
950:     {
951:         $this->execute();
952:         return isset($this->rows[$key]);
953:     }
954: 
955: 
956:     /**
957:      * Removes row from result set.
958:      * @param  string row ID
959:      * @return NULL
960:      */
961:     public function offsetUnset($key)
962:     {
963:         $this->execute();
964:         unset($this->rows[$key], $this->data[$key]);
965:     }
966: 
967: }
968: 
Nette 2.1 API documentation generated by ApiGen 2.8.0