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

svn at limb-project.com svn at limb-project.com
Thu Jan 17 18:41:52 MSK 2008


Author: serega
Date: 2008-01-17 18:41:51 +0300 (Thu, 17 Jan 2008)
New Revision: 6694
URL: http://fisheye.limb-project.com/changelog/limb/?cs=6694

Added:
   3.x/trunk/limb/active_record/src/lmbARRecordSetJoinDecorator.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARRecordSetJoinDecoratorTest.class.php
Modified:
   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/lmbARQuery.class.php
   3.x/trunk/limb/active_record/src/lmbARRecordSetAttachDecorator.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/lmbAROneToManyCollectionTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARQueryTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARRecordSetDecoratorTest.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbARTestingObjectMother.class.php
   3.x/trunk/limb/active_record/tests/cases/lmbActiveRecordTest.class.php
Log:
-- lmbARQuery :: create() factory method added. lmbActiveRecord, lmbARCollection and it's children now uses lmbARQuery :: create() internally
-- nesting eager fetching functionality added. 
  e.g lmbARQuery :: create('Author')->attach('books' => array('attach' => 'categories'))->fetch() - will load all authors with books and books with categories in 3 sql queries.
      lmbARQuery :: create('lecture')->with('course' => array('with' => 'program')) - will load lectures with courses and courses with programs in 1 sql query (we suppose programs has many courser and courses has many lectures, lectures many belongs to courses, courses many belongs to programs)
-- lmbARRecordSetJoinDecorator added to support a part of nesting eager fetching functionality

Modified: 3.x/trunk/limb/active_record/src/lmbARManyToManyCollection.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbARManyToManyCollection.class.php	2008-01-17 15:36:47 UTC (rev 6693)
+++ 3.x/trunk/limb/active_record/src/lmbARManyToManyCollection.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -17,9 +17,9 @@
  */
 class lmbARManyToManyCollection extends lmbARRelationCollection
 {
-  protected function _createARQuery($magic_params = array())
+  protected function _createARQuery($params = array())
   {
-    $query = self :: createFullARQueryForRelation($this->relation_info, $this->conn, $magic_params);
+    $query = self :: createFullARQueryForRelation($this->relation_info, $this->conn, $params);
     
     $join_table = $this->conn->quoteIdentifier($this->relation_info['table']);
     $field = $this->conn->quoteIdentifier($this->relation_info['field']);
@@ -28,12 +28,12 @@
     return $query; 
   }
   
-  static function createFullARQueryForRelation($relation_info, $conn, $magic_params = array())
+  static function createFullARQueryForRelation($relation_info, $conn, $params = array())
   {
-    return parent :: createFullARQueryForRelation(__CLASS__, $relation_info, $conn, $magic_params);
+    return parent :: createFullARQueryForRelation(__CLASS__, $relation_info, $conn, $params);
   }
   
-  static function createCoreARQueryForRelation($relation_info, $conn)
+  static function createCoreARQueryForRelation($relation_info, $conn, $params = array())
   {
     $class = $relation_info['class'];
     $object = new $class();
@@ -46,7 +46,7 @@
     $sql = "SELECT %fields% FROM {$table} INNER JOIN {$join_table} ON {$table}.{$object->getPrimaryKeyName()} = {$join_table}.{$foreign_field}" . 
            " %tables% %left_join% %where% %group% %having% %order%";
 
-    $query = new lmbARQuery($class, $conn, $sql);
+    $query = lmbARQuery :: create($class, $params, $conn, $sql); 
 
     $fields = $object->getDbTable()->getColumnsForSelect();
     foreach($fields as $field => $alias)

Modified: 3.x/trunk/limb/active_record/src/lmbAROneToManyCollection.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbAROneToManyCollection.class.php	2008-01-17 15:36:47 UTC (rev 6693)
+++ 3.x/trunk/limb/active_record/src/lmbAROneToManyCollection.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -16,21 +16,21 @@
  */
 class lmbAROneToManyCollection extends lmbARRelationCollection
 {
-  protected function _createARQuery($magic_params = array())
+  protected function _createARQuery($params = array())
   {
-    $query = self :: createFullARQueryForRelation($this->relation_info, $this->conn, $magic_params);
+    $query = self :: createFullARQueryForRelation($this->relation_info, $this->conn, $params);
     $query->addCriteria(new lmbSQLFieldCriteria($this->relation_info['field'], $this->owner->getId()));
     return $query;
   }
   
-  static function createFullARQueryForRelation($relation_info, $conn, $magic_params = array())
+  static function createFullARQueryForRelation($relation_info, $conn, $params = array())
   {
-    return parent :: createFullARQueryForRelation(__CLASS__, $relation_info, $conn, $magic_params);
+    return parent :: createFullARQueryForRelation(__CLASS__, $relation_info, $conn, $params);
   }
   
-  static function createCoreARQueryForRelation($relation_info, $conn)
+  static function createCoreARQueryForRelation($relation_info, $conn, $params = array())
   {
-    return new lmbARQuery($relation_info['class'], $conn);
+    return lmbARQuery :: create($relation_info['class'], $params, $conn);
   }
   
   function add($object)

Modified: 3.x/trunk/limb/active_record/src/lmbARQuery.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbARQuery.class.php	2008-01-17 15:36:47 UTC (rev 6693)
+++ 3.x/trunk/limb/active_record/src/lmbARQuery.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -8,6 +8,7 @@
  */
 lmb_require('limb/dbal/src/query/lmbSelectRawQuery.class.php');
 lmb_require('limb/active_record/src/lmbARRecordSetAttachDecorator.class.php');
+lmb_require('limb/active_record/src/lmbARRecordSetJoinDecorator.class.php');
 
 class lmbARQuery extends lmbSelectRawQuery
 {
@@ -33,37 +34,10 @@
     }
   }
   
-  function with($relation_name)
+  function with($relation_name, $params = array())
   {
-    $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;
-    }
+    $this->_with[$relation_name] = $params;
+
     return $this;
   }
   
@@ -82,12 +56,72 @@
   
   function fetch($decorate = true)
   {
+    $this->_applyJoins($this->_base_object, $this->_with);
+    
     $rs = parent :: fetch();
-    $rs = $this->_decorateWithAttachDecorator($rs);
+
+    if($decorate)
+      $rs = new lmbARRecordSetDecorator($rs, $this->_base_class_name, $this->_conn);
     
-    if($decorate)
-      return new lmbARRecordSetDecorator($rs, $this->_base_class_name, $this->_conn, $this->_with);
+    $rs = $this->_decorateWithJoinDecorator($rs);
+    
+    return $this->_decorateWithAttachDecorator($rs);
+  }
+  
+  protected function _applyJoins($base_object, $joins, $parent_relation_name = '')
+  {
+    if(is_string($joins))
+      $joins = array($joins => array());
+    
+    if($parent_relation_name)
+      $prefix = $parent_relation_name . '__';
     else
+    {
+      $parent_relation_name = $base_object->getTableName();
+      $prefix = '';
+    }
+    
+    foreach($joins as $relation_name => $params)
+    {
+      $relation_info = $base_object->getRelationInfo($relation_name);
+      
+      if(!$relation_info || !isset($relation_info['class']))
+        throw new lmbException('Relation info "' . $relation_name .'" not found in "' . get_class($base_object) . '" or does not contain "class" property');
+      
+      $class_name = $relation_info['class'];
+      $object = new $class_name(null, $this->_conn);
+      $this->_addFieldsForObject($object, $prefix . $relation_name, $prefix . $relation_name . '__');
+      
+      $relation_type = $base_object->getRelationType($relation_name);
+      switch($relation_type)
+      {
+        case lmbActiveRecord :: HAS_ONE:
+        case lmbActiveRecord :: MANY_BELONGS_TO:
+          $this->addLeftJoin($object->getTableName(), 
+                             $object->getPrimaryKeyName(),
+                             $parent_relation_name, 
+                             $relation_info['field'],
+                             $prefix . $relation_name);
+        break;
+        case lmbActiveRecord :: BELONGS_TO:
+          $this->addLeftJoin($object->getTableName(), 
+                             $relation_info['field'],
+                             $parent_relation_name, 
+                             $base_object->getPrimaryKeyName(),
+                             $prefix . $relation_name);
+        break;
+      }
+      
+      if(isset($params['with']))
+        $this->_applyJoins($object, $params['with'], $prefix . $relation_name);
+    }
+  }
+
+  protected function _decorateWithJoinDecorator($rs)
+  {
+    if(count($this->_with))
+      return new lmbARRecordSetJoinDecorator($rs, $this->_base_object, $this->_conn, $this->_with);
+    else
       return $rs;
   }
   
@@ -98,4 +132,60 @@
     else
       return $rs;
   }
+  
+  static function create($class_name, $params = array(), $conn = null, $sql = '')
+  {
+    if(!$conn)
+      $conn = lmbToolkit :: instance()->getDefaultDbConnection();
+    
+    $object = new $class_name;
+    $query = new lmbARQuery($class_name, $conn, $sql);
+
+    if(isset($params['criteria']) && $params['criteria'])
+      $criteria = lmbSQLCriteria :: objectify($params['criteria']); 
+    else
+      $criteria = lmbSQLCriteria :: create();
+
+    $has_class_criteria = false;
+    if(isset($params['class']))
+    {
+      $filter_object = new $params['class'];
+      $criteria = $filter_object->addClassCriteria($criteria);
+      $has_class_criteria = true;
+    }
+
+    if(!$has_class_criteria)
+      $object->addClassCriteria($criteria);
+    
+    $query->where($criteria);
+    
+    $sort_params = (isset($params['sort']) && $params['sort']) ? $params['sort'] : $object->getDefaultSortParams();
+    $query->order($sort_params);
+
+    $with = (isset($params['with']) && $params['with']) ? $params['with'] : array();
+    if(!is_array($with))
+      $with = explode(',', $with);
+    
+    foreach($with as $relation_name=> $params_or_relation_name)
+    {
+      if(is_numeric($relation_name))
+        $query->with(trim($params_or_relation_name));
+      else
+        $query->with(trim($relation_name), $params_or_relation_name);
+    }
+
+    $attach = (isset($params['attach']) && $params['attach']) ? $params['attach'] : array();
+    if(!is_array($attach))
+      $attach = explode(',', $attach);
+    
+    foreach($attach as $relation_name => $params_or_relation_name)
+    {
+      if(is_numeric($relation_name))
+        $query->attach(trim($params_or_relation_name));
+      else
+        $query->attach(trim($relation_name), $params_or_relation_name);
+    }
+    
+    return $query;
+  }
 }

Modified: 3.x/trunk/limb/active_record/src/lmbARRecordSetAttachDecorator.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbARRecordSetAttachDecorator.class.php	2008-01-17 15:36:47 UTC (rev 6693)
+++ 3.x/trunk/limb/active_record/src/lmbARRecordSetAttachDecorator.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -21,12 +21,16 @@
   protected $base_object;
   protected $conn;
   protected $attach_relations = array();
+  protected $prefix = "";
 
-  function __construct($record_set, $base_object, $conn = null, $attach_relations = array())
+  function __construct($record_set, $base_object, $conn = null, $attach_relations = array(), $prefix = "")
   {
     $this->base_object = $base_object;
     $this->conn = $conn;
+    if(is_string($attach_relations))
+      $attach_relations = array($attach_relations => array());
     $this->attach_relations = $attach_relations;
+    $this->prefix = $prefix;
 
     parent :: __construct($record_set);
   }
@@ -45,14 +49,14 @@
       {
         case lmbActiveRecord :: HAS_ONE:
         case lmbActiveRecord :: MANY_BELONGS_TO:
-          $ids = lmbArrayHelper :: getColumnValues($relation_info['field'], $this->iterator);
+          $ids = lmbArrayHelper :: getColumnValues($this->prefix . $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);
+          $ids = lmbArrayHelper :: getColumnValues($this->prefix . $this->base_object->getPrimaryKeyName(), $this->iterator);
           
           $criteria = lmbSQLCriteria :: in($relation_info['field'], $ids);
           $params['criteria'] = isset($params['criteria']) ? $params['criteria']->addAnd($criteria) : $criteria;
@@ -69,7 +73,7 @@
             
           $query = lmbAROneToManyCollection :: createFullARQueryForRelation($relation_info, $this->conn, $params);
           
-          $ids = lmbArrayHelper :: getColumnValues($this->base_object->getPrimaryKeyName(), $this->iterator);
+          $ids = lmbArrayHelper :: getColumnValues($this->prefix . $this->base_object->getPrimaryKeyName(), $this->iterator);
           $query->addCriteria(lmbSQLCriteria :: in($relation_info['field'], $ids));
           
           $attached_objects = $query->fetch();
@@ -86,7 +90,7 @@
           $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);
+          $ids = lmbArrayHelper :: getColumnValues($this->prefix . $this->base_object->getPrimaryKeyName(), $this->iterator);
           $query->addCriteria(lmbSQLCriteria :: in($relation_info['field'], $ids));
           
           $attached_objects = $query->fetch();
@@ -102,7 +106,9 @@
   
   function current()
   {
-    $record = parent :: current();
+    $object = parent :: current();
+    
+    $fields = new lmbSet();
 
     foreach($this->attach_relations as $relation_name => $params)
     {
@@ -113,16 +119,22 @@
       {
         case lmbActiveRecord :: HAS_ONE:
         case lmbActiveRecord :: MANY_BELONGS_TO:
-          $record->set($relation_name, $this->loaded_attaches[$relation_name][$record->get($relation_info['field'])]);
+          $fields->set($this->prefix . $relation_name, $this->loaded_attaches[$relation_name][$object->get($this->prefix . $relation_info['field'])]);
         break;
         case lmbActiveRecord :: BELONGS_TO:
+          $fields->set($this->prefix . $relation_name, $this->loaded_attaches[$relation_name][$object->get($this->prefix . $this->base_object->getPrimaryKeyName())]);
+        break;
         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())]);
+          $collection = $this->base_object->createRelationCollection($relation_name);
+          $collection->setDataset(new lmbCollection($this->loaded_attaches[$relation_name][$object->get($this->prefix . $this->base_object->getPrimaryKeyName())]));
+          $fields->set($this->prefix . $relation_name, $collection);
         break;
       }
     }
-    return $record;
+    
+    $object->loadFromRecord($fields);
+    return $object;
   }
 
   function at($pos)

Modified: 3.x/trunk/limb/active_record/src/lmbARRecordSetDecorator.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbARRecordSetDecorator.class.php	2008-01-17 15:36:47 UTC (rev 6693)
+++ 3.x/trunk/limb/active_record/src/lmbARRecordSetDecorator.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -20,13 +20,11 @@
 {
   protected $class_path;
   protected $conn;
-  protected $with_relations = array();
 
-  function __construct($record_set, $class_path, $conn = null, $with_relations = array())
+  function __construct($record_set, $class_path, $conn = null)
   {
     $this->class_path = $class_path;
     $this->conn = $conn;
-    $this->with_relations = $with_relations;
 
     parent :: __construct($record_set);
   }
@@ -42,35 +40,10 @@
   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)
   {
     if($path = $record->get(lmbActiveRecord :: getInheritanceField()))

Added: 3.x/trunk/limb/active_record/src/lmbARRecordSetJoinDecorator.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbARRecordSetJoinDecorator.class.php	                        (rev 0)
+++ 3.x/trunk/limb/active_record/src/lmbARRecordSetJoinDecorator.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -0,0 +1,99 @@
+<?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 lmbARRecordSetDecorator.
+ *
+ * @package active_record
+ * @version $Id: lmbARRecordSetDecorator.class.php 6691 2008-01-15 14:55:59Z serega $
+ */
+class lmbARRecordSetJoinDecorator extends lmbCollectionDecorator
+{
+  protected $base_object;
+  protected $conn;
+  protected $join_relations = array();
+  protected $prefix;
+
+  function __construct($record_set, $base_object, $conn = null, $join_relations = array(), $prefix = '')
+  {
+    $this->base_object = $base_object;
+    $this->conn = $conn;
+    if(is_string($join_relations))
+      $join_relations = array($join_relations => array());     
+    $this->join_relations = $join_relations;
+    $this->prefix = $prefix;
+
+    parent :: __construct($record_set);
+  }
+  
+  function rewind()
+  {
+    foreach($this->join_relations as $relation_name => $params)
+    {
+      $relation_info = $this->base_object->getRelationInfo($relation_name);
+      $object = new $relation_info['class'];
+      if(isset($params['with']))
+        $this->iterator = new lmbARRecordSetJoinDecorator($this->iterator, $object, $this->conn, $params['with'], $this->prefix . $relation_name . '__');
+      if(isset($params['attach']))
+        $this->iterator = new lmbARRecordSetAttachDecorator($this->iterator, $object, $this->conn, $params['attach'], $relation_name . '__');
+    }
+    
+    parent :: rewind();
+  }
+
+  function current()
+  {
+    if(!$record = parent :: current())
+      return null;
+
+    $this->_extractPrefixedFieldsAsActiveRecords($record);
+    
+    return $record;
+  }
+
+  protected function _extractPrefixedFieldsAsActiveRecords($record)
+  {
+    foreach($this->join_relations as $relation_name => $params)
+    {
+      $relation_info = $this->base_object->getRelationInfo($relation_name); 
+      $fields = new lmbSet();
+      $prefix = $this->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($this->prefix . $relation_name, $related_object);
+    }
+  }
+
+  function at($pos)
+  {
+    if(!$record = parent :: at($pos))
+      return null;
+
+    $this->_extractPrefixedFieldsAsActiveRecords($record);
+    
+    return $record;
+  }
+}
+
+

Modified: 3.x/trunk/limb/active_record/src/lmbARRelationCollection.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbARRelationCollection.class.php	2008-01-17 15:36:47 UTC (rev 6693)
+++ 3.x/trunk/limb/active_record/src/lmbARRelationCollection.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -25,7 +25,8 @@
   protected $conn;
   protected $is_owner_new;
   protected $decorators = array();
-  protected $fetch_with_relations = array();
+  protected $join_relations = array();
+  protected $attach_relations = array();
   protected $default_params = array();
 
   function __construct($relation, $owner, $criteria = null, $conn = null)
@@ -33,8 +34,8 @@
     $this->relation = $relation;
     $this->owner = $owner;
     $this->relation_info = $owner->getRelationInfo($relation);
-    $this->default_params['criteria'] = lmbSQLCriteria :: objectify($criteria);
-    $this->default_params['sort'] = array();
+    if($criteria)
+      $this->default_params['criteria'] = lmbSQLCriteria :: objectify($criteria);
 
     if(is_object($conn))
       $this->conn = $conn;
@@ -54,6 +55,11 @@
     $this->is_owner_new = $this->owner->isNew();
     $this->dataset = null;
   }
+  
+  function setDataset($dataset)
+  {
+    $this->dataset = $dataset;
+  }
 
   protected function _ensureDataset()
   {
@@ -74,14 +80,22 @@
     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'];
+    if(isset($this->default_params['criteria']))
+    {
+      if(isset($magic_params['criteria']))
+        $magic_params['criteria']->addAnd($this->default_params['criteria']);
+      else
+        $magic_params['criteria'] = $this->default_params['criteria'];
+    }
     
+    if(!isset($magic_params['sort']) && isset($this->default_params['sort']))
+      $magic_params['sort'] = $this->default_params['sort'];
+    
+    $magic_params['with'] = $this->join_relations;
+    $magic_params['attach'] = $this->attach_relations;
+    
     $query = $this->_createARQuery($magic_params);
     
-    foreach($this->fetch_with_relations as $relation)
-      $query->with($relation);
-    
     $rs = $query->fetch();
     
     return $this->_applyDecorators($rs);
@@ -95,43 +109,29 @@
       return $rs->current();
   }
   
-  function with($relation_name)
+  function with($relation_name, $params = array())
   {
-    $this->fetch_with_relations[] = $relation_name;
+    $this->join_relations[$relation_name] = $params;
     return $this;
   }
 
-  static function createFullARQueryForRelation($class, $relation_info, $conn, $magic_params = array())
+  function attach($relation_name, $params = array())
   {
-    $object = new $relation_info['class'];
+    $this->attach_relations[$relation_name] = $params;
+    return $this;
+  }
 
-    $criteria = isset($magic_params['criteria']) ? $magic_params['criteria'] : new lmbSQLCriteria();
+  static function createFullARQueryForRelation($calling_class, $relation_info, $conn, $params = array())
+  {
+    if(!isset($params['sort']) && isset($relation_info['sort_params']))
+      $params['sort'] = $relation_info['sort_params'];
     
-    $has_class_criteria = false;
-    if(isset($magic_params['class']))
-    {
-      $filter_object = new $magic_params['class'];
-      $criteria = $filter_object->addClassCriteria($criteria);
-      $has_class_criteria = true;
-    }
-
-    if(!$has_class_criteria)
-      $object->addClassCriteria($criteria);
-
-    $query = call_user_func_array(array($class, 'createCoreARQueryForRelation'), array($relation_info, $conn));
+    $query = call_user_func_array(array($calling_class, 'createCoreARQueryForRelation'), array($relation_info, $conn, $params));
     
-    $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 static function createCoreARQueryForRelation($relation_info, $conn, $params = array());
   
   abstract protected function _createARQuery($magic_params = array());
 

Modified: 3.x/trunk/limb/active_record/src/lmbActiveRecord.class.php
===================================================================
--- 3.x/trunk/limb/active_record/src/lmbActiveRecord.class.php	2008-01-17 15:36:47 UTC (rev 6693)
+++ 3.x/trunk/limb/active_record/src/lmbActiveRecord.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -684,7 +684,7 @@
            $this->_hasManyToManyRelation($relation);
   }
   /**
-   *  Generic magis getter for any attribute
+   *  Generic magic setter for any attribute
    *  @param string property name
    *  @param mixed property value
    */
@@ -1553,7 +1553,7 @@
    */
   protected function _find($params = array())
   {
-    $query = $this->_createARQuery($params);
+    $query = lmbARQuery :: create(get_class($this), $params, $this->_db_conn);
     $rs = $query->fetch();
 
     $return_first = false;
@@ -1580,28 +1580,6 @@
       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
@@ -1610,10 +1588,8 @@
    */
   function findAllRecords($criteria = null, $sort_params = array())
   {
-    $query = $this->_createARQuery(array('criteria' => $criteria, 'sort' => $sort_params));
+    $query = lmbARQuery :: create(get_class($this), $params = array('criteria' => $criteria, 'sort' => $sort_params), $this->_db_conn);
     return $query->fetch($decorate = false);
-    
-    return $this->_db_table->select($this->addClassCriteria($criteria), $sort_params, $columns);
   }
   /**
    *  Adds class name criterion to passed in criteria
@@ -1671,7 +1647,7 @@
     $this->_is_new = false;
   }
   /**
-   *  Loads current object with data from database record, overwrites any previous data, marks object dirty and unsets new status
+   *  Loads current object with data from database record, overwrites any previous data, resets object dirtiness and unsets new status
    *  @param object database record object
    */
   function loadFromRecord($record)

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-17 15:36:47 UTC (rev 6693)
+++ 3.x/trunk/limb/active_record/tests/cases/.fixture/init_tests.mysql	2008-01-17 15:41:51 UTC (rev 6694)
@@ -58,9 +58,17 @@
 PRIMARY KEY  (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
+DROP TABLE IF EXISTS `program_for_test`;
+CREATE TABLE `program_for_test` (
+  `id` bigint(20) NOT NULL auto_increment,
+  `title` varchar(255) default NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
 DROP TABLE IF EXISTS `course_for_test`;
 CREATE TABLE `course_for_test` (
  `id` bigint(20) NOT NULL auto_increment,
+ `program_id` bigint(20) default NULL,
  `title` varchar(255) default NULL,
  PRIMARY KEY  (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

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-17 15:36:47 UTC (rev 6693)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbAROneToManyCollectionTest.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -662,7 +662,6 @@
     $l->setTitle($title);
     return $l;
   }
-
 }
 
 

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARQueryTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARQueryTest.class.php	2008-01-17 15:36:47 UTC (rev 6693)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARQueryTest.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -275,6 +275,241 @@
     $this->assertEqual($groups[1]->getId(), $group1->getId());
     $this->assertEqual($groups[1]->getTitle(), 'AAA');
   }
+
+  function testFetch_NestedWithProperty_In_Attach_ForHasMany()
+  {
+    $course1 = $this->creator->createCourse();
+    $course2 = $this->creator->createCourse();
+    
+    $alt_course1 = $this->creator->createCourse();
+    $alt_course2 = $this->creator->createCourse();
+    
+    $lecture1 = $this->creator->createLecture($course1, $alt_course2);
+    $lecture2 = $this->creator->createLecture($course2, $alt_course1);
+    $lecture3 = $this->creator->createLecture($course1, $alt_course2);
+    $lecture4 = $this->creator->createLecture($course1, $alt_course1);
+    
+    $query = new lmbARQuery('CourseForTest', $this->conn);
+    $query->where(lmbSQLCriteria :: in('id', array($course1->getId(), $course2->getId())));
+    $arr = $query->attach('lectures', array('with' => 'alt_course'))->fetch()->getArray();
+    
+    //make sure we really eager fetching
+    $this->db->delete('lecture_for_test');
+    $this->db->delete('course_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(), $lecture1->getId());
+    $this->assertEqual($lectures[0]->getAltCourse()->getTitle(), $alt_course2->getTitle());
+    $this->assertEqual($lectures[1]->getId(), $lecture3->getId());
+    $this->assertEqual($lectures[1]->getAltCourse()->getTitle(), $alt_course2->getTitle());
+    $this->assertEqual($lectures[2]->getId(), $lecture4->getId());
+    $this->assertEqual($lectures[2]->getAltCourse()->getTitle(), $alt_course1->getTitle());
+    
+    
+    $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]->getAltCourse()->getTitle(), $alt_course1->getTitle());
+  }  
+  
+  function testFetch_NestedAttachProperty_In_With()
+  {
+    $course1 = $this->creator->createCourse();
+    $course2 = $this->creator->createCourse();
+    
+    $alt_course1 = $this->creator->createCourse();
+    $alt_course2 = $this->creator->createCourse();
+    
+    $lecture1 = $this->creator->createLecture($course1, $alt_course2);
+    $lecture2 = $this->creator->createLecture($course2, $alt_course1);
+    $lecture3 = $this->creator->createLecture($course1, $alt_course2);
+    $lecture4 = $this->creator->createLecture($course1, $alt_course1);
+
+    $lecture5 = $this->creator->createLecture($alt_course2);
+    $lecture6 = $this->creator->createLecture($alt_course1);
+    $lecture7 = $this->creator->createLecture($alt_course2);
+    $lecture8 = $this->creator->createLecture($alt_course1);
+    
+    $query = new lmbARQuery('LectureForTest', $this->conn);
+    $query->where(lmbSQLCriteria :: equal('course_id', $course1->getId()));
+    $iterator = $query->with('alt_course', array('attach' => 'lectures'))->fetch();
+    $arr = $iterator->getArray();
+    
+    //make sure we really eager fetching
+    $this->db->delete('lecture_for_test');
+    $this->db->delete('course_for_test');
+    
+    $this->assertIsA($arr[0], 'LectureForTest');
+    $this->assertEqual($arr[0]->getTitle(), $lecture1->getTitle());
+    
+    $this->assertEqual($arr[0]->getAltCourse()->getTitle(), $alt_course2->getTitle());
+    $alt_course_lectures = $arr[0]->getAltCourse()->getLectures();
+    $this->assertEqual($alt_course_lectures[0]->getId(), $lecture5->getId());
+    $this->assertEqual($alt_course_lectures[1]->getId(), $lecture7->getId());
+    
+    $this->assertEqual($arr[1]->getId(), $lecture3->getId());
+    $this->assertEqual($arr[1]->getAltCourse()->getTitle(), $alt_course2->getTitle());
+    $alt_course_lectures = $arr[1]->getAltCourse()->getLectures();
+    $this->assertEqual($alt_course_lectures[0]->getId(), $lecture5->getId());
+    $this->assertEqual($alt_course_lectures[1]->getId(), $lecture7->getId());
+    
+    $this->assertEqual($arr[2]->getId(), $lecture4->getId());
+    $this->assertEqual($arr[2]->getAltCourse()->getTitle(), $alt_course1->getTitle());
+    $alt_course_lectures = $arr[2]->getAltCourse()->getLectures();
+    $this->assertEqual($alt_course_lectures[0]->getId(), $lecture6->getId());
+    $this->assertEqual($alt_course_lectures[1]->getId(), $lecture8->getId());
+  }  
+
+  function testFetchNested_AttachProperty_In_WithProperty_In_Attach()
+  {
+    $course1 = $this->creator->createCourse();
+    $course2 = $this->creator->createCourse();
+    
+    $alt_course1 = $this->creator->createCourse();
+    $alt_course2 = $this->creator->createCourse();
+    
+    $lecture1 = $this->creator->createLecture($course1, $alt_course2);
+    $lecture2 = $this->creator->createLecture($course2, $alt_course1);
+    $lecture3 = $this->creator->createLecture($course1, $alt_course2);
+    $lecture4 = $this->creator->createLecture($course1, $alt_course1);
+    
+    $lecture5 = $this->creator->createLecture($alt_course2);
+    $lecture6 = $this->creator->createLecture($alt_course1);
+    $lecture7 = $this->creator->createLecture($alt_course2);
+    $lecture8 = $this->creator->createLecture($alt_course1);
+    
+    $query = new lmbARQuery('CourseForTest', $this->conn);
+    $query->where(lmbSQLCriteria :: in('id', array($course1->getId(), $course2->getId())));
+    $arr = $query->attach('lectures', array('with' => array('alt_course' => array('attach' => 'lectures'))))->fetch()->getArray();
+    
+    //make sure we really eager fetching
+    $this->db->delete('lecture_for_test');
+    $this->db->delete('course_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(), $lecture1->getId());
+    $this->assertEqual($lectures[0]->getAltCourse()->getTitle(), $alt_course2->getTitle());
+    $alt_course_lectures = $lectures[0]->getAltCourse()->getLectures();
+    $this->assertEqual($alt_course_lectures[0]->getId(), $lecture5->getId());
+    $this->assertEqual($alt_course_lectures[1]->getId(), $lecture7->getId());
+    
+    $this->assertEqual($lectures[1]->getId(), $lecture3->getId());
+    $this->assertEqual($lectures[1]->getAltCourse()->getTitle(), $alt_course2->getTitle());
+    $alt_course_lectures = $lectures[1]->getAltCourse()->getLectures();
+    $this->assertEqual($alt_course_lectures[0]->getId(), $lecture5->getId());
+    $this->assertEqual($alt_course_lectures[1]->getId(), $lecture7->getId());
+    
+    $this->assertEqual($lectures[2]->getId(), $lecture4->getId());
+    $this->assertEqual($lectures[2]->getAltCourse()->getTitle(), $alt_course1->getTitle());
+    $alt_course_lectures = $lectures[2]->getAltCourse()->getLectures();
+    $this->assertEqual($alt_course_lectures[0]->getId(), $lecture6->getId());
+    $this->assertEqual($alt_course_lectures[1]->getId(), $lecture8->getId());
+
+    $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]->getAltCourse()->getTitle(), $alt_course1->getTitle());
+    
+    $alt_course_lectures = $lectures[0]->getAltCourse()->getLectures();
+    $this->assertEqual($alt_course_lectures[0]->getId(), $lecture6->getId());
+    $this->assertEqual($alt_course_lectures[1]->getId(), $lecture8->getId());
+  }  
+  
+  function testFetch_NestedWithProperty_In_With()
+  {
+    $program1 = $this->creator->createProgram();
+    $program2 = $this->creator->createProgram();
+    $course1 = $this->creator->createCourse($program1);
+    $course2 = $this->creator->createCourse($program2);
+    $lecture1 = $this->creator->createLecture($course1);
+    $lecture2 = $this->creator->createLecture($course2);
+    
+    $query = new lmbARQuery('LectureForTest', $this->conn);
+    $iterator = $query->with('course', array('with' => 'program'))->fetch();
+    $arr = $iterator->getArray();
+    
+    //make sure we really eager fetching
+    $this->db->delete('lecture_for_test');
+    $this->db->delete('course_for_test');
+    $this->db->delete('program_for_test');
+    
+    $this->assertIsA($arr[0], 'LectureForTest');
+    $this->assertEqual($arr[0]->getTitle(), $lecture1->getTitle());
+    $this->assertEqual($arr[1]->getTitle(), $lecture2->getTitle());
+    
+    $this->assertEqual($arr[0]->getCourse()->getTitle(), $course1->getTitle());
+    $this->assertEqual($arr[1]->getCourse()->getTitle(), $course2->getTitle());
+
+    $this->assertEqual($arr[0]->getCourse()->getProgram()->getTitle(), $program1->getTitle());
+    $this->assertEqual($arr[1]->getCourse()->getProgram()->getTitle(), $program2->getTitle());
+  }
+
+  function testFetch_NestedAttachProperty_In_Attach()
+  {
+    $program1 = $this->creator->createProgram();
+    $program2 = $this->creator->createProgram();
+    $course1 = $this->creator->createCourse($program1);
+    $course2 = $this->creator->createCourse($program2);
+    $course3 = $this->creator->createCourse($program1);
+    $course4 = $this->creator->createCourse($program2);
+    $lecture1 = $this->creator->createLecture($course1);
+    $lecture2 = $this->creator->createLecture($course2);
+    $lecture3 = $this->creator->createLecture($course3);
+    $lecture4 = $this->creator->createLecture($course4);
+    $lecture5 = $this->creator->createLecture($course1);
+    $lecture6 = $this->creator->createLecture($course2);
+    $lecture7 = $this->creator->createLecture($course3);
+    $lecture8 = $this->creator->createLecture($course4);
+    
+    $query = new lmbARQuery('ProgramForTest', $this->conn);
+    $iterator = $query->attach('courses', array('attach' => 'lectures'))->fetch();
+    
+    $arr = $iterator->getArray();
+    
+    //make sure we really eager fetching
+    $this->db->delete('lecture_for_test');
+    $this->db->delete('course_for_test');
+    $this->db->delete('program_for_test');
+    
+    $this->assertIsA($arr[0], 'ProgramForTest');
+    $this->assertEqual($arr[0]->getTitle(), $program1->getTitle());
+    
+    $courses = $arr[0]->getCourses();
+    
+    $this->assertEqual($courses[0]->getTitle(), $course1->getTitle());
+    $lectures = $courses[0]->getLectures();
+    $this->assertEqual($lectures[0]->getTitle(), $lecture1->getTitle());
+    $this->assertEqual($lectures[1]->getTitle(), $lecture5->getTitle());
+
+    $this->assertEqual($courses[1]->getTitle(), $course3->getTitle());
+    $lectures = $courses[1]->getLectures();
+    $this->assertEqual($lectures[0]->getTitle(), $lecture3->getTitle());
+    $this->assertEqual($lectures[1]->getTitle(), $lecture7->getTitle());
+
+    $this->assertEqual($arr[1]->getTitle(), $program2->getTitle());
+    
+    $courses = $arr[1]->getCourses();
+    
+    $this->assertEqual($courses[0]->getTitle(), $course2->getTitle());
+    $lectures = $courses[0]->getLectures();
+    $this->assertEqual($lectures[0]->getTitle(), $lecture2->getTitle());
+    $this->assertEqual($lectures[1]->getTitle(), $lecture6->getTitle());
+
+    $this->assertEqual($courses[1]->getTitle(), $course4->getTitle());
+    $lectures = $courses[1]->getLectures();
+    $this->assertEqual($lectures[0]->getTitle(), $lecture4->getTitle());
+    $this->assertEqual($lectures[1]->getTitle(), $lecture8->getTitle());
+  }    
 }
 
 

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-17 15:36:47 UTC (rev 6693)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARRecordSetDecoratorTest.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -61,30 +61,6 @@
     $this->assertEqual($iterator[1]->getCourse()->getTitle(), $course->getTitle());
   }
 
-  function testProcessPrefixedFieldsAsRelatedActiveRecords()
-  {
-    $course = $this->_createCourseWithTwoLectures();
-    $lecture = new LectureForTest();
-    $course_info = $lecture->getRelationInfo('course'); 
-
-    $db = new lmbSimpleDb(lmbToolkit :: instance()->getDefaultDbConnection());
-    $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, '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(), 'LectureForTest');

Added: 3.x/trunk/limb/active_record/tests/cases/lmbARRecordSetJoinDecoratorTest.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARRecordSetJoinDecoratorTest.class.php	                        (rev 0)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARRecordSetJoinDecoratorTest.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -0,0 +1,71 @@
+<?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('limb/active_record/src/lmbARRecordSetJoinDecorator.class.php');
+require_once(dirname(__FILE__) . '/lmbAROneToManyRelationsTest.class.php');
+
+class lmbARRecordSetJoinDecoratorTest extends UnitTestCase
+{
+  function setUp()
+  {
+    $this->_dbCleanUp();
+  }
+
+  function tearDown()
+  {
+    $this->_dbCleanUp();
+  }
+
+  function _dbCleanUp()
+  {
+    lmbActiveRecord :: delete('CourseForTest');
+    lmbActiveRecord :: delete('LectureForTest');
+  }
+
+  function testProcessPrefixedFieldsAsRelatedActiveRecords()
+  {
+    $course = $this->_createCourseWithTwoLectures();
+    $lecture = new LectureForTest();
+    $course_info = $lecture->getRelationInfo('course'); 
+
+    $db = new lmbSimpleDb(lmbToolkit :: instance()->getDefaultDbConnection());
+    $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 lmbARRecordSetJoinDecorator($decorated, new 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]->get('course')->getTitle(), $course->getTitle());
+    $this->assertEqual($arr[1]->get('course')->getTitle(), $course->getTitle());
+  }
+
+  function _createCourseWithTwoLectures()
+  {
+    $course = new CourseForTest();
+    $course->setTitle($title = 'General Course');
+
+    $l1 = new LectureForTest();
+    $l1->setTitle('Physics');
+    $l2 = new LectureForTest();
+    $l2->setTitle('Math');
+
+    $course->addToLectures($l1);
+    $course->addToLectures($l2);
+    $course->save();
+
+    return $course;
+  }
+}
+

Modified: 3.x/trunk/limb/active_record/tests/cases/lmbARTestingObjectMother.class.php
===================================================================
--- 3.x/trunk/limb/active_record/tests/cases/lmbARTestingObjectMother.class.php	2008-01-17 15:36:47 UTC (rev 6693)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbARTestingObjectMother.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -23,6 +23,14 @@
                                                    'class' => 'PersonForTest'));
 }
 
+class ProgramForTest extends lmbActiveRecord
+{
+  protected $_db_table_name = 'program_for_test';
+
+  protected $_has_many = array('courses' => array('field' => 'program_id',
+                                                  'class' => 'CourseForTest'));
+}
+
 class CourseForTest extends lmbActiveRecord
 {
   protected $_db_table_name = 'course_for_test';
@@ -31,6 +39,10 @@
                                'alt_lectures' => array('field' => 'alt_course_id',
                                                        'class' => 'LectureForTest'));
 
+  protected $_many_belongs_to = array('program' => array('field' => 'program_id',
+                                                         'class' => 'ProgramForTest',
+                                                         'can_be_null' => true));
+  
   public $save_calls = 0;
 
   function save()
@@ -141,13 +153,25 @@
     return $number; 
   }
   
-  function createCourse()
+  function createCourse($program = null)
   {
     $course = new CourseForTest();
     $course->setTitle('Course_'. rand(0, 100));
+    
+    if($program)
+      $course->setProgram($program);
+    
     $course->save();
     return $course;
   }
+  
+  function createProgram()
+  {
+    $program = new ProgramForTest();
+    $program->setTitle('Program_'. rand(0, 100));
+    $program->save();
+    return $program;
+  }
 
   function createLecture($course, $alt_course = null, $title = '')
   {

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-17 15:36:47 UTC (rev 6693)
+++ 3.x/trunk/limb/active_record/tests/cases/lmbActiveRecordTest.class.php	2008-01-17 15:41:51 UTC (rev 6694)
@@ -653,6 +653,41 @@
     $this->assertEqual($arr[2]->getCourse()->getTitle(), $course1->getTitle());
     $this->assertEqual($arr[2]->getAltCourse()->getTitle(), $alt_course2->getTitle());
   }
+  
+  function testFindAttachRelatedObjects_HasMany()
+  {
+    $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');
+    
+    $rs = lmbActiveRecord :: find('CourseForTest', array('attach' => array('lectures' => array('sort' => array('title' => 'ASC')))));
+    $arr = $rs->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 testFindBySql()
   {



More information about the limb-svn mailing list