From 16497adb6aad7f8af3c7a18f681d1f4bb166cef6 Mon Sep 17 00:00:00 2001 From: doctrine Date: Wed, 19 Apr 2006 16:01:36 +0000 Subject: [PATCH] --- classes/Collection.class.php | 218 +++++++++++++++++------ classes/Collection/Batch.class.php | 27 +-- classes/Collection/Immediate.class.php | 15 +- classes/Collection/Lazy.class.php | 4 +- classes/Collection/Offset.class.php | 31 ++++ classes/Configurable.class.php | 9 +- classes/DQL/Parser.class.php | 48 +++-- classes/Doctrine.class.php | 29 ++- classes/Iterator.class.php | 101 +++++++++++ classes/Lib.class.php | 140 +++++++++++++++ classes/Manager.class.php | 1 + classes/Record.class.php | 10 +- classes/Session/Common.class.php | 12 +- classes/Table.class.php | 21 ++- tests/CollectionOffsetTestCase.class.php | 77 ++++++++ tests/CollectionTestCase.class.php | 30 ++-- tests/DQLParserTestCase.class.php | 5 +- tests/run.php | 14 +- 18 files changed, 642 insertions(+), 150 deletions(-) create mode 100644 classes/Collection/Offset.class.php create mode 100644 classes/Iterator.class.php create mode 100644 classes/Lib.class.php create mode 100644 tests/CollectionOffsetTestCase.class.php diff --git a/classes/Collection.class.php b/classes/Collection.class.php index ace6e1a4a..bc591a109 100644 --- a/classes/Collection.class.php +++ b/classes/Collection.class.php @@ -30,9 +30,13 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator */ protected $relation; /** - * @var boolean $expanded whether or not this collection has been expanded + * @var boolean $expandable whether or not this collection has been expanded */ - protected $expanded = false; + protected $expandable = true; + /** + * @var array $expanded + */ + protected $expanded = array(); /** * @var mixed $generator */ @@ -55,6 +59,12 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator public function getTable() { return $this->table; } + public function isExpanded($offset) { + return isset($this->expanded[$offset]); + } + public function isExpandable() { + return $this->expandable; + } /** * @param Doctrine_IndexGenerator $generator * @return void @@ -99,7 +109,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator $value = $record->get($relation->getLocal()); - foreach($this as $record) { + foreach($this->getNormalIterator() as $record) { if($value !== null) { $record->set($this->reference_field, $value); } else { @@ -117,67 +127,121 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator /** * @return boolean */ - public function expand($i = null) { - if( ! isset($this->reference)) - return false; + public function expand($key) { + $where = array(); + $params = array(); + $limit = null; + $offset = null; - $id = $this->reference->getID(); + switch(get_class($this)): + case "Doctrine_Collection_Offset": + $limit = $this->getLimit(); + $offset = (floor($key / $limit) * $limit); - if(empty($id)) - return false; + if( ! $this->expandable && isset($this->expanded[$offset])) + return false; + + $fields = implode(", ",$this->table->getColumnNames()); + break; + default: + if( ! $this->expandable) + return false; - foreach($this->data as $v) { - switch(gettype($v)): - case "array": - $ids[] = $v['id']; - break; - case "object": - $id = $v->getID(); - if( ! empty($id)) - $ids[] = $id; - break; - endswitch; - } + if( ! isset($this->reference)) + return false; - if($this instanceof Doctrine_Collection_Immediate) { - $fields = implode(", ",$this->table->getColumnNames()); - } else { - $fields = implode(", ",$this->table->getPrimaryKeys()); - } + $id = $this->reference->getID(); - if($this->relation instanceof Doctrine_ForeignKey) { - $str = ""; - $params = array($this->reference->getID()); + if(empty($id)) + return false; - if( ! empty($ids)) { - $str = " && id NOT IN (".substr(str_repeat("?, ",count($ids)),0,-2).")"; - $params = array_merge($params,$ids); + switch(get_class($this)): + case "Doctrine_Collection_Immediate": + $fields = implode(", ",$this->table->getColumnNames()); + break; + default: + $fields = implode(", ",$this->table->getPrimaryKeys()); + endswitch; + + + endswitch; + + if(isset($this->relation)) { + if($this->relation instanceof Doctrine_ForeignKey) { + $params = array($this->reference->getID()); + $where[] = $this->reference_field." = ?"; + + if( ! isset($offset)) { + $ids = $this->getPrimaryKeys(); + + if( ! empty($ids)) { + $where[] = "id NOT IN (".substr(str_repeat("?, ",count($ids)),0,-2).")"; + $params = array_merge($params,$ids); + } + + $this->expandable = false; + } + + + } elseif($this->relation instanceof Doctrine_Association) { + + $asf = $fk->getAssociationFactory(); + $query = "SELECT ".$foreign." FROM ".$asf->getTableName()." WHERE ".$local."=".$this->getID(); + + $table = $fk->getTable(); + $graph = new Doctrine_DQL_Parser($table->getSession()); + + $q = "FROM ".$table->getComponentName()." WHERE ".$table->getComponentName().".id IN ($query)"; + } - $str = " WHERE ".$this->reference_field." = ?".$str; - $query = "SELECT ".$fields." FROM ".$this->table->getTableName().$str; - $coll = $this->table->execute($query,$params); - - } elseif($this->relation instanceof Doctrine_Association) { - - $asf = $fk->getAssociationFactory(); - $query = "SELECT ".$foreign." FROM ".$asf->getTableName()." WHERE ".$local."=".$this->getID(); - - $table = $fk->getTable(); - $graph = new Doctrine_DQL_Parser($table->getSession()); - - $q = "FROM ".$table->getComponentName()." WHERE ".$table->getComponentName().".id IN ($query)"; } - foreach($coll as $record) { - if(isset($this->reference_field)) - $record->rawSet($this->reference_field, $this->reference); - - $this->reference->addReference($record); + $query = "SELECT ".$fields." FROM ".$this->table->getTableName(); + + // apply column aggregation inheritance + foreach($this->table->getInheritanceMap() as $k => $v) { + $where[] = $k." = ?"; + $params[] = $v; + } + if( ! empty($where)) { + $query .= " WHERE ".implode(" AND ",$where); } - return true; + $params = array_merge($params, array_values($this->table->getInheritanceMap())); + + $coll = $this->table->execute($query, $params, $limit, $offset); + + if( ! isset($offset)) { + foreach($coll as $record) { + if(isset($this->reference_field)) + $record->rawSet($this->reference_field,$this->reference); + + $this->reference->addReference($record); + } + } else { + $i = $offset; + + foreach($coll as $record) { + if(isset($this->reference)) { + $this->reference->addReference($record,$i); + } else + $this->data[$i] = $record; + + $i++; + } + + $this->expanded[$offset] = true; + + // check if the fetched collection's record count is smaller + // than the query limit, if so this collection has been expanded to its max size + + if(count($coll) < $limit) { + $this->expandable = false; + } + } + + return $coll; } - /** * @return boolean */ @@ -199,11 +263,11 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } /** * @param mixed $key - * @return object Doctrine_Record return a specified dao + * @return object Doctrine_Record return a specified record */ public function get($key) { if( ! isset($this->data[$key])) { - $this->expand(); + $this->expand($key); if( ! isset($this->data[$key])) $this->data[$key] = $this->table->create(); @@ -227,8 +291,12 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator */ public function getPrimaryKeys() { $list = array(); - foreach($this->data[$key] as $record): - $list[] = $record->getID(); + foreach($this->data as $record): + if(is_array($record) && isset($record['id'])) { + $list[] = $record['id']; + } else { + $list[] = $record->getID(); + } endforeach; return $list; } @@ -275,6 +343,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator return false; $this->data[$key] = $record; + return true; } if(isset($this->generator)) { @@ -282,8 +351,45 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator $this->data[$key] = $record; } else $this->data[] = $record; + return true; } + /** + * @param Doctrine_DQL_Parser $graph + * @param integer $key + */ + public function populate(Doctrine_DQL_Parser $graph) { + $name = $this->table->getComponentName(); + + if($this instanceof Doctrine_Collection_Immediate || + $this instanceof Doctrine_Collection_Offset) { + + $data = $graph->getData($name); + if(is_array($data)) { + foreach($data as $k=>$v): + $this->table->setData($v); + $this->add($this->table->getRecord()); + endforeach; + } + } elseif($this instanceof Doctrine_Collection_Batch) { + $this->data = $graph->getData($name); + + if(isset($this->generator)) { + foreach($this->data as $k => $v) { + $record = $this->get($k); + $i = $this->generator->getIndex($record); + $this->data[$i] = $record; + unset($this->data[$k]); + } + } + } + } + /** + * @return Doctrine_Iterator_Normal + */ + public function getNormalIterator() { + return new Doctrine_Iterator_Normal($this); + } /** * save * saves all records diff --git a/classes/Collection/Batch.class.php b/classes/Collection/Batch.class.php index ccc4ac2d0..16b090c5f 100644 --- a/classes/Collection/Batch.class.php +++ b/classes/Collection/Batch.class.php @@ -1,8 +1,8 @@ getTable($key)); - - $this->data = $graph->getData($key); - if( ! is_array($this->data)) - $this->data = array(); - - if(isset($this->generator)) { - foreach($this->data as $k => $v) { - $record = $this->get($k); - $i = $this->generator->getIndex($record); - $this->data[$i] = $record; - unset($this->data[$k]); - } - } + public function __construct(Doctrine_Table $table) { + parent::__construct($table); $this->batchSize = $this->getTable()->getAttribute(Doctrine::ATTR_BATCH_SIZE); } @@ -146,7 +133,7 @@ class Doctrine_Collection_Batch extends Doctrine_Collection { endswitch; } else { - $this->expand(); + $this->expand($key); if( ! isset($this->data[$key])) $this->data[$key] = $this->table->create(); @@ -161,10 +148,10 @@ class Doctrine_Collection_Batch extends Doctrine_Collection { return $this->data[$key]; } /** - * @return Doctrine_BatchIterator + * @return Doctrine_Iterator */ public function getIterator() { - return new Doctrine_BatchIterator($this); + return new Doctrine_Iterator_Expandable($this); } } ?> diff --git a/classes/Collection/Immediate.class.php b/classes/Collection/Immediate.class.php index 2858e7d33..6ff1d1208 100644 --- a/classes/Collection/Immediate.class.php +++ b/classes/Collection/Immediate.class.php @@ -12,19 +12,8 @@ class Doctrine_Collection_Immediate extends Doctrine_Collection { * @param Doctrine_DQL_Parser $graph * @param integer $key */ - public function __construct(Doctrine_DQL_Parser $graph,$key) { - $table = $graph->getTable($key); - parent::__construct($table); - - $name = $table->getComponentName(); - $data = $graph->getData($name); - if(is_array($data)) { - foreach($data as $k=>$v): - $table->setData($v); - $this->add($this->table->getRecord()); - endforeach; - } - + public function __construct(Doctrine_Table $table) { + parent::__construct($table); } } ?> diff --git a/classes/Collection/Lazy.class.php b/classes/Collection/Lazy.class.php index f4e8efaae..004a5f842 100644 --- a/classes/Collection/Lazy.class.php +++ b/classes/Collection/Lazy.class.php @@ -10,9 +10,9 @@ class Doctrine_Collection_Lazy extends Doctrine_Collection_Batch { * @param Doctrine_DQL_Parser $graph * @param string $key */ - public function __construct(Doctrine_DQL_Parser $graph,$key) { + public function __construct(Doctrine_Table $table) { + parent::__construct($table); parent::setBatchSize(1); - parent::__construct($graph,$key); } } ?> diff --git a/classes/Collection/Offset.class.php b/classes/Collection/Offset.class.php new file mode 100644 index 000000000..4d89b7288 --- /dev/null +++ b/classes/Collection/Offset.class.php @@ -0,0 +1,31 @@ +limit = $table->getAttribute(Doctrine::ATTR_COLL_LIMIT); + } + /** + * @return integer + */ + public function getLimit() { + return $this->limit; + } + /** + * @return Doctrine_Iterator_Offset + */ + public function getIterator() { + return new Doctrine_Iterator_Expandable($this); + } +} +?> diff --git a/classes/Configurable.class.php b/classes/Configurable.class.php index 77bad322e..2d370f3fd 100644 --- a/classes/Configurable.class.php +++ b/classes/Configurable.class.php @@ -56,7 +56,7 @@ abstract class Doctrine_Configurable { break; case Doctrine::ATTR_FETCHMODE: if($value < 0) - throw new Doctrine_Exception("Unknown fetchmode. Fetchmode should be an integer between 0 and 2. See Doctrine::FETCH_* constants."); + throw new Doctrine_Exception("Unknown fetchmode. See Doctrine::FETCH_* constants."); break; case Doctrine::ATTR_LISTENER: $this->setEventListener($value); @@ -87,6 +87,11 @@ abstract class Doctrine_Configurable { case Doctrine::ATTR_CREATE_TABLES: $value = (bool) $value; break; + case Doctrine::ATTR_COLL_LIMIT: + if($value < 1) { + throw new Doctrine_Exception("Collection limit should be a value greater than or equal to 1."); + } + break; case Doctrine::ATTR_COLL_KEY: if( ! ($this instanceof Doctrine_Table)) throw new Doctrine_Exception("This attribute can only be set at table level."); @@ -124,7 +129,7 @@ abstract class Doctrine_Configurable { final public function getAttribute($attribute) { $attribute = (int) $attribute; - if($attribute < 1 || $attribute > 15) + if($attribute < 1 || $attribute > 16) throw new InvalidKeyException(); if( ! isset($this->attributes[$attribute])) { diff --git a/classes/DQL/Parser.class.php b/classes/DQL/Parser.class.php index e9e10220c..72ec588bf 100644 --- a/classes/DQL/Parser.class.php +++ b/classes/DQL/Parser.class.php @@ -25,7 +25,11 @@ class Doctrine_DQL_Parser { /** * @var array $tablenames an array containing all the tables used in the query */ - private $tablenames = array(); + private $tablenames = array(); + /** + * @var array $collections an array containing all collections this parser has created/will create + */ + private $collections = array(); /** * @var array $from query FROM parts */ @@ -91,6 +95,9 @@ class Doctrine_DQL_Parser { $this->aggregate = false; $this->data = array(); $this->connectors = array(); + $this->collections = array(); + $this->paths = array(); + $this->joined = array(); } /** * loadFields -- this method loads fields for a given factory and @@ -104,16 +111,22 @@ class Doctrine_DQL_Parser { * @return void */ private function loadFields(Doctrine_Table $table,$fetchmode) { + $name = $table->getComponentName(); + switch($fetchmode): + case Doctrine::FETCH_OFFSET: + $this->limit = $table->getAttribute(Doctrine::ATTR_COLL_LIMIT); case Doctrine::FETCH_IMMEDIATE: $names = $table->getColumnNames(); break; + case Doctrine::FETCH_LAZY_OFFSET: + $this->limit = $table->getAttribute(Doctrine::ATTR_COLL_LIMIT); case Doctrine::FETCH_LAZY: case Doctrine::FETCH_BATCH: $names = $table->getPrimaryKeys(); break; default: - throw new InvalidFetchModeException(); + throw new Doctrine_Exception("Unknown fetchmode."); endswitch; $cname = $table->getComponentName(); $this->fetchModes[$cname] = $fetchmode; @@ -276,7 +289,7 @@ class Doctrine_DQL_Parser { * @return array the data row for the specified factory */ final public function getData($key) { - if(isset($this->data[$key])) + if(isset($this->data[$key]) && is_array($this->data[$key])) return $this->data[$key]; return array(); @@ -406,20 +419,26 @@ class Doctrine_DQL_Parser { * @param integer $index */ private function getCollection($name) { + $table = $this->session->getTable($name); switch($this->fetchModes[$name]): - case 0: - $coll = new Doctrine_Collection_Immediate($this,$name); + case Doctrine::FETCH_BATCH: + $coll = new Doctrine_Collection_Batch($table); break; - case 1: - $coll = new Doctrine_Collection_Batch($this,$name); + case Doctrine::FETCH_LAZY: + $coll = new Doctrine_Collection_Lazy($table); break; - case 2: - $coll = new Doctrine_Collection_Lazy($this,$name); + case Doctrine::FETCH_OFFSET: + $coll = new Doctrine_Collection_Offset($table); + break; + case Doctrine::FETCH_IMMEDIATE: + $coll = new Doctrine_Collection_Immediate($table); + break; + case Doctrine::FETCH_LAZY_OFFSET: + $coll = new Doctrine_Collection_LazyOffset($table); break; - default: - throw new Exception("Unknown fetchmode"); endswitch; + $coll->populate($this); return $coll; } /** @@ -555,6 +574,13 @@ class Doctrine_DQL_Parser { case "lazy": $fetchmode = Doctrine::FETCH_LAZY; break; + case "o": + case "offset": + $fetchmode = Doctrine::FETCH_OFFSET; + break; + case "lo": + case "lazyoffset": + $fetchmode = Doctrine::FETCH_LAZYOFFSET; default: throw new DQLException("Unknown fetchmode '$e[1]'. The availible fetchmodes are 'i', 'b' and 'l'."); endswitch; diff --git a/classes/Doctrine.class.php b/classes/Doctrine.class.php index 4fd7dbfac..9cf658930 100644 --- a/classes/Doctrine.class.php +++ b/classes/Doctrine.class.php @@ -120,6 +120,10 @@ final class Doctrine { * collection key attribute */ const ATTR_COLL_KEY = 15; + /** + * collection limit attribute + */ + const ATTR_COLL_LIMIT = 16; @@ -146,18 +150,27 @@ final class Doctrine { * IMMEDIATE FETCHING * mode for immediate fetching */ - const FETCH_IMMEDIATE = 0; + const FETCH_IMMEDIATE = 0; /** * BATCH FETCHING * mode for batch fetching */ - const FETCH_BATCH = 1; + const FETCH_BATCH = 1; /** * LAZY FETCHING * mode for lazy fetching */ - const FETCH_LAZY = 2; - + const FETCH_LAZY = 2; + /** + * LAZY FETCHING + * mode for offset fetching + */ + const FETCH_OFFSET = 3; + /** + * LAZY OFFSET FETCHING + * mode for lazy offset fetching + */ + const FETCH_LAZY_OFFSET = 4; /** * LOCKMODE CONSTANTS */ @@ -165,11 +178,11 @@ final class Doctrine { /** * mode for optimistic locking */ - const LOCK_OPTIMISTIC = 0; + const LOCK_OPTIMISTIC = 0; /** * mode for pessimistic locking */ - const LOCK_PESSIMISTIC = 1; + const LOCK_PESSIMISTIC = 1; /** * PRIMARY KEY TYPE CONSTANTS @@ -178,11 +191,11 @@ final class Doctrine { /** * auto-incremented/(sequence updated) primary key */ - const INCREMENT_KEY = 0; + const INCREMENT_KEY = 0; /** * unique key */ - const UNIQUE_KEY = 1; + const UNIQUE_KEY = 1; /** diff --git a/classes/Iterator.class.php b/classes/Iterator.class.php new file mode 100644 index 000000000..632b88a81 --- /dev/null +++ b/classes/Iterator.class.php @@ -0,0 +1,101 @@ +collection = $collection; + $this->keys = $this->collection->getKeys(); + $this->count = $this->collection->count(); + } + /** + * @return void + */ + public function rewind() { + $this->index = 0; + $i = $this->index; + if(isset($this->keys[$i])) + $this->key = $this->keys[$i]; + } + + /** + * @return integer the current key + */ + public function key() { + return $this->key; + } + /** + * @return Doctrine_Record the current record + */ + public function current() { + return $this->collection->get($this->key); + } + /** + * @return void + */ + public function next() { + $this->index++; + $i = $this->index; + if(isset($this->keys[$i])) + $this->key = $this->keys[$i]; + } +} +class Doctrine_Iterator_Normal extends Doctrine_Iterator { + /** + * @return boolean whether or not the iteration will continue + */ + public function valid() { + return ($this->index < $this->count); + } +} +class Doctrine_Iterator_Offset extends Doctrine_Iterator { + public function valid() { } +} +class Doctrine_Iterator_Expandable extends Doctrine_Iterator { + public function valid() { + if($this->index < $this->count) + return true; + elseif($this->index == $this->count) { + + $coll = $this->collection->expand($this->index); + + if($coll instanceof Doctrine_Collection) { + $count = count($coll); + if($count > 0) { + $this->keys = array_merge($this->keys, $coll->getKeys()); + $this->count += $count; + return true; + } + } + + return false; + } + } +} +?> diff --git a/classes/Lib.class.php b/classes/Lib.class.php new file mode 100644 index 000000000..15d866ada --- /dev/null +++ b/classes/Lib.class.php @@ -0,0 +1,140 @@ +"; + $r[] = "Component : ".$record->getTable()->getComponentName(); + $r[] = "ID : ".$record->getID(); + $r[] = "References : ".count($record->getReferences()); + $r[] = "State : ".Doctrine_Lib::getRecordStateAsString($record->getState()); + $r[] = "OID : ".$record->getOID(); + $r[] = ""; + return implode("\n",$r)."
"; + } + /** + * getStateAsString + * returns a given session state as string + * @param integer $state session state + */ + public static function getSessionStateAsString($state) { + switch($state): + case Doctrine_Session::STATE_OPEN: + return "open"; + break; + case Doctrine_Session::STATE_CLOSED: + return "closed"; + break; + case Doctrine_Session::STATE_BUSY: + return "busy"; + break; + case Doctrine_Session::STATE_ACTIVE: + return "active"; + break; + endswitch; + } + /** + * returns a string representation of Doctrine_Session object + * @param Doctrine_Session $session + * @return string + */ + public function getSessionAsString(Doctrine_Session $session) { + $r[] = "
";
+        $r[] = "Doctrine_Session object";
+        $r[] = "State               : ".Doctrine_Session::getStateAsString($session->getState());
+        $r[] = "Open Transactions   : ".$session->getTransactionLevel();
+        $r[] = "Open Factories      : ".$session->count();
+        $sum = 0;
+        $rsum = 0;
+        $csum = 0;
+        foreach($session->getTables() as $objTable) {
+            if($objTable->getCache() instanceof Doctrine_Cache_File) {
+                $sum += array_sum($objTable->getCache()->getStats());
+                $rsum += $objTable->getRepository()->count();
+                $csum += $objTable->getCache()->count();
+            }
+        }
+        $r[] = "Cache Hits          : ".$sum." hits ";
+        $r[] = "Cache               : ".$csum." objects ";
+
+        $r[] = "Repositories        : ".$rsum." objects ";
+        $queries = false;
+        if($session->getDBH() instanceof Doctrine_DB) {
+            $handler = "Doctrine Database Handler";
+            $queries = count($session->getDBH()->getQueries());
+            $sum     = array_sum($session->getDBH()->getExecTimes());
+        } elseif($session->getDBH() instanceof PDO) {
+            $handler = "PHP Native PDO Driver";
+        } else
+            $handler = "Unknown Database Handler";
+
+        $r[] = "DB Handler          : ".$handler;
+        if($queries) {
+            $r[] = "Executed Queries    : ".$queries;
+            $r[] = "Sum of Exec Times   : ".$sum;
+        }
+
+        $r[] = "
"; + return implode("\n",$r)."
"; + } + /** + * returns a string representation of Doctrine_Table object + * @param Doctrine_Table $table + * @return string + */ + public function getTableAsString(Doctrine_Table $table) { + $r[] = "
";
+        $r[] = "Component   : ".$this->getComponentName();
+        $r[] = "Table       : ".$this->getTableName();
+        $r[] = "Repository  : ".$this->getRepository()->count()." objects";
+        if($table->getCache() instanceof Doctrine_Cache_File) {
+            $r[] = "Cache       : ".$this->getCache()->count()." objects";
+            $r[] = "Cache hits  : ".array_sum($this->getCache()->getStats())." hits";
+        }
+        $r[] = "
"; + return implode("\n",$r)."
"; + } + /** + * returns a string representation of Doctrine_Collection object + * @param Doctrine_Collection $collection + * @return string + */ + public function getCollectionAsString(Doctrine_Collection $collection) { + $r[] = "
";
+        $r[] = get_class($collection);
+
+        foreach($collection as $key => $record) {
+            $r[] = "Key : ".$key." ID : ".$record->getID();
+        }
+        $r[] = "
"; + return implode("\n",$r); + } +} +?> diff --git a/classes/Manager.class.php b/classes/Manager.class.php index 8e982b94b..521be18e8 100644 --- a/classes/Manager.class.php +++ b/classes/Manager.class.php @@ -47,6 +47,7 @@ class Doctrine_Manager extends Doctrine_Configurable implements Countable, Itera Doctrine::ATTR_CACHE_SIZE => 100, Doctrine::ATTR_CACHE => Doctrine::CACHE_NONE, Doctrine::ATTR_BATCH_SIZE => 5, + Doctrine::ATTR_COLL_LIMIT => 5, Doctrine::ATTR_LISTENER => new EmptyEventListener(), Doctrine::ATTR_PK_COLUMNS => array("id"), Doctrine::ATTR_PK_TYPE => Doctrine::INCREMENT_KEY, diff --git a/classes/Record.class.php b/classes/Record.class.php index 6ec4d48d0..c4ef6dd81 100644 --- a/classes/Record.class.php +++ b/classes/Record.class.php @@ -777,10 +777,10 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite /** * addReference */ - public function addReference(Doctrine_Record $record) { + public function addReference(Doctrine_Record $record, $key = null) { $name = $record->getTable()->getComponentName(); - $this->references[$name]->add($record); - $this->originals[$name]->add($record); + $this->references[$name]->add($record, $key); + $this->originals[$name]->add($record, $key); } /** * getReferences @@ -823,7 +823,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite // ONE-TO-MANY $this->references[$name]->setReference($this,$fk); } - $this->originals[$name] = new Doctrine_Collection($table); + $this->originals[$name] = new Doctrine_Collection($table); } break; case Doctrine_Record::STATE_DIRTY: @@ -875,7 +875,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $coll = $graph->query($query, array($this->getID())); $this->references[$name] = $coll; - $this->originals[$name] = clone $coll; + $this->originals[$name] = clone $coll; } endswitch; diff --git a/classes/Session/Common.class.php b/classes/Session/Common.class.php index f6111422d..260d94a8b 100644 --- a/classes/Session/Common.class.php +++ b/classes/Session/Common.class.php @@ -4,13 +4,13 @@ */ class Doctrine_Session_Common extends Doctrine_Session { public function modifyLimitQuery($query,$limit = null,$offset = null) { - if(isset($limit)) + if(isset($limit) && isset($offset)) { + $query .= " LIMIT ".$limit." OFFSET ".$offset; + } elseif(isset($limit) && ! isset($offset)) { $query .= " LIMIT ".$limit; - else - $query .= " LIMIT 99999999999999"; - - if(isset($offset)) - $query .= " OFFSET ".$offset; + } elseif( ! isset($limit) && isset($offset)) { + $query .= " LIMIT 999999999999 OFFSET ".$offset; + } return $query; } diff --git a/classes/Table.class.php b/classes/Table.class.php index 52781dd90..ec80358a2 100644 --- a/classes/Table.class.php +++ b/classes/Table.class.php @@ -448,7 +448,7 @@ class Doctrine_Table extends Doctrine_Configurable { * @param $array an array where keys are field names and values representing field values * @return Doctrine_Record A new Data Access Object. Uses an sql insert statement when saved */ - final public function create(array $array = array()) { + public function create(array $array = array()) { $this->data = $array; $this->isNewEntry = true; $record = $this->getRecord(); @@ -460,7 +460,7 @@ class Doctrine_Table extends Doctrine_Configurable { * @throws Doctrine_Find_Exception * @return Doctrine_Record a record for given database identifier */ - final public function find($id = null) { + public function find($id = null) { if($id !== null) { $query = $this->query." WHERE ".implode(" = ? AND ",$this->primaryKeys)." = ?"; $query = $this->applyInheritance($query); @@ -494,7 +494,7 @@ class Doctrine_Table extends Doctrine_Configurable { * findAll * @return Doctrine_Collection a collection of all data access objects */ - final public function findAll() { + public function findAll() { $graph = new Doctrine_DQL_Parser($this->session); $users = $graph->query("FROM ".$this->name); return $users; @@ -503,7 +503,7 @@ class Doctrine_Table extends Doctrine_Configurable { * findBySql * @return Doctrine_Collection a collection of data access objects */ - final public function findBySql($sql) { + public function findBySql($sql) { $graph = new Doctrine_DQL_Parser($this->session); $users = $graph->query("FROM ".$this->name." WHERE ".$sql); return $users; @@ -553,10 +553,15 @@ class Doctrine_Table extends Doctrine_Configurable { /** * execute */ - public function execute($query, array $array = array()) { - $coll = new Doctrine_Collection($this); - $stmt = $this->session->getDBH()->prepare($query); - $stmt->execute($array); + public function execute($query, array $array = array(), $limit = null, $offset = null) { + $coll = new Doctrine_Collection($this); + $query = $this->session->modifyLimitQuery($query,$limit,$offset); + if( ! empty($array)) { + $stmt = $this->session->getDBH()->prepare($query); + $stmt->execute($array); + } else { + $stmt = $this->session->getDBH()->query($query); + } $data = $stmt->fetchAll(PDO::FETCH_ASSOC); $stmt->closeCursor(); diff --git a/tests/CollectionOffsetTestCase.class.php b/tests/CollectionOffsetTestCase.class.php new file mode 100644 index 000000000..3213a06cf --- /dev/null +++ b/tests/CollectionOffsetTestCase.class.php @@ -0,0 +1,77 @@ +session->query("FROM User-o"); + $this->assertTrue($users instanceof Doctrine_Collection_Offset); + + $this->assertEqual(count($users), 5); + $users[5]; + + $this->assertEqual($users[5]->getState(), Doctrine_Record::STATE_CLEAN); + $users[5]; + + $this->session->setAttribute(Doctrine::ATTR_COLL_LIMIT, 3); + + $users = $this->session->query("FROM User-o"); + $this->assertEqual(count($users), 3); + $this->assertEqual($users[0]->getState(), Doctrine_Record::STATE_CLEAN); + $this->assertEqual($users[1]->getState(), Doctrine_Record::STATE_CLEAN); + $this->assertEqual($users[2]->getState(), Doctrine_Record::STATE_CLEAN); + // indexes 0,1,2 in use + + $users[7]; + $this->assertEqual(count($users), 5); + $this->assertFalse($users->contains(3)); + $this->assertFalse($users->contains(4)); + $this->assertFalse($users->contains(5)); + $this->assertTrue($users->contains(6)); + $this->assertTrue($users->contains(7)); + + $this->assertEqual($users[6]->getState(), Doctrine_Record::STATE_CLEAN); + $this->assertEqual($users[7]->getState(), Doctrine_Record::STATE_CLEAN); + + + $users[5]; + $this->assertEqual(count($users), 8); + $this->assertEqual($users[3]->getState(), Doctrine_Record::STATE_CLEAN); + $this->assertEqual($users[4]->getState(), Doctrine_Record::STATE_CLEAN); + $this->assertEqual($users[5]->getState(), Doctrine_Record::STATE_CLEAN); + + + $this->session->setAttribute(Doctrine::ATTR_COLL_LIMIT, 1); + $users = $this->session->query("FROM User-b, User.Phonenumber-o WHERE User.id = 5"); + + $this->assertEqual(count($users), 1); + + $coll = $users[0]->Phonenumber; + $this->assertEqual(count($coll), 1); + $coll[1]; + + $this->assertEqual(count($coll), 2); + $this->assertEqual($coll[1]->phonenumber, "456 456"); + + } + + public function testGetIterator() { + $this->session->setAttribute(Doctrine::ATTR_COLL_LIMIT, 4); + $coll = $this->session->query("FROM User-o"); + + foreach($coll as $user) { + } + $this->assertEqual($coll->count(), 8); + $this->assertEqual($coll[3]->getState(), Doctrine_Record::STATE_CLEAN); + $this->assertEqual($coll[6]->getState(), Doctrine_Record::STATE_CLEAN); + + $this->session->setAttribute(Doctrine::ATTR_COLL_LIMIT, 3); + + $coll = $this->session->query("FROM User-o"); + + foreach($coll as $user) { + } + $this->assertEqual($coll->count(), 8); + $this->assertEqual($coll[3]->getState(), Doctrine_Record::STATE_CLEAN); + $this->assertEqual($coll[6]->getState(), Doctrine_Record::STATE_CLEAN); + } +} +?> diff --git a/tests/CollectionTestCase.class.php b/tests/CollectionTestCase.class.php index f0cb3c7c5..bef7e954f 100644 --- a/tests/CollectionTestCase.class.php +++ b/tests/CollectionTestCase.class.php @@ -6,46 +6,52 @@ class Doctrine_CollectionTestCase extends Doctrine_UnitTestCase { $this->assertEqual($coll->count(),1); $coll->add(new User()); $this->assertTrue($coll->count(),2); - + $this->assertEqual($coll->getKeys(), array(0,1)); - + $coll[2] = new User(); - + $this->assertTrue($coll->count(),3); $this->assertEqual($coll->getKeys(), array(0,1,2)); } + public function testExpand() { - $users = $this->session->query("FROM User, User.Phonenumber-l WHERE User.Phonenumber.phonenumber LIKE '%123%'"); - - $this->assertTrue($users instanceof Doctrine_Collection); + $users = $this->session->query("FROM User-b, User.Phonenumber-l WHERE User.Phonenumber.phonenumber LIKE '%123%'"); + + $this->assertTrue($users instanceof Doctrine_Collection_Batch); $this->assertTrue($users[1] instanceof User); - + $this->assertTrue($users[1]->Phonenumber instanceof Doctrine_Collection_Lazy); $data = $users[1]->Phonenumber->getData(); $coll = $users[1]->Phonenumber; $this->assertEqual(count($data), 1); - + + foreach($coll as $record) { + $record->phonenumber; + } + $coll[1]; - + $this->assertEqual(count($coll), 3); - + $this->assertTrue($coll[2]->getState() == Doctrine_Record::STATE_PROXY); $generator = new Doctrine_IndexGenerator("id"); $coll->setGenerator($generator); $generator = $coll->getGenerator(); $this->assertEqual($generator->getIndex($this->old), 4); + } public function testGenerator() { $generator = new Doctrine_IndexGenerator("name"); $coll = new Doctrine_Collection($this->objTable); $coll->setGenerator($generator); - + $user = new User(); $user->name = "name"; $coll->add($user); - + $this->assertEqual($coll["name"], $user); diff --git a/tests/DQLParserTestCase.class.php b/tests/DQLParserTestCase.class.php index 4f8ae19d9..f5f1ae85f 100644 --- a/tests/DQLParserTestCase.class.php +++ b/tests/DQLParserTestCase.class.php @@ -3,8 +3,6 @@ require_once("UnitTestCase.class.php"); class Doctrine_DQL_ParserTestCase extends Doctrine_UnitTestCase { public function testLimit() { - - $graph = new Doctrine_DQL_Parser($this->session); $coll = $graph->query("FROM User LIMIT 3"); $this->assertEqual($graph->getLimit(), 3); @@ -26,7 +24,7 @@ class Doctrine_DQL_ParserTestCase extends Doctrine_UnitTestCase { $graph = new Doctrine_DQL_Parser($this->session); $this->graph = $graph; - + $user = $this->objTable->find(5); @@ -84,7 +82,6 @@ class Doctrine_DQL_ParserTestCase extends Doctrine_UnitTestCase { //$this->assertEqual($users[0]->Group[2]->name, "Terminators"); //$this->assertEqual(count($users[0]->Group), 3); - $this->clearCache(); $users = $graph->query("FROM User-b, User.Phonenumber-l WHERE User.Phonenumber.phonenumber LIKE '%123%'"); diff --git a/tests/run.php b/tests/run.php index ac275282c..fdf294d2d 100644 --- a/tests/run.php +++ b/tests/run.php @@ -14,6 +14,7 @@ require_once("ValidatorTestCase.class.php"); require_once("CollectionTestCase.class.php"); require_once("CacheSqliteTestCase.class.php"); +require_once("CollectionOffsetTestCase.class.php"); require_once("SenseiTestCase.class.php"); @@ -25,7 +26,7 @@ $test = new GroupTest("Doctrine Framework Unit Tests"); - +/** $test->addTestCase(new Doctrine_RecordTestCase()); $test->addTestCase(new Doctrine_SessionTestCase()); @@ -45,10 +46,17 @@ $test->addTestCase(new Doctrine_BatchIteratorTestCase()); $test->addTestCase(new Doctrine_DQL_ParserTestCase()); -$test->addTestCase(new Doctrine_CollectionTestCase()); $test->addTestCase(new Doctrine_ConfigurableTestCase()); -$test->addTestCase(new Sensei_UnitTestCase()); +$test->addTestCase(new Doctrine_CollectionTestCase()); + + + + + + */ +$test->addTestCase(new Doctrine_Collection_OffsetTestCase()); +//$test->addTestCase(new Sensei_UnitTestCase()); //$test->addTestCase(new Doctrine_Cache_FileTestCase());