* Implemented eager loading of has-one relations
authorChristopher Hlubek <hlubek@networkteam.com>
Wed, 25 Mar 2009 16:52:23 +0000 (16:52 +0000)
committerChristopher Hlubek <hlubek@networkteam.com>
Wed, 25 Mar 2009 16:52:23 +0000 (16:52 +0000)
* The generic finder can now use nested examples
* Refactored the generic finder
* Fixed some bugs in the DataMap
* Moved TYPO3_DB to a property in ObjectRelationalMapper to make it injectable for unit tests

typo3/sysext/extbase/Classes/Persistence/Mapper/DataMap.php
typo3/sysext/extbase/Classes/Persistence/Mapper/ObjectRelationalMapper.php
typo3/sysext/extbase/Tests/DataMap_testcase.php
typo3/sysext/extbase/Tests/ObjectRelationalMapper_testcase.php
typo3/sysext/extbase/Tests/Repository_testcase.php

index 1b38823..2ce32e1 100644 (file)
@@ -130,14 +130,24 @@ class Tx_ExtBase_Persistence_Mapper_DataMap {
        }
 
        protected function setRelations(Tx_ExtBase_Persistence_Mapper_ColumnMap &$columnMap, $columnConfiguration) {
-               if (array_key_exists('foreign_table', $columnConfiguration['config']) && !array_key_exists('MM', $columnConfiguration['config'])) {
-                       $columnMap->setTypeOfRelation(Tx_ExtBase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY);
-                       $columnMap->setChildClassName($columnConfiguration['config']['foreign_class']);
-                       $columnMap->setChildTableName($columnConfiguration['config']['foreign_table']);
-                       $columnMap->setChildTableWhere($columnConfiguration['config']['foreign_table_where']);
-                       $columnMap->setChildSortbyFieldName($columnConfiguration['config']['foreign_sortby']);
-                       $columnMap->setParentKeyFieldName($columnConfiguration['config']['foreign_field']);
-                       $columnMap->setParentTableFieldName($columnConfiguration['config']['foreign_table_field']);
+               if (isset($columnConfiguration['config']['foreign_table']) && !isset($columnConfiguration['config']['MM'])) {
+                       if ($columnConfiguration['config']['maxitems'] == 1) {
+                               $columnMap->setTypeOfRelation(Tx_ExtBase_Persistence_Mapper_ColumnMap::RELATION_HAS_ONE);
+                               $columnMap->setChildClassName($columnConfiguration['config']['foreign_class']);
+                               $columnMap->setChildTableName($columnConfiguration['config']['foreign_table']);
+                               $columnMap->setChildTableWhere($columnConfiguration['config']['foreign_table_where']);
+                               $columnMap->setChildSortbyFieldName($columnConfiguration['config']['foreign_sortby']);
+                               $columnMap->setParentKeyFieldName($columnConfiguration['config']['foreign_field']);
+                               $columnMap->setParentTableFieldName($columnConfiguration['config']['foreign_table_field']);
+                       } else {
+                               $columnMap->setTypeOfRelation(Tx_ExtBase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY);
+                               $columnMap->setChildClassName($columnConfiguration['config']['foreign_class']);
+                               $columnMap->setChildTableName($columnConfiguration['config']['foreign_table']);
+                               $columnMap->setChildTableWhere($columnConfiguration['config']['foreign_table_where']);
+                               $columnMap->setChildSortbyFieldName($columnConfiguration['config']['foreign_sortby']);
+                               $columnMap->setParentKeyFieldName($columnConfiguration['config']['foreign_field']);
+                               $columnMap->setParentTableFieldName($columnConfiguration['config']['foreign_table_field']);
+                       }
                } elseif (array_key_exists('MM', $columnConfiguration['config'])) {
                        $columnMap->setTypeOfRelation(Tx_ExtBase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY);
                        $columnMap->setChildClassName($columnConfiguration['config']['foreign_class']);
@@ -206,7 +216,7 @@ class Tx_ExtBase_Persistence_Mapper_DataMap {
         * Returns the name of a column indicating the 'hidden' state of the row
         *
         */
-       public function getHiddenColumnName() {;
+       public function getHiddenColumnName() {
                return $GLOBALS['TCA'][$this->getTableName()]['ctrl']['enablecolumns']['disabled'];
        }
 
@@ -242,9 +252,9 @@ class Tx_ExtBase_Persistence_Mapper_DataMap {
         * @return mixed The converted value
         */
        public function convertPropertyValueToFieldValue($propertyValue) {
-               if (is_bool($value)) {
+               if (is_bool($propertyValue)) {
                        $convertedValue = $propertyValue ? 1 : 0;
-               } elseif ($value instanceof Tx_ExtBase_DomainObject_AbstractDomainObject) {
+               } elseif ($propertyValue instanceof Tx_ExtBase_DomainObject_AbstractDomainObject) {
                        $convertedValue = $propertyValue->getUid();
                } elseif ($propertyValue instanceof DateTime) {
                        $convertedValue = $propertyValue->format('U');
index 6fc5c27..610e129 100644 (file)
@@ -47,6 +47,13 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
         * @var array
         **/
        protected $dataMaps = array();
+       
+       /**
+        * The TYPO3 DB object
+        *
+        * @var t3lib_db
+        **/
+       protected $db;
 
        /**
         * Constructs a new mapper
@@ -55,6 +62,7 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
        public function __construct() {
                $this->persistenceSession = t3lib_div::makeInstance('Tx_ExtBase_Persistence_Session');
                $GLOBALS['TSFE']->includeTCA();
+               $this->db = $GLOBALS['TYPO3_DB'];
        }
        
        /**
@@ -86,34 +94,87 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
        public function find($className, $conditions = '', $groupBy = '', $orderBy = '', $limit = '', $useEnableFields = TRUE) {
                $dataMap = $this->getDataMap($className);
                if (is_array($conditions)) {
-                       $whereParts = array();
-                       foreach ($conditions as $key => $condition) {
-                               if (is_array($condition) && isset($condition[0])) {
-                                       $sql = $condition[0];
-                                       for ($i = 1; $i < count($condition); $i++) {
-                                               $markPos = strpos($sql, '?');
-                                               if ($markPos !== FALSE) {
-                                                       $sql = substr($sql, 0, $markPos) . $dataMap->convertValueToQueryParameter($condition[$i]) . substr($sql, $markPos + 1);
-                                               }
-                                       }
-                                       $whereParts[] = '(' . $sql . ')';
-                               } elseif (is_string($key)) {
-                                       if (!is_array($condition)) {
-                                               $column = $this->getDataMap($className)->getColumnMap($key)->getColumnName();
-                                               $sql = $column . ' = ' . $dataMap->convertPropertyValueToFieldValue($condition);
-                                       }
-                                       $whereParts[] = '(' . $sql . ')';
-                               }
-                       }
-                       $where = implode(' AND ', $whereParts);
+                       $where = $this->queryByConditions($dataMap, $conditions);
                } elseif (is_string($conditions)) {
                        $where = $conditions;
                }
                return $this->fetch($className, $where, $groupBy, $orderBy, $limit, $useEnableFields);
        }
+       
+       /**
+        * Get a where part for conditions by a specific data map. This will
+        * either replace placeholders (index based array) or use the condition
+        * as an example relative to the data map.
+        *
+        * @param Tx_ExtBase_Persistence_Mapper_DataMap $dataMap The data map
+        * @param array $conditions The conditions
+        * 
+        * @return string The where part
+        */
+       protected function queryByConditions(&$dataMap, $conditions) {
+               $whereParts = array();
+               foreach ($conditions as $key => $condition) {
+                       if (is_array($condition) && isset($condition[0])) {
+                               $sql = $this->replacePlaceholders($dataMap, $condition[0], array_slice($condition, 1));
+                               $whereParts[] = '(' . $sql . ')';
+                       } elseif (is_string($key)) {
+                               $sql = $this->queryByExample($dataMap, $key, $condition);
+                               if (strlen($sql) > 0) {
+                                       $whereParts[] = '(' . $sql . ')';
+                               }
+                       }
+               }
+               return implode(' AND ', $whereParts);           
+       }
 
        /**
-        * Fetches rows from the database by given SQL statement snippets
+        * Get a where part for an example condition (associative array). This also works
+        * for nested conditions.
+        *
+        * @param Tx_ExtBase_Persistence_Mapper_DataMap $dataMap The data map
+        * @param array $propertyName The property name
+        * @param array $example The example condition
+        * 
+        * @return string The where part
+        */
+       protected function queryByExample(&$dataMap, $propertyName, $example) {
+               $sql = '';
+               if (!is_array($example)) {
+                       $column = $dataMap->getTableName() . '.' . $dataMap->getColumnMap($propertyName)->getColumnName();
+                       $sql = $column . ' = ' . $dataMap->convertPropertyValueToFieldValue($example);
+               } else {
+                       $columnMap = $dataMap->getColumnMap($propertyName);
+                       $childDataMap = $this->getDataMap($columnMap->getChildClassName());
+                       $sql = $this->queryByConditions($childDataMap, $example);
+               }
+               return $sql;
+       }
+       
+       /**
+        * Replace query placeholders in a query part by the given
+        * parameters.
+        *
+        * @param Tx_ExtBase_Persistence_Mapper_DataMap $dataMap The data map for conversion
+        * @param string $queryPart The query part with placeholders
+        * @param array $parameters The parameters
+        *
+        * @return string The query part with replaced placeholders
+        */
+       protected function replacePlaceholders(&$dataMap, $queryPart, $parameters) {
+               $sql = $queryPart;
+               foreach ($parameters as $parameter) {
+                       $markPos = strpos($sql, '?');
+                       if ($markPos !== FALSE) {
+                               $sql = substr($sql, 0, $markPos) . $dataMap->convertPropertyValueToFieldValue($parameter) . substr($sql, $markPos + 1);
+                       }
+               }
+               return $sql;
+       }
+
+       /**
+        * Fetches objects from the database by given SQL statement snippets. The where
+        * statement is raw SQL and will not be escaped. It is much safer to use the
+        * generic find method to supply where conditions.
         *
         * @param string $className the className
         * @param string $where WHERE statement
@@ -129,24 +190,58 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                } else {
                        $enableFields = '';
                }
-               $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
-                       '*', // TODO limit fetched fields
-                       $dataMap->getTableName(),
+               
+               $joinTables = $this->getJoinClause($className);         
+               $res = $this->db->exec_SELECTquery(
+                       '*', // TODO limit fetched fields (CH: should we do that?)
+                       $dataMap->getTableName() . ' ' . $joinTables,
                        $where . $enableFields,
                        $groupBy,
                        $orderBy,
                        $limit
                        );
+               
+               $fieldMap = array();
+               $i = 0;
+               // FIXME mysql_fetch_field should be available in t3lib_db (patch core)
+               while ($field = mysql_fetch_field($res)) {
+                       $fieldMap[$field->table][$field->name] = $i;
+                       $i++;
+               }
+
+               $rows = array();
+               while($rows[] = $this->db->sql_fetch_row($res));
+               array_pop($rows);
+               
                // SK: Do we want to make it possible to ignore "enableFields"?
                // TODO language overlay; workspace overlay
                $objects = array();
                if (is_array($rows)) {
                        if (count($rows) > 0) {
-                               $objects = $this->reconstituteObjects($dataMap, $rows);
+                               $objects = $this->reconstituteObjects($dataMap, $fieldMap, $rows);
                        }
                }
                return $objects;
        }
+       
+       /**
+        * Get the join clause for the fetch method for a specific class. This will
+        * eagerly load all has-one relations.
+        *
+        * @param string $className The class name
+        * @return string The join clause
+        */
+       protected function getJoinClause($className) {
+               $dataMap = $this->getDataMap($className);
+               $join = '';
+               foreach ($dataMap->getColumnMaps() as $propertyName => $columnMap) {
+                       if ($columnMap->getTypeOfRelation() == Tx_ExtBase_Persistence_Mapper_ColumnMap::RELATION_HAS_ONE) {
+                               $join .= ' LEFT JOIN ' . $columnMap->getChildTableName() . ' ON ' . $dataMap->getTableName() . '.' . $columnMap->getColumnName() . ' = ' . $columnMap->getChildTableName() . '.uid';
+                               $join .= $this->getJoinClause($columnMap->getChildClassName());
+                       }
+               }
+               return $join;
+       }
 
        /**
         * Fetches a rows from the database by given SQL statement snippets taking a relation table into account
@@ -163,7 +258,7 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                } else {
                        $enableFields = '';
                }
-               $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
+               $rows = $this->db->exec_SELECTgetRows(
                        $columnMap->getChildTableName() . '.*, ' . $columnMap->getRelationTableName() . '.*',
                        $columnMap->getChildTableName() . ' LEFT JOIN ' . $columnMap->getRelationTableName() . ' ON (' . $columnMap->getChildTableName() . '.uid=' . $columnMap->getRelationTableName() . '.uid_foreign)',
                        $where . ' AND ' . $columnMap->getRelationTableName() . '.uid_local=' . t3lib_div::intval_positive($parentObject->getUid()) . $enableFields,
@@ -179,21 +274,26 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
         * reconstitutes domain objects from $rows (array)
         *
         * @param Tx_ExtBase_Persistence_Mapper_DataMap $dataMap The data map corresponding to the domain object
-        * @param array $rows The rows array fetched from the database
+        * @param array $fieldMap An array indexed by the table name and field name to the row index
+        * @param array $rows The rows array fetched from the database (not associative)
         * @return array An array of reconstituted domain objects
         */
        // SK: I Need to check this method more thoroughly.
        // SK: Are loops detected during reconstitution?
-       protected function reconstituteObjects($dataMap, array $rows) {
+       protected function reconstituteObjects($dataMap, &$fieldMap, array $rows) {
                $objects = array();             
                foreach ($rows as $row) {
                        $properties = array();
                        foreach ($dataMap->getColumnMaps() as $columnMap) {
-                               $properties[$columnMap->getPropertyName()] = $dataMap->convertFieldValueToPropertyValue($columnMap->getPropertyName(), $row[$columnMap->getColumnName()]);
+                               $fieldValue = $row[$fieldMap[$dataMap->getTableName()][$columnMap->getColumnName()]];
+                               $properties[$columnMap->getPropertyName()] = $dataMap->convertFieldValueToPropertyValue($columnMap->getPropertyName(), $fieldValue);
                        }
                        $object = $this->reconstituteObject($dataMap->getClassName(), $properties);
                        foreach ($dataMap->getColumnMaps() as $columnMap) {
-                               if ($columnMap->getTypeOfRelation() === Tx_ExtBase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
+                               if ($columnMap->getTypeOfRelation() === Tx_ExtBase_Persistence_Mapper_ColumnMap::RELATION_HAS_ONE) {
+                                       list($relatedObject) = $this->reconstituteObjects($this->getDataMap($columnMap->getChildClassName()), $fieldMap, array($row));
+                                       $object->_reconstituteProperty($columnMap->getPropertyName(), $relatedObject);
+                               } elseif ($columnMap->getTypeOfRelation() === Tx_ExtBase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
                                        $where = $columnMap->getParentKeyFieldName() . '=' . intval($object->getUid());
                                        $relatedDataMap = $this->getDataMap($columnMap->getChildClassName());
                                        $relatedObjects = $this->fetch($columnMap->getChildClassName(), $where);
@@ -293,11 +393,11 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                $row['tstamp'] = time();
 
                $tableName = $dataMap->getTableName();
-               $res = $GLOBALS['TYPO3_DB']->exec_INSERTquery(
+               $res = $this->db->exec_INSERTquery(
                        $tableName,
                        $row
                        );
-               $object->_reconstituteProperty('uid', $GLOBALS['TYPO3_DB']->sql_insert_id());
+               $object->_reconstituteProperty('uid', $this->db->sql_insert_id());
 
                $this->persistRelations($object, $propertyName, $this->getRelations($dataMap, $properties));
        }
@@ -332,7 +432,7 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                }
 
                $tableName = $dataMap->getTableName();
-               $res = $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
+               $res = $this->db->exec_UPDATEquery(
                        $tableName,
                        'uid=' . $object->getUid(),
                        $row
@@ -356,13 +456,13 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                $tableName = $dataMap->getTableName();
                if ($onlySetDeleted === TRUE && !empty($deletedColumnName)) {
                        $deletedColumnName = $dataMap->getDeletedColumnName();
-                       $res = $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
+                       $res = $this->db->exec_UPDATEquery(
                                $tableName,
                                'uid=' . $object->getUid(),
                                array($deletedColumnName => 1)
                                );
                } else {
-                       $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
+                       $res = $this->db->exec_DELETEquery(
                                $tableName,
                                'uid=' . $object->getUid()
                                );
@@ -485,7 +585,7 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                        'sorting' => 9999 // TODO sorting of mm table items
                        );
                $tableName = $dataMap->getColumnMap($parentPropertyName)->getRelationTableName();
-               $res = $GLOBALS['TYPO3_DB']->exec_INSERTquery(
+               $res = $this->db->exec_INSERTquery(
                        $tableName,
                        $rowToInsert
                        );
@@ -502,7 +602,7 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
        protected function deleteRelationInRelationTable($relatedObject, Tx_ExtBase_DomainObject_AbstractDomainObject $parentObject, $parentPropertyName) {
                $dataMap = $this->getDataMap(get_class($parentObject));
                $tableName = $dataMap->getColumnMap($parentPropertyName)->getRelationTableName();
-               $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+               $res = $this->db->exec_SELECTquery(
                        'uid_foreign',
                        $tableName,
                        'uid_local=' . $parentObject->getUid()
@@ -522,7 +622,7 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                }
                if (count($relationsToDelete) > 0) {
                        $relationsToDeleteList = implode(',', $relationsToDelete);
-                       $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
+                       $res = $this->db->exec_DELETEquery(
                                $tableName,
                                'uid_local=' . $parentObject->getUid() . ' AND uid_foreign IN (' . $relationsToDeleteList . ')'
                                );
index 3872aaf..092096f 100644 (file)
@@ -195,6 +195,6 @@ class Tx_ExtBase_Persistence_Mapper_DataMap_testcase extends Tx_ExtBase_Base_tes
                $dataMap = new Tx_ExtBase_Persistence_Mapper_DataMap('Tx_BlogExample_Domain_Blog');
                $dataMap->initialize();
                $this->assertEquals(Tx_ExtBase_Persistence_Mapper_ColumnMap::RELATION_HAS_ONE, $dataMap->getColumnMap('author')->getTypeOfRelation(), 'The author relation was not of type HAS_ONE.');
-       }       
+       }
 }
 ?>
\ No newline at end of file
index 574eb11..3e4b5ba 100644 (file)
@@ -32,8 +32,13 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper_testcase extends Tx_E
                        ->method('includeTCA')
                        ->will($this->returnValue(NULL));
                
+               $this->typo3Db = $GLOBALS['TYPO3_DB'];
                $GLOBALS['TYPO3_DB'] = $this->getMock('tslib_DB', array('fullQuoteStr'));
        }
+       
+       public function tearDown() {
+               $GLOBALS['TYPO3_DB'] = $this->typo3Db;
+       }
 
        public function test_FindByConditionWithPlaceholders() {
                $mapper = $this->getMock('Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper', array('fetch'));
@@ -50,11 +55,6 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper_testcase extends Tx_E
                        ->method('fullQuoteStr')
                        ->with($this->equalTo('bar'))
                        ->will($this->returnValue('"bar"'));
-
-               $GLOBALS['TYPO3_DB']->expects($this->at(2))
-                       ->method('fullQuoteStr')
-                       ->with($this->equalTo('0'))
-                       ->will($this->returnValue('0'));
                
                $mapper->find('Tx_BlogExample_Domain_Blog',
                        array(
@@ -67,7 +67,7 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper_testcase extends Tx_E
                $mapper = $this->getMock('Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper', array('fetch', 'getDataMap'));
                $mapper->expects($this->once())
                        ->method('fetch')
-                       ->with($this->equalTo('Tx_BlogExample_Domain_Blog'), $this->equalTo('(blog_name = "foo") AND (hidden = 0)'));
+                       ->with($this->equalTo('Tx_BlogExample_Domain_Blog'), $this->equalTo('(tx_blogexample_domain_blog.blog_name = "foo") AND (tx_blogexample_domain_blog.hidden = 0)'));
 
                $columnMap1 = $this->getMock('Tx_ExtBase_Persistence_Mapper_ColumnMap', array('getColumnName'), array(), '', FALSE);
                $columnMap1->expects($this->once())
@@ -79,13 +79,22 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper_testcase extends Tx_E
                        ->method('getColumnName')
                        ->will($this->returnValue('hidden'));
 
-               $dataMap = $this->getMock('Tx_ExtBase_Persistence_Mapper_DataMap', array('getColumnMap'), array(), '', FALSE);
+               $dataMap = $this->getMock('Tx_ExtBase_Persistence_Mapper_DataMap', array('getColumnMap', 'getTableName'), array(), '', FALSE);
+
                $dataMap->expects($this->at(0))
+                       ->method('getTableName')
+                       ->will($this->returnValue('tx_blogexample_domain_blog'));
+
+               $dataMap->expects($this->at(1))
                        ->method('getColumnMap')
                        ->with($this->equalTo('blogName'))
                        ->will($this->returnValue($columnMap1));
 
-               $dataMap->expects($this->at(1))
+               $dataMap->expects($this->at(2))
+                       ->method('getTableName')
+                       ->will($this->returnValue('tx_blogexample_domain_blog'));
+
+               $dataMap->expects($this->at(3))
                        ->method('getColumnMap')
                        ->with($this->equalTo('hidden'))
                        ->will($this->returnValue($columnMap2));
@@ -99,11 +108,6 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper_testcase extends Tx_E
                        ->method('fullQuoteStr')
                        ->with($this->equalTo('foo'))
                        ->will($this->returnValue('"foo"'));
-
-               $GLOBALS['TYPO3_DB']->expects($this->at(1))
-                       ->method('fullQuoteStr')
-                       ->with($this->equalTo('0'))
-                       ->will($this->returnValue('0'));
                
                $mapper->find('Tx_BlogExample_Domain_Blog',
                        array(
@@ -111,5 +115,73 @@ class Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper_testcase extends Tx_E
                                'hidden' => FALSE
                        ));
        }
+       
+       public function test_FindByConditionWithNestedExample() {
+               $mapper = $this->getMock('Tx_ExtBase_Persistence_Mapper_ObjectRelationalMapper', array('fetch', 'getDataMap'));
+               $mapper->expects($this->once())
+                       ->method('fetch')
+                       ->with($this->equalTo('Tx_BlogExample_Domain_Blog'), $this->equalTo('(tx_blogexample_domain_blog.hidden = 0) AND ((tx_blogexample_domain_author.name = "Christopher"))'));
+
+               $columnMap1 = $this->getMock('Tx_ExtBase_Persistence_Mapper_ColumnMap', array('getColumnName'), array(), '', FALSE);
+               $columnMap1->expects($this->once())
+                       ->method('getColumnName')
+                       ->will($this->returnValue('hidden'));
+
+               $columnMap3 = $this->getMock('Tx_ExtBase_Persistence_Mapper_ColumnMap', array('getChildClassName'), array(), '', FALSE);
+               $columnMap3->expects($this->once())
+                       ->method('getChildClassName')
+                       ->will($this->returnValue('Tx_BlogExample_Domain_Author'));
+
+               $dataMap1 = $this->getMock('Tx_ExtBase_Persistence_Mapper_DataMap', array('getColumnMap', 'getTableName'), array(), '', FALSE);
+               $dataMap1->expects($this->at(0))
+                       ->method('getTableName')
+                       ->will($this->returnValue('tx_blogexample_domain_blog'));
+               $dataMap1->expects($this->at(1))
+                       ->method('getColumnMap')
+                       ->with($this->equalTo('hidden'))
+                       ->will($this->returnValue($columnMap1));
+               $dataMap1->expects($this->at(2))
+                       ->method('getColumnMap')
+                       ->with($this->equalTo('author'))
+                       ->will($this->returnValue($columnMap3));
+
+               $columnMap2 = $this->getMock('Tx_ExtBase_Persistence_Mapper_ColumnMap', array('getColumnName'), array(), '', FALSE);
+               $columnMap2->expects($this->once())
+                       ->method('getColumnName')
+                       ->will($this->returnValue('name'));
+
+               $dataMap2 = $this->getMock('Tx_ExtBase_Persistence_Mapper_DataMap', array('getColumnMap', 'getTableName'), array(), '', FALSE);
+               $dataMap2->expects($this->at(0))
+                       ->method('getTableName')
+                       ->will($this->returnValue('tx_blogexample_domain_author'));
+               $dataMap2->expects($this->at(1))
+                       ->method('getColumnMap')
+                       ->with($this->equalTo('name'))
+                       ->will($this->returnValue($columnMap2));
+               
+               $mapper->expects($this->at(0))
+                       ->method('getDataMap')
+                       ->with($this->equalTo('Tx_BlogExample_Domain_Blog'))
+                       ->will($this->returnValue($dataMap1));
+               
+               $mapper->expects($this->at(1))
+                       ->method('getDataMap')
+                       ->with($this->equalTo('Tx_BlogExample_Domain_Author'))
+                       ->will($this->returnValue($dataMap2));
+               
+               $GLOBALS['TYPO3_DB']->expects($this->any())
+                       ->method('fullQuoteStr')
+                       ->with($this->equalTo('Christopher'))
+                       ->will($this->returnValue('"Christopher"'));
+               
+               $mapper->find('Tx_BlogExample_Domain_Blog',
+                       array(
+                               'hidden' => FALSE,
+                               'author' => array(
+                                       'name' => 'Christopher'
+                               )
+                       ));
+       }
+       
 }
 ?>
\ No newline at end of file
index 0ccf39b..5e4ddb7 100644 (file)
@@ -50,5 +50,15 @@ class Tx_ExtBase_Persistence_Repository_testcase extends Tx_ExtBase_Base_testcas
                
                $repository->findByName('foo');
        }
+
+       public function test_MagicFindOneByPropertyUsesGenericFind() {
+               $repository = $this->getMock('TX_Blogexample_Domain_BlogRepository', array('find'), array('TX_Blogexample_Domain_Blog'));
+               $repository->expects($this->once())
+                       ->method('find')
+                       ->with($this->equalTo(array('name' => 'foo')), $this->equalTo(''), $this->equalTo(''), $this->equalTo(1))
+                       ->will($this->returnValue(array()));
+               
+               $repository->findOneByName('foo');
+       }
 }
 ?>
\ No newline at end of file