[limb-svn] r6691 - in 3.x/trunk/limb/active_record: . src tests/cases tests/cases/.fixture

svn at limb-project.com svn at limb-project.com
Tue Jan 15 17:55:59 MSK 2008


Author: serega
Date: 2008-01-15 17:55:59 +0300 (Tue, 15 Jan 2008)
New Revision: 6691
URL: http://fisheye.limb-project.com/changelog/limb/?cs=6691

Added:
   3.x/trunk/limb/active_record/src/lmbARQuery.class.php
   3.x/trunk/limb/active_record/src/lmbARRecordSetAttachDecorator.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARBaseTestCase.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARQueryTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARTestingObjectMother.class.php
Modified:
   3.x/trunk/limb/active_record/common.inc.php
   3.x/trunk/limb/active_record/src/lmbARManyToManyCollection.class.php
   3.x/trunk/limb/active_record/src/lmbAROneToManyCollection.class.php
   3.x/trunk/limb/active_record/src/lmbARRecordSetDecorator.class.php
   3.x/trunk/limb/active_record/src/lmbARRelationCollection.class.php
   3.x/trunk/limb/active_record/src/lmbActiveRecord.class.php
   3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.mysql
   3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.oci
   3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.pgsql
   3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.sqlite
   3.x/trunk/limb/active_record/tests/cases/.setup.php
   3.x/trunk/limb/active_record/tests/cases/lmbARAttributesLazyLoadingTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARAutoTimesTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARDirtyTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARImportTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARManyToManyCollectionTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARManyToManyRelationsTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbAROneToManyCollectionTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbAROneToManyRelationsTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbAROneToOneRelationsTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARRecordSetDecoratorTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARRelationsDefinitionMethodsTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARSubclassingTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARTransactionTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARValidationTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARValueObjectTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbActiveRecordTest.class.php
Log:
-- major refactoring and improvements in ACTIVE_RECORD package:
  * new lmbARQuery class added. lmbARQuery is a child class of lmbSQLRawCriteria. lmbARQuery is created in order to implement so called eager fetching. The common usage of lmbARQuery is the following: 
    $query = new lmbARQuery($active_record_class, $connection);
    $ar_objects = $query->fetch();
  * lmbActiveRecord, lmbARManyToManyCollection and lmbAROneToManyCollection now uses lmbARQuery internally for fetching operations.
  * lmbARQuery :: with($relation_name) added that allows to eager fetch related object with one sql-query. with() support the following relation types: HAS_ONE, BELONGS_TO, MANY_BELONGS_TO.
  The common usage of lmbARQuery :: wiht() is the following: 
    $query = new lmbARQuery('Person', $connection);
    $ar_objects = $query->with('social_security')->fetch();
    Multiple with() for different relations is allowed to: 
    $query = new lmbARQuery('Lecture', $connection);
    $ar_objects = $query->with('course')->with('alt_course')->fetch();
    Note: at the moment lmbARQuery doesnot check uniqueness of loaded objects. That means what we can load multiple instances of the same object several times. lmbARQuery :: with() also does not set pre-fetched object is both directions, that is lectures from the last example will eagerly get courses but courses will not get any lectures.
  * You can also use new 'with' parameter in lmbActiveRecord :: find()-methods as well since lmbActiveRecord uses lmbARQuery internally now.
  * lmbARQuery :: attach($relation_name, $magic_params = array()) added that allows to eagerly fetch related object with separate sql-query. lmbARQuery :: attach() works for all types of relations (except COMPOSED_OF). lmbARQuery :: attach() does separate query for every relation.
    $query = lmbARQuery('Photo', $connection);
    $query->attach('tags', array('sort' => array('title' => 'DESC')))->attach('comments');
    $photos_with_tags_and_commantes = $query->fetch();
  * lmbARQuery :: attach() functionaliry is supported with new lmbARRecordSetAttachDecorator. This new class uses lmbARManyToManyCollection and lmbAROneToManyCollection internally for appropriate types of relations. This caused many refactorings in lmbARCollection and it's child classes.
  * Refactoring in ACTIVE_RECORD package test is also started. New lmbARBaseTestCase and lmbARTestingObjectMother classes added in order to remove code duplication. 
  

Modified: 3.x/trunk/limb/active_record/common.inc.php
===================================================================
--- 3.x/trunk/limb/active_record/common.inc.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/common.inc.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -12,6 +12,7 @@
  * @version $Id$
  */
 require_once('limb/core/common.inc.php');
+require_once('limb/validation/common.inc.php');
 require_once('limb/dbal/common.inc.php');
 require_once(dirname(__FILE__) . '/toolkit.inc.php');
 lmb_require('limb/active_record/src/lmbActiveRecord.class.php');

Modified: 3.x/trunk/limb/active_record/src/lmbARManyToManyCollection.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbARManyToManyCollection.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/src/lmbARManyToManyCollection.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -17,44 +17,42 @@
  */
 class lmbARManyToManyCollection extends lmbARRelationCollection
 {
-  protected function _createDbRecordSet2($criteria = null)
+  protected function _createARQuery($magic_params = array())
   {
-    $class = $this->relation_info['class'];
-    $object = new $class();
-    $table = $object->getTableName();
+    $query = self :: createFullARQueryForRelation($this->relation_info, $this->conn, $magic_params);
+    
+    $join_table = $this->conn->quoteIdentifier($this->relation_info['table']);
+    $field = $this->conn->quoteIdentifier($this->relation_info['field']);
+    $query->addCriteria("{$join_table}.{$field} = {$this->owner->getId()}");
 
-    $join_table = $this->relation_info['table'];
-    $field = $this->relation_info['field'];
-    $foreign_field = $this->relation_info['foreign_field'];
-
-    $sql = "SELECT {$table}.* FROM {$table}, {$join_table}
-            WHERE {$table}." . $object->getPrimaryKeyName() . "={$join_table}.$foreign_field AND
-            {$join_table}.{$field}=" . $this->owner->getId() . ' %where%';
-
-    $query = new lmbSelectRawQuery($sql, $this->conn);
-    if($criteria)
-      $query->addCriteria($criteria);
-    return $query->getRecordSet();
+    return $query; 
   }
-
-  protected function _createDbRecordSet($criteria = null)
+  
+  static function createFullARQueryForRelation($relation_info, $conn, $magic_params = array())
   {
-    $class = $this->relation_info['class'];
+    return parent :: createFullARQueryForRelation(__CLASS__, $relation_info, $conn, $magic_params);
+  }
+  
+  static function createCoreARQueryForRelation($relation_info, $conn)
+  {
+    $class = $relation_info['class'];
     $object = new $class();
-    $table = $this->conn->quoteIdentifier($object->getTableName());
 
-    $join_table = $this->conn->quoteIdentifier($this->relation_info['table']);
-    $field = $this->conn->quoteIdentifier($this->relation_info['field']);
-    $foreign_field = $this->conn->quoteIdentifier($this->relation_info['foreign_field']);
+    $table = $conn->quoteIdentifier($object->getTableName());
+    $join_table = $conn->quoteIdentifier($relation_info['table']);
+    $field = $conn->quoteIdentifier($relation_info['field']);
+    $foreign_field = $conn->quoteIdentifier($relation_info['foreign_field']);
+    
+    $sql = "SELECT %fields% FROM {$table} INNER JOIN {$join_table} ON {$table}.{$object->getPrimaryKeyName()} = {$join_table}.{$foreign_field}" . 
+           " %tables% %left_join% %where% %group% %having% %order%";
 
-    $sql = "SELECT $table.* FROM $table, $join_table
-            WHERE $table." . $object->getPrimaryKeyName() . "=$join_table.$foreign_field AND
-            $join_table.$field=" . $this->owner->getId() . ' %where%';
+    $query = new lmbARQuery($class, $conn, $sql);
 
-    $query = new lmbSelectRawQuery($sql, $this->conn);
-    if($criteria)
-      $query->addCriteria($criteria);
-    return $query->getRecordSet();
+    $fields = $object->getDbTable()->getColumnsForSelect();
+    foreach($fields as $field => $alias)
+      $query->addField($field, $alias);
+    
+    return $query;
   }
 
   function set($objects)

Modified: 3.x/trunk/limb/active_record/src/lmbAROneToManyCollection.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbAROneToManyCollection.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/src/lmbAROneToManyCollection.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -16,18 +16,23 @@
  */
 class lmbAROneToManyCollection extends lmbARRelationCollection
 {
-  protected function _createDbRecordSet($extra_criteria = null)
+  protected function _createARQuery($magic_params = array())
   {
-    $class = $this->relation_info['class'];
-    $object = new $class(null, $this->conn);
-    $criteria = new lmbSQLFieldCriteria($this->relation_info['field'], $this->owner->getId());
-
-    if($extra_criteria)
-      $criteria->addAnd($extra_criteria);
-
-    return $object->getDbTable()->select($criteria);
+    $query = self :: createFullARQueryForRelation($this->relation_info, $this->conn, $magic_params);
+    $query->addCriteria(new lmbSQLFieldCriteria($this->relation_info['field'], $this->owner->getId()));
+    return $query;
   }
-
+  
+  static function createFullARQueryForRelation($relation_info, $conn, $magic_params = array())
+  {
+    return parent :: createFullARQueryForRelation(__CLASS__, $relation_info, $conn, $magic_params);
+  }
+  
+  static function createCoreARQueryForRelation($relation_info, $conn)
+  {
+    return new lmbARQuery($relation_info['class'], $conn);
+  }
+  
   function add($object)
   {
     $property = $object->mapFieldToProperty($this->relation_info['field']);

Added: 3.x/trunk/limb/active_record/src/lmbARQuery.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbARQuery.class.php	                        (rev 0)
+++ 3.x/trunk/limb/active_record/src/lmbARQuery.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -0,0 +1,101 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+lmb_require('limb/dbal/src/query/lmbSelectRawQuery.class.php');
+lmb_require('limb/active_record/src/lmbARRecordSetAttachDecorator.class.php');
+
+class lmbARQuery extends lmbSelectRawQuery
+{
+  protected $_base_class_name;
+  protected $_base_object;
+  protected $_with = array();
+  protected $_attach = array();
+  
+  function __construct($base_class_name, $conn, $sql = '')
+  {
+    $this->_base_class_name = $base_class_name;
+    $this->_base_object = new $this->_base_class_name(null, $conn);
+
+    if(!$sql)
+    {
+      parent :: __construct($conn);
+      $this->addTable($this->_base_object->getTableName());
+      $this->_addFieldsForObject($this->_base_object);
+    }
+    else
+    {
+      parent :: __construct($sql, $conn);
+    }
+  }
+  
+  function with($relation_name)
+  {
+    $relation_info = $this->_base_object->getRelationInfo($relation_name);
+    $this->_with[$relation_name] = $relation_info;
+    
+    if(!$relation_info || !isset($relation_info['class']))
+      throw new lmbException('Relation info "' . $relation_name .'" not found in "' . $this->_base_class_name . '" or does not contain "class" property');
+    
+    $class_name = $relation_info['class'];
+    $object = new $class_name(null, $this->_conn);
+    $this->_addFieldsForObject($object, $relation_name, $prefix = $relation_name . '__');
+    
+    $relation_type = $this->_base_object->getRelationType($relation_name);
+    switch($relation_type)
+    {
+      case lmbActiveRecord :: HAS_ONE:
+      case lmbActiveRecord :: MANY_BELONGS_TO:
+        $this->addLeftJoin($object->getTableName(), 
+                           $object->getPrimaryKeyName(),
+                           $this->_base_object->getTableName(), 
+                           $relation_info['field'],
+                           $relation_name);
+      break;
+      case lmbActiveRecord :: BELONGS_TO:
+        $this->addLeftJoin($object->getTableName(), 
+                           $relation_info['field'],
+                           $this->_base_object->getTableName(), 
+                           $this->_base_object->getPrimaryKeyName(),
+                           $relation_name);
+      break;
+    }
+    return $this;
+  }
+  
+  function attach($relation_name, $params = array())
+  {
+    $this->_attach[$relation_name] = $params;
+    return $this;
+  }
+  
+  protected function _addFieldsForObject($object, $table_name = '', $prefix = '')
+  {
+    $fields = $object->getDbTable()->getColumnsForSelect($table_name, $object->getLazyAttributes(), $prefix);
+    foreach($fields as $field => $alias)
+      $this->addField($field, $alias);
+  }
+  
+  function fetch($decorate = true)
+  {
+    $rs = parent :: fetch();
+    $rs = $this->_decorateWithAttachDecorator($rs);
+    
+    if($decorate)
+      return new lmbARRecordSetDecorator($rs, $this->_base_class_name, $this->_conn, $this->_with);
+    else
+      return $rs;
+  }
+  
+  protected function _decorateWithAttachDecorator($rs)
+  {
+    if(count($this->_attach))
+      return new lmbARRecordSetAttachDecorator($rs, $this->_base_object, $this->_conn, $this->_attach);
+    else
+      return $rs;
+  }
+}

Added: 3.x/trunk/limb/active_record/src/lmbARRecordSetAttachDecorator.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbARRecordSetAttachDecorator.class.php	                        (rev 0)
+++ 3.x/trunk/limb/active_record/src/lmbARRecordSetAttachDecorator.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -0,0 +1,134 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+lmb_require('limb/core/src/lmbCollectionDecorator.class.php');
+lmb_require('limb/core/src/lmbClassPath.class.php');
+lmb_require('limb/core/src/lmbSet.class.php');
+
+/**
+ * class lmbARRecordSetAttachDecorator. This class is a part of eager fetching functionality 
+ *
+ * @package active_record
+ * @version $Id$
+ */
+class lmbARRecordSetAttachDecorator extends lmbCollectionDecorator
+{
+  protected $base_object;
+  protected $conn;
+  protected $attach_relations = array();
+
+  function __construct($record_set, $base_object, $conn = null, $attach_relations = array())
+  {
+    $this->base_object = $base_object;
+    $this->conn = $conn;
+    $this->attach_relations = $attach_relations;
+
+    parent :: __construct($record_set);
+  }
+  
+  function rewind()
+  {
+    foreach($this->attach_relations as $relation_name => $params)
+    {
+      $relation_type = $this->base_object->getRelationType($relation_name);
+      $relation_info = $this->base_object->getRelationInfo($relation_name);
+      
+      $relation_class = $relation_info['class'];
+      $relation_object = new $relation_class(null, $this->conn);
+      
+      switch($relation_type)
+      {
+        case lmbActiveRecord :: HAS_ONE:
+        case lmbActiveRecord :: MANY_BELONGS_TO:
+          $ids = lmbArrayHelper :: getColumnValues($relation_info['field'], $this->iterator);
+          $attached_objects = lmbActiveRecord :: findByIds($relation_class, $ids, $params, $this->conn);
+          $this->loaded_attaches[$relation_name] = lmbCollection :: toFlatArray($attached_objects, 
+                                                                                $key_field = $relation_object->getPrimaryKeyName(),
+                                                                                $export_each = false); 
+        break;
+        case lmbActiveRecord :: BELONGS_TO:
+          $ids = lmbArrayHelper :: getColumnValues($this->base_object->getPrimaryKeyName(), $this->iterator);
+          
+          $criteria = lmbSQLCriteria :: in($relation_info['field'], $ids);
+          $params['criteria'] = isset($params['criteria']) ? $params['criteria']->addAnd($criteria) : $criteria;
+          $attached_objects = lmbActiveRecord :: find($relation_class, $params, $this->conn);
+          $this->loaded_attaches[$relation_name] = lmbCollection :: toFlatArray($attached_objects, 
+                                                                                $key_field = $relation_info['field'], 
+                                                                                $export_each = false); 
+        break;
+        case lmbActiveRecord :: HAS_MANY:
+          if(isset($params['sort']))
+            $params['sort'] = array($relation_info['field'] => 'ASC') + $params['sort']; 
+          else
+            $params['sort'] = array($relation_info['field'] => 'ASC');
+            
+          $query = lmbAROneToManyCollection :: createFullARQueryForRelation($relation_info, $this->conn, $params);
+          
+          $ids = lmbArrayHelper :: getColumnValues($this->base_object->getPrimaryKeyName(), $this->iterator);
+          $query->addCriteria(lmbSQLCriteria :: in($relation_info['field'], $ids));
+          
+          $attached_objects = $query->fetch();
+          
+          foreach($attached_objects as $attached_object)
+            $this->loaded_attaches[$relation_name][$attached_object->get($relation_info['field'])][] = $attached_object; 
+        break;
+        case lmbActiveRecord :: HAS_MANY_TO_MANY:
+          if(isset($params['sort']))
+            $params['sort'] = array($relation_info['field'] => 'ASC') + $params['sort']; 
+          else
+            $params['sort'] = array($relation_info['field'] => 'ASC');
+            
+          $query = lmbARManyToManyCollection :: createFullARQueryForRelation($relation_info, $this->conn, $params);
+          $query->addField($relation_info['table']. '.' . $relation_info['field'], "link__id");
+          
+          $ids = lmbArrayHelper :: getColumnValues($this->base_object->getPrimaryKeyName(), $this->iterator);
+          $query->addCriteria(lmbSQLCriteria :: in($relation_info['field'], $ids));
+          
+          $attached_objects = $query->fetch();
+          
+          foreach($attached_objects as $attached_object)
+            $this->loaded_attaches[$relation_name][$attached_object->get("link__id")][] = $attached_object; 
+        break;
+      }
+    }
+    
+    parent :: rewind();
+  }
+  
+  function current()
+  {
+    $record = parent :: current();
+
+    foreach($this->attach_relations as $relation_name => $params)
+    {
+      $relation_type = $this->base_object->getRelationType($relation_name);
+      $relation_info = $this->base_object->getRelationInfo($relation_name);
+      
+      switch($relation_type)
+      {
+        case lmbActiveRecord :: HAS_ONE:
+        case lmbActiveRecord :: MANY_BELONGS_TO:
+          $record->set($relation_name, $this->loaded_attaches[$relation_name][$record->get($relation_info['field'])]);
+        break;
+        case lmbActiveRecord :: BELONGS_TO:
+        case lmbActiveRecord :: HAS_MANY:
+        case lmbActiveRecord :: HAS_MANY_TO_MANY:
+          $record->set($relation_name, $this->loaded_attaches[$relation_name][$record->get($this->base_object->getPrimaryKeyName())]);
+        break;
+      }
+    }
+    return $record;
+  }
+
+  function at($pos)
+  {
+    throw new lmbException('at() is not implemented in lmbARRecordSetAttachDecorator. Please consider using getArray() instead');
+  }
+}
+
+

Modified: 3.x/trunk/limb/active_record/src/lmbARRecordSetDecorator.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbARRecordSetDecorator.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/src/lmbARRecordSetDecorator.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -8,6 +8,7 @@
  */
 lmb_require('limb/core/src/lmbCollectionDecorator.class.php');
 lmb_require('limb/core/src/lmbClassPath.class.php');
+lmb_require('limb/core/src/lmbSet.class.php');
 
 /**
  * class lmbARRecordSetDecorator.
@@ -19,25 +20,19 @@
 {
   protected $class_path;
   protected $conn;
+  protected $with_relations = array();
 
-  function __construct($record_set, $class_path = '', $conn = null)
+  function __construct($record_set, $class_path, $conn = null, $with_relations = array())
   {
     $this->class_path = $class_path;
     $this->conn = $conn;
+    $this->with_relations = $with_relations;
 
     parent :: __construct($record_set);
   }
 
-  function setClassPath($class_path)
-  {
-    $this->class_path = $class_path;
-  }
-
   function current()
   {
-    if(!$this->class_path)
-      throw new lmbException('ActiveRecord class path is not defined');
-
     if(!$record = parent :: current())
       return null;
 
@@ -47,9 +42,34 @@
   protected function _createObjectFromRecord($record)
   {
     $object = $this->_createObject($record);
+    $this->_extractPrefixedFieldsAsActiveRecords($record);
     $object->loadFromRecord($record);
     return $object;
   }
+  
+  protected function _extractPrefixedFieldsAsActiveRecords($record)
+  {
+    foreach($this->with_relations as $relation_name => $relation_info)
+    {
+      $fields = new lmbSet();
+      $prefix = $relation_name . '__';
+      
+      foreach($record->export() as $field => $value)
+      {
+        if(strpos($field, $prefix) === 0)
+        {
+          $non_prefixes_field_name = substr($field, strlen($prefix));
+          $fields->set($non_prefixes_field_name, $value);
+          $record->remove($field);
+        }
+      }
+      
+      $related_object_class = $relation_info['class'];
+      $related_object = new $related_object_class(null, $this->conn);
+      $related_object->loadFromRecord($fields);
+      $record->set($relation_name, $related_object);
+    }
+  }
 
   protected function _createObject($record)
   {

Modified: 3.x/trunk/limb/active_record/src/lmbARRelationCollection.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbARRelationCollection.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/src/lmbARRelationCollection.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -22,17 +22,19 @@
   protected $relation_info;
   protected $owner;
   protected $dataset;
-  protected $criteria;
   protected $conn;
   protected $is_owner_new;
   protected $decorators = array();
+  protected $fetch_with_relations = array();
+  protected $default_params = array();
 
   function __construct($relation, $owner, $criteria = null, $conn = null)
   {
     $this->relation = $relation;
     $this->owner = $owner;
     $this->relation_info = $owner->getRelationInfo($relation);
-    $this->criteria = lmbSQLCriteria :: objectify($criteria);
+    $this->default_params['criteria'] = lmbSQLCriteria :: objectify($criteria);
+    $this->default_params['sort'] = array();
 
     if(is_object($conn))
       $this->conn = $conn;
@@ -64,14 +66,25 @@
       $this->dataset = $this->find();
   }
 
-  abstract protected function _createDbRecordSet($criteria = null);
-
   function find($magic_params = array())
   {
     if($this->is_owner_new)
       throw new lmbException('Not implemented for in memory collection');
 
-    return $this->_createDecoratedDbRecordSet($magic_params);
+    if(is_string($magic_params) || is_object($magic_params))
+      $magic_params = array('criteria' => lmbSQLCriteria :: objectify($magic_params));
+    
+    $magic_params['criteria'] = isset($magic_params['criteria']) ? $magic_params['criteria']->addAnd($this->default_params['criteria']) : $this->default_params['criteria'];
+    $magic_params['sort'] = isset($magic_params['sort']) ? $magic_params['sort'] : $this->default_params['sort'];
+    
+    $query = $this->_createARQuery($magic_params);
+    
+    foreach($this->fetch_with_relations as $relation)
+      $query->with($relation);
+    
+    $rs = $query->fetch();
+    
+    return $this->_applyDecorators($rs);
   }
 
   function findFirst($magic_params = array())
@@ -81,65 +94,68 @@
     if($rs->valid())
       return $rs->current();
   }
+  
+  function with($relation_name)
+  {
+    $this->fetch_with_relations[] = $relation_name;
+    return $this;
+  }
 
-  protected function _createDecoratedDbRecordSet($magic_params = array())
+  static function createFullARQueryForRelation($class, $relation_info, $conn, $magic_params = array())
   {
-    $class = $this->relation_info['class'];
-    $object = new $class();
+    $object = new $relation_info['class'];
 
-    $criteria = clone $this->criteria;
-
-    $sort_params = array();
+    $criteria = isset($magic_params['criteria']) ? $magic_params['criteria'] : new lmbSQLCriteria();
+    
     $has_class_criteria = false;
-
-    if(is_string($magic_params) || is_object($magic_params))
-      $criteria->addAnd($magic_params);
-    elseif(is_array($magic_params))
+    if(isset($magic_params['class']))
     {
-      if(isset($magic_params['criteria']))
-        $criteria->addAnd($magic_params['criteria']);
-
-      if(isset($magic_params['class']))
-      {
-        $filter_object = new $magic_params['class'];
-        $criteria = $filter_object->addClassCriteria($criteria);
-        $has_class_criteria = true;
-      }
-
-      if(isset($magic_params['sort']))
-        $sort_params = $magic_params['sort'];
+      $filter_object = new $magic_params['class'];
+      $criteria = $filter_object->addClassCriteria($criteria);
+      $has_class_criteria = true;
     }
 
     if(!$has_class_criteria)
       $object->addClassCriteria($criteria);
 
-    $rs = $this->_createDbRecordSet($criteria);
-    $this->_applySortParams($rs, $sort_params);
-    $dataset = $object->_decorateRecordSet($rs);
-    return $this->_applyDecorators($dataset);
+    $query = call_user_func_array(array($class, 'createCoreARQueryForRelation'), array($relation_info, $conn));
+    
+    $query->addCriteria($criteria);
+
+    $sort_params = array();
+    if(isset($magic_params['sort']))
+      $sort_params = $magic_params['sort'];
+    
+    self :: applySortParams($query, $relation_info, $sort_params);
+    
+    return $query;
   }
+  
+  abstract static function createCoreARQueryForRelation($relation_info, $conn);
+  
+  abstract protected function _createARQuery($magic_params = array());
 
-  protected function _applySortParams($rs, $sort_params = array())
+  static function applySortParams($query, $relation_info, $sort_params = array())
   {
     if(count($sort_params))
     {
-      $rs->sort($sort_params);
+      $query->order($sort_params);
       return;
     }
-
-    if(isset($this->relation_info['sort_params']) &&
-       is_array($this->relation_info['sort_params']) &&
-       count($this->relation_info['sort_params']))
+    
+    if(isset($relation_info['sort_params']) &&
+       is_array($relation_info['sort_params']) &&
+       count($relation_info['sort_params']))
     {
-      $rs->sort($this->relation_info['sort_params']);
+      $query->order($relation_info['sort_params']);
       return;
     }
 
-    $class = $this->relation_info['class'];
+    $class = $relation_info['class'];
     $object = new $class();
     if(count($default_sort_params = $object->getDefaultSortParams()))
     {
-      $rs->sort($default_sort_params);
+      $query->order($default_sort_params);
       return;
     }
   }
@@ -273,8 +289,17 @@
 
   function sort($params)
   {
-    $this->_ensureDataset();
-    $this->dataset->sort($params);
+    if($this->is_owner_new)
+    {
+      $this->_ensureDataset();
+      $this->dataset->sort($params);
+    }
+    else
+    {
+      // we want to give users ability to change sort params at any time so we just save the last sort params 
+      //  and apply them at the last moment in find() method or even deeper
+      $this->default_params['sort'] = $params;
+    }
     return $this;
   }
 

Modified: 3.x/trunk/limb/active_record/src/lmbActiveRecord.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbActiveRecord.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/src/lmbActiveRecord.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -18,9 +18,10 @@
 lmb_require('limb/validation/src/exception/lmbValidationException.class.php');
 lmb_require('limb/active_record/src/lmbARException.class.php');
 lmb_require('limb/active_record/src/lmbARNotFoundException.class.php');
-lmb_require('limb/active_record/src/lmbARRecordSetDecorator.class.php');
 lmb_require('limb/active_record/src/lmbAROneToManyCollection.class.php');
 lmb_require('limb/active_record/src/lmbARManyToManyCollection.class.php');
+lmb_require('limb/active_record/src/lmbARQuery.class.php');
+lmb_require('limb/active_record/src/lmbARRecordSetDecorator.class.php');
 
 /**
  * Base class responsible for ActiveRecord design pattern implementation. Inspired by Rails ActiveRecord class.
@@ -139,7 +140,7 @@
   /**
    * @var array sort params used to order objects during database retrieval
    */
-  protected $_default_sort_params;
+  protected $_default_sort_params = array();
   /**
    * @var object database metainfo object
    */
@@ -159,6 +160,16 @@
   const ON_BEFORE_DESTROY          = 9;
   const ON_AFTER_DESTROY           = 10;
   /**#@-*/
+  
+  /**#@+
+   * Relation type constants
+   */
+  const HAS_ONE             = 1;
+  const HAS_MANY            = 2;
+  const HAS_MANY_TO_MANY    = 3;
+  const BELONGS_TO          = 4;
+  const MANY_BELONGS_TO     = 5;
+  /**#@-*/
 
   /**
    * @var array event listeners attached to the concrete object instance
@@ -332,7 +343,25 @@
     if(isset($this->_relations[$relation]))
       return $this->_relations[$relation];
   }
+  
+  function getRelationType($relation)
+  {
+    if(isset($this->_has_one[$relation]))
+      return self :: HAS_ONE;
 
+    if(isset($this->_has_many[$relation]))
+      return self :: HAS_MANY;
+
+    if(isset($this->_has_many_to_many[$relation]))
+      return self :: HAS_MANY_TO_MANY;
+
+    if(isset($this->_belongs_to[$relation]))
+      return self :: BELONGS_TO;
+
+    if(isset($this->_many_belongs_to[$relation]))
+      return self :: MANY_BELONGS_TO;
+  }
+
   protected function _getAllRelations()
   {
      return array_merge($this->_has_one,
@@ -342,14 +371,14 @@
                         $this->_many_belongs_to,
                         $this->_composed_of);
   }
-
+  
   protected function _getSingleObjectRelations()
   {
      return array_merge($this->_has_one,
                         $this->_belongs_to,
                         $this->_many_belongs_to,
                         $this->_composed_of);
-  }
+  }  
 
   /**
    *  Returns all relations info for one-to-many
@@ -549,6 +578,11 @@
         $this->_loadLazyAttribute($attribute);
     }
   }
+  
+  function getLazyAttributes()
+  {
+    return $this->_lazy_attributes;
+  }
 
   function has($property)
   {
@@ -1519,9 +1553,8 @@
    */
   protected function _find($params = array())
   {
-    $criteria = isset($params['criteria']) ? $params['criteria'] : null;
-    $sort_params = isset($params['sort']) ? $params['sort'] : array();
-    $rs = $this->_decorateRecordSet($this->findAllRecords($criteria, $sort_params));
+    $query = $this->_createARQuery($params);
+    $rs = $query->fetch();
 
     $return_first = false;
     foreach(array_values($params) as $value)
@@ -1546,6 +1579,29 @@
     else
       return $rs;
   }
+  
+  protected function _createARQuery($params)
+  {
+    $query = new lmbARQuery(get_class($this), $this->_db_conn);
+
+    $criteria = (isset($params['criteria']) && $params['criteria']) ? $params['criteria'] : lmbSQLCriteria :: create();
+    $criteria = $this->addClassCriteria($criteria);    
+    $query->addCriteria($criteria);
+    
+    $sort_params = (isset($params['sort']) && $params['sort']) ? $params['sort'] : $this->_default_sort_params;
+    foreach($sort_params as $field => $sort_type)
+      $query->addOrder($field, $sort_type);
+
+    $with = (isset($params['with']) && $params['with']) ? $params['with'] : array();
+    if(!is_array($with))
+      $with = explode(',', $with);
+    
+    foreach($with as $relation_name)
+      $query->with(trim($relation_name));
+    
+    return $query;
+  }
+  
   /**
    *  Finds a collection of records(not lmbActiveRecord objects!) from database table
    *  @param string|object filtering criteria
@@ -1554,12 +1610,10 @@
    */
   function findAllRecords($criteria = null, $sort_params = array())
   {
-    if(!count($sort_params))
-      $sort_params = $this->_default_sort_params;
-
-    return $this->_db_table->select($this->addClassCriteria($criteria),
-                                    $sort_params,
-                                    $this->_getColumnsForSelect());
+    $query = $this->_createARQuery(array('criteria' => $criteria, 'sort' => $sort_params));
+    return $query->fetch($decorate = false);
+    
+    return $this->_db_table->select($this->addClassCriteria($criteria), $sort_params, $columns);
   }
   /**
    *  Adds class name criterion to passed in criteria
@@ -1761,11 +1815,6 @@
     $db_table->update($set, $criteria);
   }
 
-  protected function _getColumnsForSelect()
-  {
-    return $this->_db_table->getColumnsForSelect('', $this->_lazy_attributes);
-  }
-
   protected function _removeOneToOneObjects()
   {
     foreach($this->_has_one as $property => $info)

Modified: 3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.mysql
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.mysql	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.mysql	2008-01-15 14:55:59 UTC (rev 6691)
@@ -93,6 +93,7 @@
 CREATE TABLE `user_for_test` (
   `id` bigint(20) NOT NULL auto_increment,
   `first_name` varchar(255) default NULL,
+  `linked_object_id` bigint(20) default NULL,
   PRIMARY KEY  (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 

Modified: 3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.oci
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.oci	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.oci	2008-01-15 14:55:59 UTC (rev 6691)
@@ -195,6 +195,7 @@
 CREATE TABLE user_for_test (
   id number NOT NULL,
   first_name varchar(255) default NULL,
+  linked_object_id number default NULL,
   PRIMARY KEY  (id)
 );
 /

Modified: 3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.pgsql
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.pgsql	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.pgsql	2008-01-15 14:55:59 UTC (rev 6691)
@@ -104,6 +104,7 @@
 CREATE TABLE user_for_test (
   "id" SERIAL,
   "first_name" varchar(255)  default NULL,
+  "linked_object_id" int8 default NULL,
   PRIMARY KEY  (id)
 )  ;
 

Modified: 3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.sqlite
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.sqlite	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.sqlite	2008-01-15 14:55:59 UTC (rev 6691)
@@ -81,7 +81,8 @@
 DROP TABLE "user_for_test";
 CREATE TABLE "user_for_test" (
   "id" INTEGER PRIMARY KEY,
-  "first_name" varchar(255) default NULL
+  "first_name" varchar(255) default NULL,
+  "linked_object_id" bigint(20) default NULL
 );
 
 DROP TABLE "user2group_for_test";

Modified: 3.x/trunk/limb/active_record/tests/cases/.setup.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/.setup.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/.setup.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -1,9 +1,18 @@
 <?php
 require_once(dirname(__FILE__) . '/../../common.inc.php');
 require_once('limb/dbal/src/lmbDbDump.class.php');
+require_once('limb/core/src/lmbSet.class.php');
+require_once('limb/core/src/lmbCollection.class.php');
+require_once('limb/active_record/src/lmbActiveRecord.class.php');
+require_once('limb/dbal/src/criteria/lmbSQLRawCriteria.class.php');
+require_once('limb/dbal/src/lmbSimpleDb.class.php');
+require_once('limb/dbal/src/lmbTableGateway.class.php');
+require_once('limb/dbal/src/lmbSimpleDb.class.php');
 
 $type = lmbToolkit :: instance()->getDefaultDbConnection()->getType();
 $this->dump = new lmbDbDump(dirname(__FILE__) . '/.fixture/init_tests.' . $type);
 $this->dump->load();
 
+require_once(dirname(__FILE__) . '/lmbARBaseTestCase.class.php');
 
+

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARAttributesLazyLoadingTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARAttributesLazyLoadingTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARAttributesLazyLoadingTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -6,9 +6,6 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
-require_once('limb/active_record/src/lmbActiveRecord.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
-require_once('limb/dbal/src/lmbTableGateway.class.php');
 
 class LazyTestOneTableObject extends lmbActiveRecord
 {

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARAutoTimesTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARAutoTimesTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARAutoTimesTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -6,8 +6,6 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
-require_once('limb/active_record/src/lmbActiveRecord.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
 
 class TestAutoTimesObject extends lmbActiveRecord
 {

Added: 3.x/trunk/limb/active_record/tests/cases/lmbARBaseTestCase.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARBaseTestCase.class.php	                        (rev 0)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARBaseTestCase.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -0,0 +1,43 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+require_once(dirname(__FILE__) . '/lmbARTestingObjectMother.class.php'); 
+
+class lmbARBaseTestCase extends UnitTestCase
+{
+  protected $conn;
+  protected $db;
+  protected $creator;
+  protected $tables_to_cleanup = array();
+
+  function setUp()
+  {
+    $toolkit = lmbToolkit :: save();
+    $this->conn = $toolkit->getDefaultDbConnection();
+    $this->db = new lmbSimpleDb($this->conn);
+    $this->creator = new lmbARTestingObjectMother($this->conn);
+
+    $this->_cleanUp();
+  }
+
+  function tearDown()
+  {
+    $this->_cleanUp();
+
+    $this->conn->disconnect();
+
+    lmbToolkit :: restore();
+  }
+
+  protected function _cleanUp()
+  {
+    foreach($this->tables_to_cleanup as $table_name)
+      $this->db->delete($table_name);
+  }
+}
+

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARDirtyTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARDirtyTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARDirtyTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -6,9 +6,6 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
-require_once('limb/active_record/src/lmbActiveRecord.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
-require_once('limb/dbal/src/lmbTableGateway.class.php');
 require_once(dirname(__FILE__) . '/lmbActiveRecordTest.class.php');
 require_once(dirname(__FILE__) . '/lmbAROneToManyRelationsTest.class.php');
 require_once(dirname(__FILE__) . '/lmbARValueObjectTest.class.php');

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARImportTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARImportTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARImportTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -6,9 +6,6 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
-require_once('limb/active_record/src/lmbActiveRecord.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
-require_once('limb/core/src/lmbSet.class.php');
 require_once(dirname(__FILE__) . '/lmbActiveRecordTest.class.php');
 require_once(dirname(__FILE__) . '/lmbAROneToManyRelationsTest.class.php');
 require_once(dirname(__FILE__) . '/lmbAROneToOneRelationsTest.class.php');

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARManyToManyCollectionTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARManyToManyCollectionTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARManyToManyCollectionTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -7,7 +7,6 @@
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
 require_once('limb/active_record/src/lmbARManyToManyCollection.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
 require_once(dirname(__FILE__) . '/lmbARManyToManyRelationsTest.class.php');
 
 Mock :: generate('GroupForTest', 'MockGroupForTest');

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARManyToManyRelationsTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARManyToManyRelationsTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARManyToManyRelationsTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -6,45 +6,7 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
-require_once('limb/active_record/src/lmbActiveRecord.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
-require_once('limb/validation/src/rule/lmbRequiredRule.class.php');
 
-class GroupForTest extends lmbActiveRecord
-{
-  protected $_db_table_name = 'group_for_test';
-
-  protected $_has_many_to_many = array('users' => array('field' => 'group_id',
-                                                        'foreign_field' => 'user_id',
-                                                        'table' => 'user2group_for_test',
-                                                        'class' => 'UserForTest'));
-
-  protected $_test_validator;
-
-  function setValidator($validator)
-  {
-    $this->_test_validator = $validator;
-  }
-
-  function _createValidator()
-  {
-    if($this->_test_validator)
-      return $this->_test_validator;
-
-    return parent :: _createValidator();
-  }
-}
-
-class UserForTest extends lmbActiveRecord
-{
-  protected $_db_table_name = 'user_for_test';
-
-  protected $_has_many_to_many = array('groups' => array('field' => 'user_id',
-                                                         'foreign_field' => 'group_id',
-                                                         'table' => 'user2group_for_test',
-                                                         'class' => 'GroupForTest'));
-}
-
 class GroupsForTestCollectionStub extends lmbARManyToManyCollection{}
 
 class UserForTestWithCustomCollection extends lmbActiveRecord
@@ -58,27 +20,10 @@
                                                          'collection' => 'GroupsForTestCollectionStub'));
 }
 
-class lmbARManyToManyRelationsTest extends UnitTestCase
+class lmbARManyToManyRelationsTest extends lmbARBaseTestCase
 {
-  protected $db;
+  protected $tables_to_cleanup = array('user_for_test', 'group_for_test', 'user2group_for_test', 'test_one_table_object'); 
 
-  function setUp()
-  {
-    $this->db = new lmbSimpleDb(lmbToolkit :: instance()->getDefaultDbConnection());
-    $this->_dbCleanUp();
-  }
-
-  function tearDown()
-  {
-    $this->_dbCleanUp();
-  }
-
-  function _dbCleanUp()
-  {
-    lmbActiveRecord :: delete('GroupForTest');
-    lmbActiveRecord :: delete('UserForTest');
-  }
-
   function testMapPropertyToField()
   {
     $group = new GroupForTest();
@@ -96,15 +41,11 @@
 
   function testAddFromOneSideOfRelation()
   {
-    $user = new UserForTest();
-    $user->setFirstName('Bob');
+    $user = $this->creator->initUser();
 
-    $group1 = new GroupForTest();
-    $group1->setTitle('vp1');
+    $group1 = $this->creator->initGroup();
+    $group2 = $this->creator->initGroup();
 
-    $group2 = new GroupForTest();
-    $group2->setTitle('vp2');
-
     $user->addToGroups($group1);
     $user->addToGroups($group2);
     $user->save();
@@ -123,18 +64,12 @@
 
   function testLoadShouldNotMixTables()
   {
-    $user1 = new UserForTest();
-    $user1->setFirstName('Bob');
+    $user1 = $this->creator->initUser();
+    $user2 = $this->creator->initUser();
 
-    $user2 = new UserForTest();
-    $user2->setFirstName('Joe');
-
-    $group1 = new GroupForTest();
-    $group1->setTitle('vp1');
-
-    $group2 = new GroupForTest();
-    $group2->setTitle('vp2');
-
+    $group1 = $this->creator->initGroup();
+    $group2 = $this->creator->initGroup();
+    
     $user1->addToGroups($group1);
     $user1->addToGroups($group2);
     $user1->save();
@@ -155,16 +90,35 @@
     $this->assertEqual($rs->current()->getId(), $group2->getId());
   }
 
-  function testSetingCollectionDirectlyCallsAddToMethod()
+  function testFetch_WithRelatedObjectsUsing_WithMethod()
   {
-    $user = new UserForTest();
-    $user->setFirstName('Bob');
+    $linked_object1 = $this->creator->createOneTableObject();
+    $linked_object2 = $this->creator->createOneTableObject();
+    
+    $user1 = $this->creator->createUser($linked_object1);
+    $user2 = $this->creator->createUser($linked_object2);
+    
+    $group = $this->creator->createGroup();
 
-    $g1 = new GroupForTest();
-    $g1->setTitle('vp1');
-    $g2 = new GroupForTest();
-    $g2->setTitle('vp2');
+    $group->setUsers(array($user1, $user2));    
 
+    $group2 = lmbActiveRecord :: findById('GroupForTest', $group->getId());
+    $arr = $group2->getUsers()->with('linked_object')->getArray();
+
+    //make sure we really eager fetching
+    $this->db->delete('test_one_table_object');
+
+    $this->assertEqual($arr[0]->getFirstName(), $user1->getFirstName());
+    $this->assertEqual($arr[1]->getFirstName(), $user2->getFirstName());
+  }
+
+  function testSetingCollectionDirectlyCallsAddToMethod()
+  {
+    $user = $this->creator->initUser();
+    
+    $g1 = $this->creator->initGroup();
+    $g2 = $this->creator->initGroup();
+
     $user->setGroups(array($g1, $g2));
     $arr = $user->getGroups()->getArray();
     $this->assertEqual(sizeof($arr), 2);
@@ -174,14 +128,11 @@
 
   function testSetFlushesPreviousCollection()
   {
-    $user = new UserForTest();
-    $user->setFirstName('Bob');
-
-    $g1 = new GroupForTest();
-    $g1->setTitle('vp1');
-    $g2 = new GroupForTest();
-    $g2->setTitle('vp2');
-
+    $user = $this->creator->initUser();
+    
+    $g1 = $this->creator->initGroup();
+    $g2 = $this->creator->initGroup();
+    
     $user->addToGroups($g1);
     $user->addToGroups($g2);
 
@@ -193,15 +144,11 @@
 
   function testUpdateRelations()
   {
-    $user = new UserForTest();
-    $user->setFirstName('Bob');
-
-    $group1 = new GroupForTest();
-    $group1->setTitle('vp1');
-
-    $group2 = new GroupForTest();
-    $group2->setTitle('vp2');
-
+    $user = $this->creator->initUser();
+    
+    $group1 = $this->creator->initGroup();
+    $group2 = $this->creator->initGroup();
+    
     $user->addToGroups($group1);
     $user->addToGroups($group2);
     $user->save();
@@ -219,18 +166,12 @@
 
   function testDeleteAlsoRemovesManyToManyRecords()
   {
-    $user1 = new UserForTest();
-    $user1->setFirstName('Bob');
+    $user1 = $this->creator->initUser();
+    $user2 = $this->creator->initUser();
 
-    $user2 = new UserForTest();
-    $user2->setFirstName('Bob');
-
-    $group1 = new GroupForTest();
-    $group1->setTitle('vp1');
-
-    $group2 = new GroupForTest();
-    $group2->setTitle('vp2');
-
+    $group1 = $this->creator->initGroup();
+    $group2 = $this->creator->initGroup();
+    
     $user1->addToGroups($group1);
     $user1->addToGroups($group2);
     $user1->save();
@@ -259,8 +200,7 @@
 
   function testErrorListIsSharedWithCollection()
   {
-    $user = new UserForTest();
-    $user->setFirstName('Bob');
+    $user = $this->creator->initUser();
 
     $group = new GroupForTest();
 
@@ -269,7 +209,6 @@
     $group->setValidator($validator);
 
     $user->addToGroups($group);
-    $user->addToGroups($group);
 
     $error_list = new lmbErrorList();
     $this->assertFalse($user->trySave($error_list));

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbAROneToManyCollectionTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbAROneToManyCollectionTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbAROneToManyCollectionTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -8,7 +8,6 @@
  */
 require_once('limb/active_record/src/lmbAROneToManyCollection.class.php');
 require_once('limb/core/src/lmbCollectionDecorator.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
 require_once(dirname(__FILE__) . '/lmbAROneToManyRelationsTest.class.php');
 
 Mock :: generate('LectureForTest', 'MockLectureForTest');

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbAROneToManyRelationsTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbAROneToManyRelationsTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbAROneToManyRelationsTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -6,51 +6,8 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
-require_once('limb/active_record/src/lmbActiveRecord.class.php');
 require_once('limb/active_record/src/lmbAROneToManyCollection.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
 
-class CourseForTest extends lmbActiveRecord
-{
-  protected $_db_table_name = 'course_for_test';
-  protected $_has_many = array('lectures' => array('field' => 'course_id',
-                                                   'class' => 'LectureForTest'),
-                               'alt_lectures' => array('field' => 'alt_course_id',
-                                                       'class' => 'LectureForTest'));
-
-  public $save_calls = 0;
-
-  function save()
-  {
-    parent :: save();
-    $this->save_calls++;
-  }
-}
-
-class LectureForTest extends lmbActiveRecord
-{
-  protected $_db_table_name = 'lecture_for_test';
-  protected $_many_belongs_to = array('course' => array('field' => 'course_id',
-                                                        'class' => 'CourseForTest'),
-                                      'alt_course' => array('field' => 'alt_course_id',
-                                                            'class' => 'CourseForTest',
-                                                            'can_be_null' => true));
-  protected $_test_validator;
-
-  function setValidator($validator)
-  {
-    $this->_test_validator = $validator;
-  }
-
-  function _createValidator()
-  {
-    if($this->_test_validator)
-      return $this->_test_validator;
-
-    return parent :: _createValidator();
-  }
-}
-
 class LecturesForTestCollectionStub extends lmbAROneToManyCollection{}
 
 class CourseForTestWithCustomCollection extends lmbActiveRecord
@@ -71,27 +28,10 @@
 
 Mock :: generate('LectureForTest', 'MockLectureForTest');
 
-class lmbAROneToManyRelationsTest extends UnitTestCase
+class lmbAROneToManyRelationsTest extends lmbARBaseTestCase
 {
-  protected $db;
-
-  function setUp()
-  {
-    $this->db = new lmbSimpleDb(lmbToolkit :: instance()->getDefaultDbConnection());
-    $this->_dbCleanUp();
-  }
-
-  function tearDown()
-  {
-    $this->_dbCleanUp();
-  }
-
-  function _dbCleanUp()
-  {
-    $this->db->delete('course_for_test');
-    $this->db->delete('lecture_for_test');
-  }
-
+  protected $tables_to_cleanup = array('course_for_test', 'lecture_for_test');
+  
   function testHas()
   {
     $lecture = new LectureForTest();
@@ -437,7 +377,46 @@
     $error_list = new lmbErrorList();
     $this->assertFalse($course->trySave($error_list));
   }
+  
+  function testFetchWithRelatedObjects_UsingWithMethod()
+  {
+    $course = $this->creator->createCourse();
+    
+    $alt_course1 = $this->creator->createCourse();
+    $alt_course2 = $this->creator->createCourse();
+    
+    $lecture1 = $this->creator->createLecture($course, $alt_course1);
+    $lecture2 = $this->creator->createLecture($course, $alt_course2);
+    $lecture3 = $this->creator->createLecture($course, $alt_course1);
+    
+    $lectures = $course->getLectures()->with('course')->with('alt_course');
+    $arr = $lectures->getArray();
+    
+    //make sure we really eager fetching
+    $this->db->delete('course_for_test');
+    
+    $this->assertIsA($arr[0], 'LectureForTest');
+    $this->assertEqual($arr[0]->getTitle(), $lecture1->getTitle());
+    $this->assertIsA($arr[0]->getCourse(), 'CourseForTest');
+    $this->assertEqual($arr[0]->getCourse()->getTitle(), $course->getTitle());
+    $this->assertIsA($arr[0]->getAltCourse(), 'CourseForTest');
+    $this->assertEqual($arr[0]->getAltCourse()->getTitle(), $alt_course1->getTitle());
 
+    $this->assertIsA($arr[1], 'LectureForTest');
+    $this->assertEqual($arr[1]->getTitle(), $lecture2->getTitle());
+    $this->assertIsA($arr[1]->getCourse(), 'CourseForTest');
+    $this->assertEqual($arr[1]->getCourse()->getTitle(), $course->getTitle());
+    $this->assertIsA($arr[1]->getAltCourse(), 'CourseForTest');
+    $this->assertEqual($arr[1]->getAltCourse()->getTitle(), $alt_course2->getTitle());
+
+    $this->assertIsA($arr[2], 'LectureForTest');
+    $this->assertEqual($arr[2]->getTitle(), $lecture3->getTitle());
+    $this->assertIsA($arr[2]->getCourse(), 'CourseForTest');
+    $this->assertEqual($arr[2]->getCourse()->getTitle(), $course->getTitle());
+    $this->assertIsA($arr[2]->getAltCourse(), 'CourseForTest');
+    $this->assertEqual($arr[2]->getAltCourse()->getTitle(), $alt_course1->getTitle());
+  }
+
   function _initCourse()
   {
     $course = new CourseForTest();

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbAROneToOneRelationsTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbAROneToOneRelationsTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbAROneToOneRelationsTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -6,23 +6,7 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
-require_once('limb/active_record/src/lmbActiveRecord.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
 
-class PersonForTest extends lmbActiveRecord
-{
-  public $save_count = 0;
-  protected $_has_one = array('social_security' => array('field' => 'ss_id',
-                                                         'class' => 'SocialSecurityForTest',
-                                                         'can_be_null' => true));
-
-  function _onSave()
-  {
-    $this->save_count++;
-  }
-
-}
-
 class PersonForTestNoCascadeDelete extends lmbActiveRecord
 {
   protected $_db_table_name = 'person_for_test';
@@ -47,33 +31,10 @@
   }
 }
 
-class SocialSecurityForTest extends lmbActiveRecord
+class lmbAROneToOneRelationsTest extends lmbARBaseTestCase
 {
-  protected $_belongs_to = array('person' => array('field' => 'ss_id',
-                                                   'class' => 'PersonForTest'));
-}
-
-class lmbAROneToOneRelationsTest extends UnitTestCase
-{
-  protected $db;
-
-  function setUp()
-  {
-    $this->db = new lmbSimpleDb(lmbToolkit :: instance()->getDefaultDbConnection());
-    $this->_dbCleanUp();
-  }
-
-  function tearDown()
-  {
-    $this->_dbCleanUp();
-  }
-
-  function _dbCleanUp()
-  {
-    $this->db->delete('person_for_test');
-    $this->db->delete('social_security_for_test');
-  }
-
+  protected $tables_to_cleanup = array('person_for_test', 'social_security_for_test'); 
+  
   function testHas()
   {
     $person = new PersonForTest();

Added: 3.x/trunk/limb/active_record/tests/cases/lmbARQueryTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARQueryTest.class.php	                        (rev 0)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARQueryTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -0,0 +1,280 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+ 
+class lmbARQueryTest extends lmbARBaseTestCase
+{
+  protected $tables_to_cleanup = array('test_one_table_object', 
+                                       'person_for_test', 
+                                       'social_security_for_test',
+                                       'lecture_for_test',
+                                       'course_for_test');
+  
+  function testSimpleFetch()
+  {
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
+    
+    $query = new lmbARQuery('TestOneTableObject', $this->conn);
+    $iterator = $query->fetch();
+    $arr = $iterator->getArray();
+    
+    $this->assertIsA($arr[0], 'TestOneTableObject');
+    $this->assertEqual($arr[0]->getAnnotation(), $object1->getAnnotation());
+    $this->assertIsA($arr[1], 'TestOneTableObject');
+    $this->assertEqual($arr[1]->getAnnotation(), $object2->getAnnotation());
+  }
+  
+  function testFetch_With_RelatedHasOneObject()
+  {
+    $person1 = $this->creator->createPerson();
+    $person2 = $this->creator->createPerson();
+    
+    $query = new lmbARQuery('PersonForTest', $this->conn);
+    $query->with('social_security');
+    $iterator = $query->fetch();
+    $arr = $iterator->getArray();
+
+    //make sure we really eager fetching
+    $this->db->delete('social_security_for_test');
+    
+    $this->assertIsA($arr[0], 'PersonForTest');
+    $this->assertEqual($arr[0]->getName(), $person1->getName());
+    $this->assertIsA($arr[0]->getSocialSecurity(), 'SocialSecurityForTest');
+    $this->assertEqual($arr[0]->getSocialSecurity()->getCode(), $person1->getSocialSecurity()->getCode());
+    
+    $this->assertIsA($arr[1], 'PersonForTest');
+    $this->assertEqual($arr[1]->getName(), $person2->getName());
+    $this->assertIsA($arr[1]->getSocialSecurity(), 'SocialSecurityForTest');
+    $this->assertEqual($arr[1]->getSocialSecurity()->getCode(), $person2->getSocialSecurity()->getCode());
+  }
+
+  function testFetch_With_RelatedBelongsToObject()
+  {
+    $person1 = $this->creator->createPerson();
+    $ss1 = $person1->getSocialSecurity();
+    $person2 = $this->creator->createPerson();
+    $ss2 = $person2->getSocialSecurity();
+    
+    $query = new lmbARQuery('SocialSecurityForTest', $this->conn);
+    $query->with('person');
+    $iterator = $query->fetch();
+    $arr = $iterator->getArray();
+    
+    //make sure we really eager fetching
+    $this->db->delete('person_for_test');
+    
+    $this->assertIsA($arr[0], 'SocialSecurityForTest');
+    $this->assertEqual($arr[0]->getCode(), $ss1->getCode());
+    $this->assertIsA($arr[0]->getPerson(), 'PersonForTest');
+    $this->assertEqual($arr[0]->getPerson()->getName(), $person1->getName());
+    
+    $this->assertIsA($arr[1], 'SocialSecurityForTest');
+    $this->assertEqual($arr[1]->getCode(), $ss2->getCode());
+    $this->assertIsA($arr[1]->getPerson(), 'PersonForTest');
+    $this->assertEqual($arr[1]->getPerson()->getName(), $person2->getName());
+  }
+
+  function testFetch_With_RelatedManyBelongsToObject()
+  {
+    $course1 = $this->creator->createCourse();
+    $course2 = $this->creator->createCourse();
+    $lecture1 = $this->creator->createLecture($course1);
+    $lecture2 = $this->creator->createLecture($course1);
+    $lecture3 = $this->creator->createLecture($course2);
+    
+    $query = new lmbARQuery('LectureForTest', $this->conn);
+    $query->with('course');
+    $iterator = $query->fetch();
+    $arr = $iterator->getArray();
+
+    //make sure we really eager fetching
+    $this->db->delete('course_for_test');
+    
+    $this->assertIsA($arr[0], 'LectureForTest');
+    $this->assertEqual($arr[0]->getTitle(), $lecture1->getTitle());
+    $this->assertIsA($arr[0]->getCourse(), 'CourseForTest');
+    $this->assertEqual($arr[0]->getCourse()->getTitle(), $course1->getTitle());
+    
+    $this->assertIsA($arr[1], 'LectureForTest');
+    $this->assertEqual($arr[1]->getTitle(), $lecture2->getTitle());
+    $this->assertIsA($arr[1]->getCourse(), 'CourseForTest');
+    $this->assertEqual($arr[1]->getCourse()->getTitle(), $course1->getTitle());
+    
+    $this->assertIsA($arr[2], 'LectureForTest');
+    $this->assertEqual($arr[2]->getTitle(), $lecture3->getTitle());
+    $this->assertIsA($arr[2]->getCourse(), 'CourseForTest');
+    $this->assertEqual($arr[2]->getCourse()->getTitle(), $course2->getTitle());
+  }
+
+  function testFetch_Attach_RelatedHasOneObjects()
+  {
+    $person1 = $this->creator->createPerson();
+    $person2 = $this->creator->createPerson();
+    
+    $query = new lmbARQuery('PersonForTest', $this->conn);
+    // note attach() has the same effect as with() but workds is a different way - it produces another sql request 
+    $iterator = $query->attach('social_security')->fetch();
+    $arr = $iterator->getArray();
+
+    //make sure we really eager fetching
+    $this->db->delete('social_security_for_test');
+    
+    $this->assertIsA($arr[0], 'PersonForTest');
+    $this->assertEqual($arr[0]->getName(), $person1->getName());
+    $this->assertIsA($arr[0]->getSocialSecurity(), 'SocialSecurityForTest');
+    $this->assertEqual($arr[0]->getSocialSecurity()->getCode(), $person1->getSocialSecurity()->getCode());
+    
+    $this->assertIsA($arr[1], 'PersonForTest');
+    $this->assertEqual($arr[1]->getName(), $person2->getName());
+    $this->assertIsA($arr[1]->getSocialSecurity(), 'SocialSecurityForTest');
+    $this->assertEqual($arr[1]->getSocialSecurity()->getCode(), $person2->getSocialSecurity()->getCode());
+  }
+
+  function testFetch_Attach_RelatedBelongsToObjects()
+  {
+    $id = $this->db->insert('person_for_test', array('id' => 100, 'name' => 'junky person'));
+    
+    $person1 = $this->creator->createPerson();
+    $person2 = $this->creator->createPerson();
+    
+    $this->db->delete('person_for_test', 'id = ' . $id);
+    
+    $query = new lmbARQuery('SocialSecurityForTest', $this->conn);
+    // note attach() has the same effect as with() but workds is a different way - it produces another sql request 
+    $arr = $query->attach('person')->fetch()->getArray();
+
+    //make sure we really eager fetching
+    $this->db->delete('person_for_test');
+    
+    $this->assertIsA($arr[0], 'SocialSecurityForTest');
+    $this->assertEqual($arr[0]->getCode(), $person1->getSocialSecurity()->getCode());
+    $this->assertIsA($arr[0]->getPerson(), 'PersonForTest');
+    $this->assertEqual($arr[0]->getPerson()->getName(), $person1->getName());
+    
+    $this->assertIsA($arr[1], 'SocialSecurityForTest');
+    $this->assertEqual($arr[1]->getCode(), $person2->getSocialSecurity()->getCode());
+    $this->assertIsA($arr[1]->getPerson(), 'PersonForTest');
+    $this->assertEqual($arr[1]->getPerson()->getName(), $person2->getName());
+  }
+
+  function testFetch_Attach_RelatedManyBelongsToObjects()
+  {
+   $course = $this->creator->createCourse();
+    
+    $alt_course1 = $this->creator->createCourse();
+    $alt_course2 = $this->creator->createCourse();
+    
+    $lecture1 = $this->creator->createLecture($course, $alt_course1);
+    $lecture2 = $this->creator->createLecture($course, $alt_course2);
+    $lecture3 = $this->creator->createLecture($course, $alt_course1);
+    
+    $query = new lmbARQuery('LectureForTest', $this->conn);
+    $arr = $query->attach('course')->attach('alt_course')->fetch()->getArray();
+    
+    //make sure we really eager fetching
+    $this->db->delete('course_for_test');
+    
+    $this->assertIsA($arr[0], 'LectureForTest');
+    $this->assertEqual($arr[0]->getTitle(), $lecture1->getTitle());
+    $this->assertIsA($arr[0]->getCourse(), 'CourseForTest');
+    $this->assertEqual($arr[0]->getCourse()->getTitle(), $course->getTitle());
+    $this->assertIsA($arr[0]->getAltCourse(), 'CourseForTest');
+    $this->assertEqual($arr[0]->getAltCourse()->getTitle(), $alt_course1->getTitle());
+
+    $this->assertIsA($arr[1], 'LectureForTest');
+    $this->assertEqual($arr[1]->getTitle(), $lecture2->getTitle());
+    $this->assertIsA($arr[1]->getCourse(), 'CourseForTest');
+    $this->assertEqual($arr[1]->getCourse()->getTitle(), $course->getTitle());
+    $this->assertIsA($arr[1]->getAltCourse(), 'CourseForTest');
+    $this->assertEqual($arr[1]->getAltCourse()->getTitle(), $alt_course2->getTitle());
+
+    $this->assertIsA($arr[2], 'LectureForTest');
+    $this->assertEqual($arr[2]->getTitle(), $lecture3->getTitle());
+    $this->assertIsA($arr[2]->getCourse(), 'CourseForTest');
+    $this->assertEqual($arr[2]->getCourse()->getTitle(), $course->getTitle());
+    $this->assertIsA($arr[2]->getAltCourse(), 'CourseForTest');
+    $this->assertEqual($arr[2]->getAltCourse()->getTitle(), $alt_course1->getTitle()); 
+  }
+
+  function testFetch_Attach_RelatedHasMany()
+  {
+    $course1 = $this->creator->createCourse();
+    $course2 = $this->creator->createCourse();
+    
+    $lecture1 = $this->creator->createLecture($course1, null, 'ZZZ');
+    $lecture2 = $this->creator->createLecture($course2, null, 'CCC');
+    $lecture3 = $this->creator->createLecture($course1, null, 'AAA');
+    $lecture4 = $this->creator->createLecture($course1, null, 'BBB');
+    
+    $query = new lmbARQuery('CourseForTest', $this->conn);
+    $arr = $query->attach('lectures', array('sort' => array('title' => 'ASC')))->fetch()->getArray();
+    
+    //make sure we really eager fetching
+    $this->db->delete('lecture_for_test');
+    
+    $this->assertIsA($arr[0], 'CourseForTest');
+    $this->assertEqual($arr[0]->getTitle(), $course1->getTitle());
+    $lectures = $arr[0]->getLectures();
+    $this->assertEqual(count($lectures), 3);
+    $this->assertEqual($lectures[0]->getId(), $lecture3->getId());
+    $this->assertEqual($lectures[0]->getTitle(), 'AAA');
+    $this->assertEqual($lectures[1]->getId(), $lecture4->getId());
+    $this->assertEqual($lectures[1]->getTitle(), 'BBB');
+    $this->assertEqual($lectures[2]->getId(), $lecture1->getId());
+    $this->assertEqual($lectures[2]->getTitle(), 'ZZZ');
+    
+    $this->assertIsA($arr[1], 'CourseForTest');
+    $this->assertEqual($arr[1]->getTitle(), $course2->getTitle());
+    $lectures = $arr[1]->getLectures();
+    $this->assertEqual(count($lectures), 1);
+    $this->assertEqual($lectures[0]->getId(), $lecture2->getId());
+    $this->assertEqual($lectures[0]->getTitle(), 'CCC');
+  }
+
+  function testFetch_Attach_RelatedHasManyToMany()
+  {
+    $user1 = $this->creator->createUser();
+    $user2 = $this->creator->createUser();
+
+    $group1 = $this->creator->createGroup('AAA');
+    $group2 = $this->creator->createGroup('BBB');
+    $group3 = $this->creator->createGroup('ZZZ');
+    
+    $group1->setUsers(array($user1, $user2));
+    $group2->setUsers(array($user2));
+    $group3->setUsers(array($user1));
+     
+    $query = new lmbARQuery('UserForTest', $this->conn);
+    $arr = $query->attach('groups', array('sort' => array('title' => 'DESC')))->fetch()->getArray();
+    
+    //make sure we really eager fetching
+    $this->db->delete('group_for_test');
+    $this->db->delete('user2group_for_test');
+    
+    $this->assertIsA($arr[0], 'UserForTest');
+    $this->assertEqual($arr[0]->getFirstName(), $user1->getFirstName());
+    $groups = $arr[0]->getGroups();
+    $this->assertEqual(count($groups), 2);
+    $this->assertEqual($groups[0]->getId(), $group3->getId());
+    $this->assertEqual($groups[0]->getTitle(), 'ZZZ');
+    $this->assertEqual($groups[1]->getId(), $group1->getId());
+    $this->assertEqual($groups[1]->getTitle(), 'AAA');
+    
+    $this->assertIsA($arr[1], 'UserForTest');
+    $this->assertEqual($arr[1]->getFirstName(), $user2->getFirstName());
+    $groups = $arr[1]->getGroups();
+    $this->assertEqual(count($groups), 2);
+    $this->assertEqual($groups[0]->getId(), $group2->getId());
+    $this->assertEqual($groups[0]->getTitle(), 'BBB');
+    $this->assertEqual($groups[1]->getId(), $group1->getId());
+    $this->assertEqual($groups[1]->getTitle(), 'AAA');
+  }
+}
+
+

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARRecordSetDecoratorTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARRecordSetDecoratorTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARRecordSetDecoratorTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -7,7 +7,6 @@
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
 require_once('limb/active_record/src/lmbARRecordSetDecorator.class.php');
-require_once('limb/core/src/lmbCollection.class.php');
 require_once(dirname(__FILE__) . '/lmbAROneToManyRelationsTest.class.php');
 
 class lmbARRecordSetDecoratorTest extends UnitTestCase
@@ -46,25 +45,6 @@
     $this->assertEqual($lecture2->getCourse()->getTitle(), $course->getTitle());
   }
 
-  function testSetActiveRecordClassPathWithSetter()
-  {
-    $course = $this->_createCourseWithTwoLectures();
-
-    $db = new lmbSimpleDb(lmbToolkit :: instance()->getDefaultDbConnection());
-    $decorated = $db->select('lecture_for_test');
-
-    $iterator = new lmbARRecordSetDecorator($decorated);
-    $iterator->setClassPath('LectureForTest');
-    $iterator->rewind();
-
-    $lecture1 = $iterator->current();
-    $this->assertEqual($lecture1->getCourse()->getTitle(), $course->getTitle());
-
-    $iterator->next();
-    $lecture2 = $iterator->current();
-    $this->assertEqual($lecture2->getCourse()->getTitle(), $course->getTitle());
-  }
-
   function testGetOffsetIsDecorated()
   {
     $course = $this->_createCourseWithTwoLectures();
@@ -72,8 +52,7 @@
     $db = new lmbSimpleDb(lmbToolkit :: instance()->getDefaultDbConnection());
     $decorated = $db->select('lecture_for_test');
 
-    $iterator = new lmbARRecordSetDecorator($decorated);
-    $iterator->setClassPath('LectureForTest');
+    $iterator = new lmbARRecordSetDecorator($decorated, 'LectureForTest');
 
     $this->assertEqual($iterator->at(0)->getCourse()->getTitle(), $course->getTitle());
     $this->assertEqual($iterator[0]->getCourse()->getTitle(), $course->getTitle());
@@ -82,27 +61,33 @@
     $this->assertEqual($iterator[1]->getCourse()->getTitle(), $course->getTitle());
   }
 
-  function testThrowExceptionIfClassPathIsNotDefined()
+  function testProcessPrefixedFieldsAsRelatedActiveRecords()
   {
     $course = $this->_createCourseWithTwoLectures();
+    $lecture = new LectureForTest();
+    $course_info = $lecture->getRelationInfo('course'); 
 
     $db = new lmbSimpleDb(lmbToolkit :: instance()->getDefaultDbConnection());
-    $decorated = $db->select('lecture_for_test');
+    $sql = 'SELECT lecture_for_test.*, course_for_test.id as course__id, course_for_test.title as course__title 
+            FROM lecture_for_test LEFT JOIN course_for_test ON course_for_test.id = lecture_for_test.course_id';
+    $decorated = lmbDBAL :: fetch($sql);
 
-    $iterator = new lmbARRecordSetDecorator($decorated);
-    $iterator->rewind();
-    try
-    {
-      $iterator->current();
-      $this->assertTrue(false);
-    }
-    catch(lmbException $e){}
+    $iterator = new lmbARRecordSetDecorator($decorated, 'LectureForTest', null, array('course' => $course_info));
+    
+    // let's fetch all data in order to actually call rewind() and current();
+    $arr = $iterator->getArray();
+    
+    // now let's remove everything from db tables so we can be sure that processing is correct
+    $db->delete('lecture_for_test');
+    $db->delete('course_for_test');
+    
+    $this->assertEqual($arr[0]->getCourse()->getTitle(), $course->getTitle());
+    $this->assertEqual($arr[1]->getCourse()->getTitle(), $course->getTitle());
   }
 
   function testIfRecordIsEmpty()
   {
-    $iterator = new lmbARRecordSetDecorator(new lmbCollection());
-    $iterator->setClassPath('LectureForTest');
+    $iterator = new lmbARRecordSetDecorator(new lmbCollection(), 'LectureForTest');
     $iterator->rewind();
     $this->assertFalse($iterator->valid());
   }

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARRelationsDefinitionMethodsTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARRelationsDefinitionMethodsTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARRelationsDefinitionMethodsTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -6,8 +6,6 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
-require_once('limb/active_record/src/lmbActiveRecord.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
 
 class TestOneTableObjectWithRelationsByMethods extends lmbActiveRecord
 {

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARSubclassingTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARSubclassingTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARSubclassingTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -6,8 +6,6 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
-require_once('limb/active_record/src/lmbActiveRecord.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
 
 class TestOneTableTypedObject extends lmbActiveRecord
 {

Added: 3.x/trunk/limb/active_record/tests/cases/lmbARTestingObjectMother.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARTestingObjectMother.class.php	                        (rev 0)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARTestingObjectMother.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -0,0 +1,198 @@
+<?php
+class TestOneTableObject extends lmbActiveRecord
+{
+  protected $_db_table_name = 'test_one_table_object';
+}
+
+class PersonForTest extends lmbActiveRecord
+{
+  public $save_count = 0;
+  protected $_has_one = array('social_security' => array('field' => 'ss_id',
+                                                         'class' => 'SocialSecurityForTest',
+                                                         'can_be_null' => true));
+
+  function _onSave()
+  {
+    $this->save_count++;
+  }
+}
+
+class SocialSecurityForTest extends lmbActiveRecord
+{
+  protected $_belongs_to = array('person' => array('field' => 'ss_id',
+                                                   'class' => 'PersonForTest'));
+}
+
+class CourseForTest extends lmbActiveRecord
+{
+  protected $_db_table_name = 'course_for_test';
+  protected $_has_many = array('lectures' => array('field' => 'course_id',
+                                                   'class' => 'LectureForTest'),
+                               'alt_lectures' => array('field' => 'alt_course_id',
+                                                       'class' => 'LectureForTest'));
+
+  public $save_calls = 0;
+
+  function save()
+  {
+    parent :: save();
+    $this->save_calls++;
+  }
+}
+
+class LectureForTest extends lmbActiveRecord
+{
+  protected $_db_table_name = 'lecture_for_test';
+  protected $_many_belongs_to = array('course' => array('field' => 'course_id',
+                                                        'class' => 'CourseForTest'),
+                                      'alt_course' => array('field' => 'alt_course_id',
+                                                            'class' => 'CourseForTest',
+                                                            'can_be_null' => true));
+  protected $_test_validator;
+
+  function setValidator($validator)
+  {
+    $this->_test_validator = $validator;
+  }
+
+  function _createValidator()
+  {
+    if($this->_test_validator)
+      return $this->_test_validator;
+
+    return parent :: _createValidator();
+  }
+}
+
+class GroupForTest extends lmbActiveRecord
+{
+  protected $_db_table_name = 'group_for_test';
+
+  protected $_has_many_to_many = array('users' => array('field' => 'group_id',
+                                                        'foreign_field' => 'user_id',
+                                                        'table' => 'user2group_for_test',
+                                                        'class' => 'UserForTest'));
+
+  protected $_test_validator;
+
+  function setValidator($validator)
+  {
+    $this->_test_validator = $validator;
+  }
+
+  function _createValidator()
+  {
+    if($this->_test_validator)
+      return $this->_test_validator;
+
+    return parent :: _createValidator();
+  }
+}
+
+class UserForTest extends lmbActiveRecord
+{
+  protected $_db_table_name = 'user_for_test';
+
+  protected $_has_many_to_many = array('groups' => array('field' => 'user_id',
+                                                         'foreign_field' => 'group_id',
+                                                         'table' => 'user2group_for_test',
+                                                         'class' => 'GroupForTest'));
+
+  protected $_has_one = array('linked_object' => array('field' => 'linked_object_id',
+                                                       'class' => 'TestOneTableObject',
+                                                       'can_be_null' => true));
+}
+
+class lmbARTestingObjectMother
+{
+  function initOneTableObject()
+  {
+    $object = new TestOneTableObject();
+    $object->set('annotation', 'Annotation ' . rand(0, 1000));
+    $object->set('content', 'Content ' . rand(0, 1000));
+    $object->set('news_date', date("Y-m-d", time()));
+    $object->set('ordr', rand(0, 1000));
+    return $object;
+  }
+  
+  function createOneTableObject()
+  {
+    $object = $this->initOneTableObject();
+    $object->save();
+    return $object;
+  }
+  
+  function createPerson()
+  {
+    $person = new PersonForTest();
+    $person->setName('Person_' . rand(0, 1000));
+
+    $number = $this->createSocialSecurity($person);
+    $person->setSocialSecurity($number);
+    $person->save();
+    return $person;
+  }
+  
+  function createSocialSecurity($person)
+  {
+    $number = new SocialSecurityForTest();
+    $number->setCode(rand(0,1000));
+    $number->setPerson($person);
+    return $number; 
+  }
+  
+  function createCourse()
+  {
+    $course = new CourseForTest();
+    $course->setTitle('Course_'. rand(0, 100));
+    $course->save();
+    return $course;
+  }
+
+  function createLecture($course, $alt_course = null, $title = '')
+  {
+    $lecture = new LectureForTest();
+    $title = $title ? $title : 'Lecture_'. rand(0, 100);
+    $lecture->setTitle($title);
+    $lecture->setCourse($course);
+    
+    if($alt_course)
+      $lecture->setAltCourse($alt_course);
+      
+    $lecture->save();
+    return $lecture;
+  }
+  
+  function initUser($linked_object = null)
+  {
+    $user = new UserForTest();
+    $user->setFirstName('User_' . rand(0, 1000));
+    
+    if($linked_object)
+      $user->setLinkedObject($linked_object);
+
+    return $user;
+  }
+  
+  function createUser($linked_object = null)
+  {
+    $user = $this->initUser($linked_object);
+    $user->save();
+    return $user;
+  }
+  
+  function initGroup($title = '')
+  {
+    $group = new GroupForTest();
+    $title = $title ? $title : 'Group_' . rand(0, 1000);
+    $group->setTitle($title);
+    return $group;
+  }
+  
+  function createGroup($title = '')
+  {
+    $group = $this->initGroup($title);
+    $group->save();
+    return $group;
+  }
+}

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARTransactionTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARTransactionTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARTransactionTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -6,10 +6,20 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
-require_once('limb/active_record/src/lmbActiveRecord.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
 require_once(dirname(__FILE__) . '/lmbActiveRecordTest.class.php');//need TestOneTableObjectFailing
 
+class TestOneTableObjectFailing extends lmbActiveRecord
+{
+  var $fail;
+  protected $_db_table_name = 'test_one_table_object';
+
+  protected function _onAfterSave()
+  {
+    if(is_object($this->fail))
+      throw $this->fail;
+  }
+}
+
 class lmbARTransactionTest extends UnitTestCase
 {
   function setUp()

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARValidationTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARValidationTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARValidationTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -6,8 +6,6 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
-require_once('limb/active_record/src/lmbActiveRecord.class.php');
-require_once('limb/validation/src/lmbValidator.class.php');
 require_once('limb/validation/src/lmbErrorList.class.php');
 require_once(dirname(__FILE__) . '/lmbActiveRecordTest.class.php');//need TestOneTableObjectFailing
 

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARValueObjectTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARValueObjectTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARValueObjectTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -6,8 +6,6 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
-require_once('limb/active_record/src/lmbActiveRecord.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
 
 class TestingValueObject
 {
@@ -42,7 +40,7 @@
                                   'not_required_date' => array('field' => 'date_end',
                                                                'class' => 'TestingValueObject',
                                                                'getter' => 'getValue',
-  														       'can_be_null' => true));
+  														    'can_be_null' => true));
 }
 
 class LessonWithNullObject extends LessonForTest 

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbActiveRecordTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbActiveRecordTest.class.php	2008-01-15 14:11:34 UTC (rev 6690)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbActiveRecordTest.class.php	2008-01-15 14:55:59 UTC (rev 6691)
@@ -6,29 +6,7 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */
-require_once('limb/active_record/src/lmbActiveRecord.class.php');
-require_once('limb/dbal/src/criteria/lmbSQLRawCriteria.class.php');
-require_once('limb/dbal/src/lmbSimpleDb.class.php');
-require_once('limb/dbal/src/lmbTableGateway.class.php');
 
-class TestOneTableObject extends lmbActiveRecord
-{
-  protected $_db_table_name = 'test_one_table_object';
-  protected $dummy;
-}
-
-class TestOneTableObjectFailing extends lmbActiveRecord
-{
-  var $fail;
-  protected $_db_table_name = 'test_one_table_object';
-
-  protected function _onAfterSave()
-  {
-    if(is_object($this->fail))
-      throw $this->fail;
-  }
-}
-
 class TestOneTableObjectWithCustomDestroy extends lmbActiveRecord
 {
   protected $_db_table_name = 'test_one_table_object';
@@ -108,35 +86,10 @@
   protected $_default_sort_params = array('id' => 'DESC');
 }
 
-class lmbActiveRecordTest extends UnitTestCase
+class lmbActiveRecordTest extends lmbARBaseTestCase
 {
-  var $conn;
-  var $db;
-  var $class_name = 'TestOneTableObject';
+  protected $tables_to_cleanup = array('test_one_table_object', 'lecture_for_test', 'course_for_test');
 
-  function setUp()
-  {
-    $toolkit = lmbToolkit :: save();
-    $this->conn = $toolkit->getDefaultDbConnection();
-    $this->db = new lmbSimpleDb($this->conn);
-
-    $this->_cleanUp();
-  }
-
-  function tearDown()
-  {
-    $this->_cleanUp();
-
-    $this->conn->disconnect();
-
-    lmbToolkit :: restore();
-  }
-
-  function _cleanUp()
-  {
-    $this->db->delete('test_one_table_object');
-  }
-
   function testArrayAccessConsidersDbFields()
   {
     $object = new TestOneTableObject();
@@ -187,7 +140,7 @@
 
   function testDontCreateNewRecordTwice()
   {
-    $object = $this->_initActiveRecordWithData(new TestOneTableObject());
+    $object = $this->creator->initOneTableObject();
 
     $object->save();
     $object->save();
@@ -199,7 +152,7 @@
 
   function testIsNew()
   {
-    $object = $this->_initActiveRecordWithData(new TestOneTableObject());
+    $object = $this->creator->initOneTableObject();
     $this->assertTrue($object->isNew());
 
     $object->save();
@@ -212,7 +165,7 @@
 
   function testDetach()
   {
-    $object = $this->_initActiveRecordWithData(new TestOneTableObject());
+    $object = $this->creator->initOneTableObject();
     $this->assertTrue($object->isNew());
 
     $object->save();
@@ -231,7 +184,7 @@
 
   function testSaveUpdate()
   {
-    $object = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object = $this->creator->createOneTableObject();
 
     $object->set('annotation', $annotation = 'Other annotation');
     $object->set('content', $content = 'Other content');
@@ -293,8 +246,8 @@
 
   function testFindById()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $found = lmbActiveRecord :: findById('TestOneTableObject', $object2->getId());
     $this->assertEqual($found->export(), $object2->export());
@@ -321,8 +274,8 @@
 
   function testLoadById()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $loaded = new TestOneTableObject();
     $loaded->loadById($object2->getId());
@@ -343,7 +296,7 @@
 
   function testPassingIntToConstructorLoadsObject()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
 
     $object2 = new TestOneTableObject($object1->getId());
     $this->assertEqual($object2->export(), $object1->export());
@@ -362,8 +315,8 @@
 
   function testFindFirst()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
     $this->assertFalse($object2->isNew());
 
     $found = lmbActiveRecord :: findFirst('TestOneTableObject', array('criteria' => 'id=' . $object1->getId()));
@@ -382,8 +335,8 @@
 
   function testFindFirstConvertStringToCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
     $this->assertFalse($object2->isNew());
 
     $found = lmbActiveRecord :: findFirst('TestOneTableObject', 'id=' . $object1->getId());
@@ -402,8 +355,8 @@
 
   function testFindFirstConvertObjectToCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
     $this->assertFalse($object2->isNew());
 
     $found = lmbActiveRecord :: findFirst('TestOneTableObject', new lmbSQLRawCriteria('id=' . $object1->getId()));
@@ -422,8 +375,8 @@
 
   function testFindFirstConvertArrayToCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
     $this->assertFalse($object2->isNew());
 
     $found = lmbActiveRecord :: findFirst('TestOneTableObject', array('id=?', $object1->getId()));
@@ -442,8 +395,8 @@
 
   function testFindFirstWithSortParams()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $found = lmbActiveRecord :: findFirst('TestOneTableObject', array('sort' => array('id' => 'DESC')));
     $this->assertEqual($found->get('id'), $object2->getId());
@@ -473,8 +426,8 @@
 
   function testFindOneAlias()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
     $this->assertFalse($object2->isNew());
 
     $found = lmbActiveRecord :: findOne('TestOneTableObject', 'id=' . $object1->getId());
@@ -493,8 +446,8 @@
 
   function testFindAllRecordsNoCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $object = new TestOneTableObject();
     $rs = $object->findAllRecords();
@@ -506,8 +459,8 @@
 
   function testFildAllRecordsWithCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $object = new TestOneTableObject();
     $rs = $object->findAllRecords(new lmbSQLFieldCriteria('id', $object2->getId()));
@@ -519,8 +472,8 @@
 
   function testFindAllNoCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $rs = lmbActiveRecord :: find('TestOneTableObject');
     $rs->rewind();
@@ -538,8 +491,8 @@
 
   function testFindAllWithCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $rs = lmbActiveRecord :: find('TestOneTableObject', array('criteria' => new lmbSQLFieldCriteria('id', $object2->getId())));
     $rs->rewind();
@@ -557,8 +510,8 @@
 
   function testFindConvertObjectToCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $rs = lmbActiveRecord :: find('TestOneTableObject', new lmbSQLFieldCriteria('id', $object2->getId()));
     $rs->rewind();
@@ -576,8 +529,8 @@
 
   function testFindConvertStringToCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $rs = lmbActiveRecord :: find('TestOneTableObject', 'id=' . $object2->getId());
     $rs->rewind();
@@ -595,8 +548,8 @@
 
   function testFindConvertArrayToCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $rs = lmbActiveRecord :: find('TestOneTableObject', array('id=?', $object2->getId()));
     $rs->rewind();
@@ -614,8 +567,8 @@
 
   function testFindWithIntegerCallsFindById()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $object = lmbActiveRecord :: find('TestOneTableObject', $object2->getId());
     $this->assertEqual($object2->getId(), $object->getId());
@@ -635,8 +588,8 @@
 
   function testFindAllWithSortParams()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $rs = lmbActiveRecord :: find('TestOneTableObject', array('sort' => array('id' => 'DESC')));
     $arr = $rs->getArray();
@@ -672,10 +625,39 @@
     $this->assertEqual($arr[1]->get('id'), $object1->getId());
   }
 
+  function testFindWithRelatedObjects_UsingWithParam()
+  {
+    $course1 = $this->creator->createCourse();
+    $course2 = $this->creator->createCourse();
+    $alt_course1 = $this->creator->createCourse();
+    $alt_course2 = $this->creator->createCourse();
+    $lecture1 = $this->creator->createLecture($course1, $alt_course1);
+    $lecture2 = $this->creator->createLecture($course2, $alt_course2);
+    $lecture3 = $this->creator->createLecture($course1, $alt_course2);
+    
+    $rs = lmbActiveRecord :: find('LectureForTest', array('with' => 'course, alt_course'));
+    $arr = $rs->getArray();
+
+    //make sure we really eager fetching
+    $this->db->delete('course_for_test'); 
+    
+    $this->assertEqual($arr[0]->getId(), $lecture1->getId());
+    $this->assertEqual($arr[0]->getCourse()->getTitle(), $course1->getTitle());
+    $this->assertEqual($arr[0]->getAltCourse()->getTitle(), $alt_course1->getTitle());
+    
+    $this->assertEqual($arr[1]->getId(), $lecture2->getId());
+    $this->assertEqual($arr[1]->getCourse()->getTitle(), $course2->getTitle());
+    $this->assertEqual($arr[1]->getAltCourse()->getTitle(), $alt_course2->getTitle());
+    
+    $this->assertEqual($arr[2]->getId(), $lecture3->getId());
+    $this->assertEqual($arr[2]->getCourse()->getTitle(), $course1->getTitle());
+    $this->assertEqual($arr[2]->getAltCourse()->getTitle(), $alt_course2->getTitle());
+  }
+
   function testFindBySql()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $rs = lmbActiveRecord :: findBySql('TestOneTableObject', 'select * from test_one_table_object order by id desc');
     $rs->rewind();
@@ -697,8 +679,8 @@
 
   function testFindFirstBySql()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $object = lmbActiveRecord :: findFirstBySql('TestOneTableObject', 'select * from test_one_table_object order by id desc');
     $this->assertEqual($object2->getId(), $object->getId());
@@ -710,8 +692,8 @@
 
   function testFindOneBySqlAlias()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $object = lmbActiveRecord :: findOneBySql('TestOneTableObject', 'select * from test_one_table_object order by id desc');
     $this->assertEqual($object2->getId(), $object->getId());
@@ -723,9 +705,9 @@
 
   function testFindByIds()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object3 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
+    $object3 = $this->creator->createOneTableObject();
 
     $rs = lmbActiveRecord :: findByIds('TestOneTableObject',
                                        array($object1->getId(), $object3->getId()),
@@ -749,8 +731,8 @@
 
   function testFindByIdsReturnEmptyIteratorIfNoIds()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $rs = lmbActiveRecord :: findByIds('TestOneTableObject', array());
     $rs->rewind();
@@ -764,8 +746,8 @@
 
   function testGetDatasetActsAsStaticFind()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $ds = $object2->getDataset();
     $this->assertEqual($ds->at(0)->getId(), $object1->getId());
@@ -774,8 +756,8 @@
 
   function testDelete()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     lmbActiveRecord :: delete('TestOneTableObject');
     $this->assertEqual($this->db->count('test_one_table_object'), 0);
@@ -783,8 +765,8 @@
 
   function testDeleteShort()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     TestOneTableObject :: delete();
     $this->assertEqual($this->db->count('test_one_table_object'), 0);
@@ -792,8 +774,8 @@
 
   function testDeleteCallsDestroy()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObjectWithCustomDestroy());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObjectWithCustomDestroy());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     ob_start();
     lmbActiveRecord :: delete('TestOneTableObjectWithCustomDestroy');
@@ -806,8 +788,8 @@
 
   function testDeleteShortCallsDestroy()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObjectWithCustomDestroy());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObjectWithCustomDestroy());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     ob_start();
     TestOneTableObjectWithCustomDestroy :: delete();
@@ -820,8 +802,8 @@
 
   function testDeleteByCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $criteria = new lmbSQLFieldCriteria('id', $object2->getId());
     lmbActiveRecord :: delete('TestOneTableObject', $criteria);
@@ -834,8 +816,8 @@
 
   function testDeleteShortByCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $criteria = new lmbSQLFieldCriteria('id', $object2->getId());
     TestOneTableObject :: delete($criteria);
@@ -848,8 +830,8 @@
 
   function testDeleteRaw()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     lmbActiveRecord :: deleteRaw('TestOneTableObject');
 
@@ -858,8 +840,8 @@
 
   function testDeleteShortRaw()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     TestOneTableObject :: deleteRaw();
 
@@ -868,8 +850,8 @@
 
   function testDeleteRawDoesntCallDestroy()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObjectWithCustomDestroy());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObjectWithCustomDestroy());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     ob_start();
     lmbActiveRecord :: deleteRaw('TestOneTableObjectWithCustomDestroy');
@@ -882,8 +864,8 @@
 
   function testDeleteShortRawDoesntCallDestroy()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObjectWithCustomDestroy());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObjectWithCustomDestroy());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     ob_start();
     TestOneTableObjectWithCustomDestroy :: deleteRaw();
@@ -896,8 +878,8 @@
 
   function testDeleteRawByCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $criteria = new lmbSQLFieldCriteria('id', $object2->getId());
     lmbActiveRecord :: deleteRaw('TestOneTableObject', $criteria);
@@ -910,8 +892,8 @@
 
   function testDeleteShortRawByCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     $criteria = new lmbSQLFieldCriteria('id', $object2->getId());
     TestOneTableObject :: deleteRaw($criteria);
@@ -924,8 +906,8 @@
 
   function testUpdateRawAllWithArraySet()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     lmbActiveRecord :: updateRaw('TestOneTableObject', array('content' => 'blah'));
 
@@ -940,8 +922,8 @@
 
   function testUpdateShortRawAllWithArraySet()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     TestOneTableObject :: updateRaw(array('content' => 'blah'));
 
@@ -956,8 +938,8 @@
 
   function testUpdateRawAllWithRawValues()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     lmbActiveRecord :: updateRaw('TestOneTableObject', 'ordr=1');
 
@@ -972,8 +954,8 @@
 
   function testUpdateShortRawAllWithRawValues()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     TestOneTableObject :: updateRaw('ordr=1');
 
@@ -988,8 +970,8 @@
 
   function testUpdateRawWithCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     lmbActiveRecord :: updateRaw('TestOneTableObject', array('content' => 'blah'), 'id=' . $object2->getId());
 
@@ -1004,8 +986,8 @@
 
   function testUpdateShortRawWithCriteria()
   {
-    $object1 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
-    $object2 = $this->_initActiveRecordWithDataAndSave(new TestOneTableObject());
+    $object1 = $this->creator->createOneTableObject();
+    $object2 = $this->creator->createOneTableObject();
 
     TestOneTableObject :: updateRaw(array('content' => 'blah'), 'id=' . $object2->getId());
 
@@ -1023,21 +1005,5 @@
     $object = new TestOneTableObject();
     $this->assertEqual($object->getTableName(), 'test_one_table_object');
   }
-
-  protected function _initActiveRecordWithData($object)
-  {
-    $object->set('annotation', 'Annotation ' . time());
-    $object->set('content', 'Content ' . time());
-    $object->set('news_date', date("Y-m-d", time()));
-    $object->set('ordr', mt_rand());
-    return $object;
-  }
-
-  protected function _initActiveRecordWithDataAndSave($object)
-  {
-    $this->_initActiveRecordWithData($object);
-    $object->save();
-    return $object;
-  }
 }
 



More information about the limb-svn mailing list