silverstripe-framework/tests/model/DataQueryTest.php
Daniel Hensby dd554d883f
Proving bug with Deep nested many_many relations
When adding a filter to a many_many with a shared inheritance, the FROM table is removed and added as a LEFT JOIN which causes a syntax error.

This means `$dataList->filter('ManyManyRel.ID', array(1,2))` doesn't work.
2016-05-12 12:38:02 +01:00

361 lines
12 KiB
PHP

<?php
class DataQueryTest extends SapphireTest {
protected static $fixture_file = 'DataQueryTest.yml';
protected $extraDataObjects = array(
'DataQueryTest_A',
'DataQueryTest_B',
'DataQueryTest_C',
'DataQueryTest_D',
'DataQueryTest_E',
'DataQueryTest_F',
'DataQueryTest_G',
);
public function testSortByJoinedFieldRetainsSourceInformation() {
$bar = new DataQueryTest_C();
$bar->Title = "Bar";
$bar->write();
$foo = new DataQueryTest_B();
$foo->Title = "Foo";
$foo->TestC = $bar->ID;
$foo->write();
$query = new DataQuery('DataQueryTest_B');
$result = $query->leftJoin(
'DataQueryTest_C',
"\"DataQueryTest_B\".\"TestCID\" = \"DataQueryTest_B\".\"ID\""
)->sort('"DataQueryTest_B"."Title"', 'ASC');
$result = $result->execute()->record();
$this->assertEquals('Foo', $result['Title']);
}
/**
* Test the leftJoin() and innerJoin method of the DataQuery object
*/
public function testJoins() {
$dq = new DataQuery('Member');
$dq->innerJoin("Group_Members", "\"Group_Members\".\"MemberID\" = \"Member\".\"ID\"");
$this->assertContains("INNER JOIN \"Group_Members\" ON \"Group_Members\".\"MemberID\" = \"Member\".\"ID\"",
$dq->sql());
$dq = new DataQuery('Member');
$dq->leftJoin("Group_Members", "\"Group_Members\".\"MemberID\" = \"Member\".\"ID\"");
$this->assertContains("LEFT JOIN \"Group_Members\" ON \"Group_Members\".\"MemberID\" = \"Member\".\"ID\"",
$dq->sql());
}
public function testApplyRelation() {
// Test applyRelation with two has_ones pointing to the same class
$dq = new DataQuery('DataQueryTest_B');
$dq->applyRelation('TestC');
$this->assertTrue($dq->query()->isJoinedTo('DataQueryTest_C'));
$this->assertContains('"DataQueryTest_C"."ID" = "DataQueryTest_B"."TestCID"', $dq->sql());
$dq = new DataQuery('DataQueryTest_B');
$dq->applyRelation('TestCTwo');
$this->assertTrue($dq->query()->isJoinedTo('DataQueryTest_C'));
$this->assertContains('"DataQueryTest_C"."ID" = "DataQueryTest_B"."TestCTwoID"', $dq->sql());
}
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());
//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() {
$dq = new DataQuery('DataQueryTest_C');
$this->assertEquals('DataQueryTest_A', $dq->applyRelation('TestA'),
'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_A', $dq->applyRelation('TestAs'),
'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_A', $dq->applyRelation('ManyTestAs'),
'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_B', $dq->applyRelation('TestB'),
'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_B', $dq->applyRelation('TestBs'),
'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_B', $dq->applyRelation('ManyTestBs'),
'DataQuery::applyRelation should return the name of the related object.');
$newDQ = new DataQuery('DataQueryTest_E');
$this->assertEquals('DataQueryTest_A', $newDQ->applyRelation('TestA'),
'DataQuery::applyRelation should return the name of the related object.');
}
public function testRelationOrderWithCustomJoin() {
$dataQuery = new DataQuery('DataQueryTest_B');
$dataQuery->innerJoin('DataQueryTest_D', '"DataQueryTest_D"."RelationID" = "DataQueryTest_B"."ID"');
$dataQuery->execute();
}
public function testDisjunctiveGroup() {
$dq = new DataQuery('DataQueryTest_A');
$dq->where('DataQueryTest_A.ID = 2');
$subDq = $dq->disjunctiveGroup();
$subDq->where('DataQueryTest_A.Name = \'John\'');
$subDq->where('DataQueryTest_A.Name = \'Bob\'');
$this->assertContains(
"WHERE (DataQueryTest_A.ID = 2) AND ((DataQueryTest_A.Name = 'John') OR (DataQueryTest_A.Name = 'Bob'))",
$dq->sql()
);
}
public function testConjunctiveGroup() {
$dq = new DataQuery('DataQueryTest_A');
$dq->where('DataQueryTest_A.ID = 2');
$subDq = $dq->conjunctiveGroup();
$subDq->where('DataQueryTest_A.Name = \'John\'');
$subDq->where('DataQueryTest_A.Name = \'Bob\'');
$this->assertContains(
"WHERE (DataQueryTest_A.ID = 2) AND ((DataQueryTest_A.Name = 'John') AND (DataQueryTest_A.Name = 'Bob'))",
$dq->sql()
);
}
public function testNestedGroups() {
$dq = new DataQuery('DataQueryTest_A');
$dq->where('DataQueryTest_A.ID = 2');
$subDq = $dq->disjunctiveGroup();
$subDq->where('DataQueryTest_A.Name = \'John\'');
$subSubDq = $subDq->conjunctiveGroup();
$subSubDq->where('DataQueryTest_A.Age = 18');
$subSubDq->where('DataQueryTest_A.Age = 50');
$subDq->where('DataQueryTest_A.Name = \'Bob\'');
$this->assertContains(
"WHERE (DataQueryTest_A.ID = 2) AND ((DataQueryTest_A.Name = 'John') OR ((DataQueryTest_A.Age = 18) "
. "AND (DataQueryTest_A.Age = 50)) OR (DataQueryTest_A.Name = 'Bob'))",
$dq->sql()
);
}
public function testEmptySubgroup() {
$dq = new DataQuery('DataQueryTest_A');
$dq->conjunctiveGroup();
$this->assertContains('WHERE (1=1)', $dq->sql());
}
public function testSubgroupHandoff() {
$dq = new DataQuery('DataQueryTest_A');
$subDq = $dq->disjunctiveGroup();
$orgDq = clone $dq;
$subDq->sort('"DataQueryTest_A"."Name"');
$orgDq->sort('"DataQueryTest_A"."Name"');
$this->assertEquals($dq->sql(), $orgDq->sql());
$subDq->limit(5, 7);
$orgDq->limit(5, 7);
$this->assertEquals($dq->sql(), $orgDq->sql());
}
public function testOrderByMultiple() {
$dq = new DataQuery('SQLQueryTest_DO');
$dq = $dq->sort('"Name" ASC, MID("Name", 8, 1) DESC');
$this->assertContains(
'ORDER BY "Name" ASC, "_SortColumn0" DESC',
$dq->sql()
);
}
public function testDefaultSort() {
$query = new DataQuery('DataQueryTest_E');
$result = $query->column('Title');
$this->assertEquals(array('First', 'Second', 'Last'), $result);
}
public function testDistinct() {
$query = new DataQuery('DataQueryTest_E');
$this->assertContains('SELECT DISTINCT', $query->sql(), 'Query is set as distinct by default');
$query = $query->distinct(false);
$this->assertNotContains('SELECT DISTINCT', $query->sql(), 'Query does not contain distinct');
$query = $query->distinct(true);
$this->assertContains('SELECT DISTINCT', $query->sql(), 'Query contains distinct');
}
public function testComparisonClauseInt() {
DB::query("INSERT INTO \"DataQueryTest_F\" (\"SortOrder\") VALUES (2)");
$query = new DataQuery('DataQueryTest_F');
$query->where(DB::getConn()->comparisonClause('"SortOrder"', '2'));
$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');
$query->where(DB::getConn()->comparisonClause('"MyDate"', '1988-03-04%'));
$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');
$query->where(DB::getConn()->comparisonClause('"MyDate"', '1988%'));
$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');
$query->where(DB::getConn()->comparisonClause('"MyDate"', '%03-04%'));
$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');
$query->where(DB::getConn()->comparisonClause('"MyString"', 'helloworld'));
$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::getConn()->comparisonClause('"MyString"', 'HelloWorld', false, false, true));
$this->assertGreaterThan(0, $query->count(), "Couldn't find MyString");
$query2 = new DataQuery('DataQueryTest_F');
$query2->where(DB::getConn()->comparisonClause('"MyString"', 'helloworld', false, false, true));
$this->assertEquals(0, $query2->count(), "Found mystring. Shouldn't be able too.");
$this->resetDBSchema(true);
}
}
class DataQueryTest_A extends DataObject implements TestOnly {
private static $db = array(
'Name' => 'Varchar',
);
private static $has_one = array(
'TestC' => 'DataQueryTest_C',
);
}
class DataQueryTest_B extends DataObject implements TestOnly {
private static $db = array(
'Title' => 'Varchar',
);
private static $has_one = array(
'TestC' => 'DataQueryTest_C',
'TestCTwo' => 'DataQueryTest_C',
);
}
class DataQueryTest_C extends DataObject implements TestOnly {
private static $db = array(
'Title' => 'Varchar'
);
private static $has_one = array(
'TestA' => 'DataQueryTest_A',
'TestB' => 'DataQueryTest_B',
);
private static $has_many = array(
'TestAs' => 'DataQueryTest_A',
'TestBs' => 'DataQueryTest_B.TestC',
'TestBsTwo' => 'DataQueryTest_B.TestCTwo',
);
private static $many_many = array(
'ManyTestAs' => 'DataQueryTest_A',
'ManyTestBs' => 'DataQueryTest_B',
);
}
class DataQueryTest_D extends DataObject implements TestOnly {
private static $has_one = array(
'Relation' => 'DataQueryTest_B',
);
}
class DataQueryTest_E extends DataQueryTest_C implements TestOnly {
private static $db = array(
'SortOrder' => 'Int'
);
private static $many_many = array(
'ManyTestGs' => 'DataQueryTest_G',
);
private static $default_sort = '"DataQueryTest_E"."SortOrder" ASC';
}
class DataQueryTest_F extends DataObject implements TestOnly {
private static $db = array(
'SortOrder' => 'Int',
'MyDate' => 'SS_Datetime',
'MyString' => 'Text'
);
}
class DataQueryTest_G extends DataQueryTest_C implements TestOnly {
private static $belongs_many_many = array(
'ManyTestEs' => 'DataQueryTest_E',
);
}