diff --git a/model/DataQuery.php b/model/DataQuery.php index 4e3a4065d..e82bcf1ee 100644 --- a/model/DataQuery.php +++ b/model/DataQuery.php @@ -698,9 +698,11 @@ class DataQuery { $componentBaseClass = ClassInfo::baseDataClass($componentClass); $this->query->addInnerJoin($relationTable, "\"$relationTable\".\"$parentField\" = \"$parentBaseClass\".\"ID\""); + if (!$this->query->isJoinedTo($componentBaseClass)) { $this->query->addLeftJoin($componentBaseClass, "\"$relationTable\".\"$componentField\" = \"$componentBaseClass\".\"ID\""); - if(ClassInfo::hasTable($componentClass)) { + } + if(ClassInfo::hasTable($componentClass) && !$this->query->isJoinedTo($componentClass)) { $this->query->addLeftJoin($componentClass, "\"$relationTable\".\"$componentField\" = \"$componentClass\".\"ID\""); } diff --git a/tests/model/DataListTest.php b/tests/model/DataListTest.php index 9f84c6a2d..828805502 100755 --- a/tests/model/DataListTest.php +++ b/tests/model/DataListTest.php @@ -25,6 +25,8 @@ class DataListTest extends SapphireTest { 'DataObjectTest\NamespacedClass', 'DataObjectTest_Company', 'DataObjectTest_Fan', + 'ManyManyListTest_Product', + 'ManyManyListTest_Category', ); public function testFilterDataObjectByCreatedDate() { diff --git a/tests/model/DataObjectLazyLoadingTest.php b/tests/model/DataObjectLazyLoadingTest.php index 001e24b20..a49753273 100644 --- a/tests/model/DataObjectLazyLoadingTest.php +++ b/tests/model/DataObjectLazyLoadingTest.php @@ -35,6 +35,9 @@ class DataObjectLazyLoadingTest extends SapphireTest { 'DataObjectTest_Play', 'DataObjectTest_Ploy', 'DataObjectTest_Bogey', + // From ManyManyListTest + 'ManyManyListTest_Product', + 'ManyManyListTest_Category', // From VersionedTest 'VersionedTest_DataObject', 'VersionedTest_Subclass', diff --git a/tests/model/DataObjectTest.php b/tests/model/DataObjectTest.php index 044b94c86..08a4bcd29 100644 --- a/tests/model/DataObjectTest.php +++ b/tests/model/DataObjectTest.php @@ -29,6 +29,8 @@ class DataObjectTest extends SapphireTest { 'DataObjectTest_Play', 'DataObjectTest_Ploy', 'DataObjectTest_Bogey', + 'ManyManyListTest_Product', + 'ManyManyListTest_Category', ); public function testDb() { @@ -1107,7 +1109,7 @@ class DataObjectTest extends SapphireTest { public function testValidateModelDefinitionsFailsWithArray() { Config::nest(); - + $object = new DataObjectTest_Team; $method = $this->makeAccessible($object, 'validateModelDefinitions'); @@ -1124,7 +1126,7 @@ class DataObjectTest extends SapphireTest { public function testValidateModelDefinitionsFailsWithIntKey() { Config::nest(); - + $object = new DataObjectTest_Team; $method = $this->makeAccessible($object, 'validateModelDefinitions'); @@ -1141,7 +1143,7 @@ class DataObjectTest extends SapphireTest { public function testValidateModelDefinitionsFailsWithIntValue() { Config::nest(); - + $object = new DataObjectTest_Team; $method = $this->makeAccessible($object, 'validateModelDefinitions'); @@ -1161,7 +1163,7 @@ class DataObjectTest extends SapphireTest { */ public function testValidateModelDefinitionsPassesWithExtraFields() { Config::nest(); - + $object = new DataObjectTest_Team; $method = $this->makeAccessible($object, 'validateModelDefinitions'); @@ -1808,7 +1810,7 @@ class DataObjectTest_SubTeam extends DataObjectTest_Team implements TestOnly { private static $many_many = array( 'FormerPlayers' => 'DataObjectTest_Player' ); - + private static $many_many_extraFields = array( 'FormerPlayers' => array( 'Position' => 'Varchar(100)' diff --git a/tests/model/DataObjectTest.yml b/tests/model/DataObjectTest.yml index f5065efef..19065154e 100644 --- a/tests/model/DataObjectTest.yml +++ b/tests/model/DataObjectTest.yml @@ -59,6 +59,28 @@ DataObjectTest_TeamComment: Name: Phil Comment: Phil is a unique guy, and comments on team2 Team: =>DataObjectTest_Team.team2 +ManyManyListTest_Product: + producta: + Title: 'Product A' + productb: + Title: 'Product B' + RelatedProducts: =>ManyManyListTest_Product.producta +ManyManyListTest_Category: + categorya: + Title: 'Category A' + Products: =>ManyManyListTest_Product.producta,=>ManyManyListTest_Product.productb + comment1: + Name: Joe + Comment: This is a team comment by Joe + Team: =>DataObjectTest_Team.team1 + comment2: + Name: Bob + Comment: This is a team comment by Bob + Team: =>DataObjectTest_Team.team1 + comment3: + Name: Phil + Comment: Phil is a unique guy, and comments on team2 + Team: =>DataObjectTest_Team.team2 DataObjectTest_Fan: fan1: Name: Damian diff --git a/tests/model/DataQueryTest.php b/tests/model/DataQueryTest.php index c3d1d4f35..d68b5142c 100644 --- a/tests/model/DataQueryTest.php +++ b/tests/model/DataQueryTest.php @@ -11,6 +11,7 @@ class DataQueryTest extends SapphireTest { 'DataQueryTest_D', 'DataQueryTest_E', 'DataQueryTest_F', + 'DataQueryTest_G', ); @@ -63,11 +64,39 @@ class DataQueryTest extends SapphireTest { } public function testApplyReplationDeepInheretence() { + //test has_one relation $newDQ = new DataQuery('DataQueryTest_E'); //apply a relation to a relation from an ancestor class $newDQ->applyRelation('TestA'); $this->assertTrue($newDQ->query()->isJoinedTo('DataQueryTest_C')); $this->assertContains('"DataQueryTest_A"."ID" = "DataQueryTest_C"."TestAID"', $newDQ->sql($params)); + + //test many_many relation + + //test many_many with separate inheritance + $newDQ = new DataQuery('DataQueryTest_C'); + $baseDBTable = ClassInfo::baseDataClass('DataQueryTest_C'); + $newDQ->applyRelation('ManyTestAs'); + //check we are "joined" to the DataObject's table (there is no distinction between FROM or JOIN clauses) + $this->assertTrue($newDQ->query()->isJoinedTo($baseDBTable)); + //check we are explicitly selecting "FROM" the DO's table + $this->assertContains("FROM \"$baseDBTable\"", $newDQ->sql()); + + //test many_many with shared inheritance + $newDQ = new DataQuery('DataQueryTest_E'); + $baseDBTable = ClassInfo::baseDataClass('DataQueryTest_E'); + //check we are "joined" to the DataObject's table (there is no distinction between FROM or JOIN clauses) + $this->assertTrue($newDQ->query()->isJoinedTo($baseDBTable)); + //check we are explicitly selecting "FROM" the DO's table + $this->assertContains("FROM \"$baseDBTable\"", $newDQ->sql(), 'The FROM clause is missing from the query'); + $newDQ->applyRelation('ManyTestGs'); + //confirm we are still joined to the base table + $this->assertTrue($newDQ->query()->isJoinedTo($baseDBTable)); + //double check it is the "FROM" clause + $this->assertContains("FROM \"$baseDBTable\"", $newDQ->sql(), 'The FROM clause has been removed from the query'); + //another (potentially less crude check) for checking "FROM" clause + $fromTables = $newDQ->query()->getFrom(); + $this->assertEquals('"' . $baseDBTable . '"', $fromTables[$baseDBTable]); } public function testRelationReturn() { @@ -195,7 +224,7 @@ class DataQueryTest extends SapphireTest { $query = $query->distinct(true); $this->assertContains('SELECT DISTINCT', $query->sql($params), 'Query contains distinct'); } - + public function testComparisonClauseInt() { DB::query("INSERT INTO \"DataQueryTest_F\" (\"SortOrder\") VALUES (2)"); $query = new DataQuery('DataQueryTest_F'); @@ -203,7 +232,7 @@ class DataQueryTest extends SapphireTest { $this->assertGreaterThan(0, $query->count(), "Couldn't find SortOrder"); $this->resetDBSchema(true); } - + public function testComparisonClauseDateFull() { DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')"); $query = new DataQuery('DataQueryTest_F'); @@ -211,7 +240,7 @@ class DataQueryTest extends SapphireTest { $this->assertGreaterThan(0, $query->count(), "Couldn't find MyDate"); $this->resetDBSchema(true); } - + public function testComparisonClauseDateStartsWith() { DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')"); $query = new DataQuery('DataQueryTest_F'); @@ -219,7 +248,7 @@ class DataQueryTest extends SapphireTest { $this->assertGreaterThan(0, $query->count(), "Couldn't find MyDate"); $this->resetDBSchema(true); } - + public function testComparisonClauseDateStartsPartial() { DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')"); $query = new DataQuery('DataQueryTest_F'); @@ -227,7 +256,7 @@ class DataQueryTest extends SapphireTest { $this->assertGreaterThan(0, $query->count(), "Couldn't find MyDate"); $this->resetDBSchema(true); } - + public function testComparisonClauseTextCaseInsensitive() { DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyString\") VALUES ('HelloWorld')"); $query = new DataQuery('DataQueryTest_F'); @@ -235,13 +264,13 @@ class DataQueryTest extends SapphireTest { $this->assertGreaterThan(0, $query->count(), "Couldn't find MyString"); $this->resetDBSchema(true); } - + public function testComparisonClauseTextCaseSensitive() { DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyString\") VALUES ('HelloWorld')"); $query = new DataQuery('DataQueryTest_F'); $query->where(DB::get_conn()->comparisonClause('"MyString"', 'HelloWorld', false, false, true)); $this->assertGreaterThan(0, $query->count(), "Couldn't find MyString"); - + $query2 = new DataQuery('DataQueryTest_F'); $query2->where(DB::get_conn()->comparisonClause('"MyString"', 'helloworld', false, false, true)); $this->assertEquals(0, $query2->count(), "Found mystring. Shouldn't be able too."); @@ -310,6 +339,10 @@ class DataQueryTest_E extends DataQueryTest_C implements TestOnly { 'SortOrder' => 'Int' ); + private static $many_many = array( + 'ManyTestGs' => 'DataQueryTest_G', + ); + private static $default_sort = '"DataQueryTest_E"."SortOrder" ASC'; } @@ -321,3 +354,11 @@ class DataQueryTest_F extends DataObject implements TestOnly { 'MyString' => 'Text' ); } + +class DataQueryTest_G extends DataQueryTest_C implements TestOnly { + + private static $belongs_many_many = array( + 'ManyTestEs' => 'DataQueryTest_E', + ); + +} diff --git a/tests/model/HasManyListTest.php b/tests/model/HasManyListTest.php index 100b861f2..a9a72403f 100644 --- a/tests/model/HasManyListTest.php +++ b/tests/model/HasManyListTest.php @@ -10,6 +10,8 @@ class HasManyListTest extends SapphireTest { 'DataObjectTest_SubTeam', 'DataObjectTest_Player', 'DataObjectTest_TeamComment', + 'ManyManyListTest_Product', + 'ManyManyListTest_Category', ); public function testRelationshipEmptyOnNewRecords() { diff --git a/tests/model/ManyManyListTest.php b/tests/model/ManyManyListTest.php index 11bd354ee..f912ba753 100644 --- a/tests/model/ManyManyListTest.php +++ b/tests/model/ManyManyListTest.php @@ -15,6 +15,8 @@ class ManyManyListTest extends SapphireTest { 'DataObjectTest_Company', 'DataObjectTest_TeamComment', 'ManyManyListTest_ExtraFields', + 'ManyManyListTest_Product', + 'ManyManyListTest_Category', ); @@ -267,6 +269,17 @@ class ManyManyListTest extends SapphireTest { $this->assertSQLEquals($expected, $list->sql($parameters)); } + public function testFilteringOnPreviouslyJoinedTable() { + + /** @var ManyManyListTest_Category $category */ + $category = $this->objFromFixture('ManyManyListTest_Category', 'categorya'); + + /** @var ManyManyList $productsRelatedToProductB */ + $productsRelatedToProductB = $category->Products()->filter('RelatedProducts.Title', 'Product B'); + + $this->assertEquals(1, $productsRelatedToProductB->count()); + } + } @@ -291,3 +304,33 @@ class ManyManyListTest_ExtraFields extends DataObject implements TestOnly { ) ); } + +class ManyManyListTest_Product extends DataObject implements TestOnly { + + private static $db = array( + 'Title' => 'Varchar' + ); + + private static $many_many = array( + 'RelatedProducts' => 'ManyManyListTest_Product' + ); + + private static $belongs_many_many = array( + 'RelatedTo' => 'ManyManyListTest_Product', + 'Categories' => 'ManyManyListTest_Category' + ); + +} + +class ManyManyListTest_Category extends DataObject implements TestOnly { + + private static $db = array( + 'Title' => 'Varchar' + ); + + private static $many_many = array( + 'Products' => 'ManyManyListTest_Product' + ); + +} + diff --git a/tests/model/MapTest.php b/tests/model/MapTest.php index 43ca7cd19..119c39210 100755 --- a/tests/model/MapTest.php +++ b/tests/model/MapTest.php @@ -5,7 +5,7 @@ * @subpackage tests */ class SS_MapTest extends SapphireTest { - + // Borrow the model from DataObjectTest protected static $fixture_file = 'DataObjectTest.yml'; @@ -18,9 +18,11 @@ class SS_MapTest extends SapphireTest { 'DataObjectTest_FieldlessSubTable', 'DataObjectTest_ValidatedObject', 'DataObjectTest_Player', - 'DataObjectTest_TeamComment' + 'DataObjectTest_TeamComment', + 'ManyManyListTest_Product', + 'ManyManyListTest_Category', ); - + public function testValues() { $list = DataObjectTest_TeamComment::get()->sort('Name'); @@ -76,13 +78,13 @@ class SS_MapTest extends SapphireTest { . "Bob: This is a team comment by Bob\n" . "Phil: Phil is a unique guy, and comments on team2\n", $text); } - + public function testDefaultConfigIsIDAndTitle() { $list = DataObjectTest_Team::get(); $map = new SS_Map($list); $this->assertEquals('Team 1', $map[$this->idFromFixture('DataObjectTest_Team', 'team1')]); } - + public function testSetKeyFieldAndValueField() { $list = DataObjectTest_TeamComment::get(); $map = new SS_Map($list); @@ -90,7 +92,7 @@ class SS_MapTest extends SapphireTest { $map->setValueField('Comment'); $this->assertEquals('This is a team comment by Joe', $map['Joe']); } - + public function testToArray() { $list = DataObjectTest_TeamComment::get(); $map = new SS_Map($list, 'Name', 'Comment'); @@ -168,10 +170,10 @@ class SS_MapTest extends SapphireTest { "Phil" => "Phil is a unique guy, and comments on team2"), $map->toArray()); $map->unshift(0, '(Select)'); - + $this->assertEquals('(All)', $map[-1]); $this->assertEquals('(Select)', $map[0]); - + $this->assertEquals(array( 0 => "(Select)", -1 => "(All)", @@ -217,7 +219,7 @@ class SS_MapTest extends SapphireTest { 1 => "(All)" ), $map->toArray()); } - + public function testCount() { $list = DataObjectTest_TeamComment::get(); $map = new SS_Map($list, 'Name', 'Comment'); @@ -262,7 +264,7 @@ class SS_MapTest extends SapphireTest { $list = DataObjectTest_TeamComment::get()->sort('ID'); $map = new SS_Map($list, 'Name', 'Comment'); $map->push(1, 'Pushed'); - + $text = ""; foreach($map as $k => $v) { @@ -285,7 +287,7 @@ class SS_MapTest extends SapphireTest { foreach($map as $k => $v) { $text .= "$k: $v\n"; } - + $this->assertEquals("1: unshifted\n", $text); } @@ -298,7 +300,7 @@ class SS_MapTest extends SapphireTest { foreach($map as $k => $v) { $text .= "$k: $v\n"; } - + $this->assertEquals("1: pushed\n", $text); } } diff --git a/tests/model/PaginatedListTest.php b/tests/model/PaginatedListTest.php index d02e616f7..73dc5f879 100644 --- a/tests/model/PaginatedListTest.php +++ b/tests/model/PaginatedListTest.php @@ -12,7 +12,9 @@ class PaginatedListTest extends SapphireTest { protected $extraDataObjects = array( 'DataObjectTest_Team', 'DataObjectTest_SubTeam', - 'DataObjectTest_Player' + 'DataObjectTest_Player', + 'ManyManyListTest_Product', + 'ManyManyListTest_Category', ); public function testPageStart() {