connection = $this->createConnectionFake(); $this->queryBuilder = new QueryBuilder($this->connection); } public function testExpr(): void { $expr = $this->queryBuilder->expr(); Assert::assertInstanceOf(ExpressionBuilder::class, $expr); Assert::assertSame($expr, $this->queryBuilder->expr()); } public function testSetParameter(): void { $queryBuilder = $this->queryBuilder->setParameter(':one', 'first'); Assert::assertSame($queryBuilder, $this->queryBuilder); $this->queryBuilder->setParameter('two', true); $this->queryBuilder->setParameter(':three', false); $this->queryBuilder->setParameter(4, 'fourth'); Assert::assertSame([ 'one' => 'first', 'two' => 1, 'three' => 0, 4 => 'fourth', ], $this->queryBuilder->getParameters()); } public function testSetQueryPart(): void { $this->queryBuilder->select('t.name', 't.enabled') ->distinct() ->from('table1', 't') ->leftJoin('t', 'table2', 'j', 't.id = j.tid') ->where('t.enabled = 1'); $queryBuilder = $this->queryBuilder->setQueryPart('select', 't.name'); Assert::assertSame($queryBuilder, $this->queryBuilder); $this->queryBuilder->setQueryPart('where', 't.enabled = 0'); $this->queryBuilder->setQueryPart('groupBy', 'j.code'); $this->queryBuilder->setQueryPart('distinct', null); $this->assertSQL('SELECT t.name FROM table1 t LEFT JOIN table2 j ON t.id = j.tid WHERE t.enabled = 0 GROUP BY j.code'); } public function testGetSQLSelectSimple(): void { $this->queryBuilder->select('1') ->from('table1'); $this->assertSQL('SELECT 1 FROM table1', 2); } public function testGetSQLSelectComplex(): void { $this->queryBuilder->select('t.name') ->from('table1', 't') ->leftJoin('t', 'table2', 'j', 't.id = j.fid') ->where('t.enabled = 1') ->groupBy('t.type') ->having('t.salary > 5000') ->orderBy('t.id', 'DESC') ->setFirstResult(30) ->setMaxResults(10); $this->assertSQL('SELECT t.name FROM table1 t LEFT JOIN table2 j ON t.id = j.fid WHERE t.enabled = 1 GROUP BY t.type HAVING t.salary > 5000 ORDER BY t.id DESC LIMIT 10 OFFSET 30', 2); } public function testGetSQLSelectHint(): void { $this->queryBuilder->select('1') ->add('from', [ 'table' => 'table1', 'alias' => 't', 'hint' => 'USE INDEX (`PRIMARY`)', ], true) ->where('t.enabled = 0'); $this->assertSQL('SELECT 1 FROM table1 t USE INDEX (`PRIMARY`) WHERE t.enabled = 0', 2); } public function testGetSQLInsert(): void { $this->queryBuilder->insert('table1') ->values(['name' => 'Jack', 'enabled' => 1]); $this->assertSQL('INSERT INTO table1 (name, enabled) VALUES(Jack, 1)', 2); } public function testGetSQLUpdate(): void { $this->queryBuilder->update('table1') ->set('enabled', '1') ->where('enabled = 0'); $this->assertSQL('UPDATE table1 SET enabled = 1 WHERE enabled = 0', 2); } public function testGetSQLDelete(): void { $this->queryBuilder->delete('table1') ->where('enabled = 1'); $this->assertSQL('DELETE FROM table1 WHERE enabled = 1', 2); } public function testGetJoinCondition(): void { $this->queryBuilder->select('t.name') ->from('table1', 'l') ->leftJoin('l', 'table2', 'j', 'l.id = j.fid'); Assert::assertSame('l.id = j.fid', $this->queryBuilder->getJoinCondition('j')); Assert::assertFalse($this->queryBuilder->getJoinCondition('k')); } public function testAddJoinCondition(): void { $this->queryBuilder->select('t.name') ->from('table1', 't') ->leftJoin('t', 'table2', 'j', 't.id = j.fid'); $this->queryBuilder->addJoinCondition('j', $this->queryBuilder->expr()->eq('j.removed', 1)); $this->assertSQL('SELECT t.name FROM table1 t LEFT JOIN table2 j ON t.id = j.fid and (j.removed = 1)'); } public function testAddJoinConditionNonExistentJoin(): void { $this->queryBuilder->select('t.name') ->from('table1', 't') ->leftJoin('t', 'table2', 'j', 't.id = j.fid'); $this->expectException(QueryException::class); $this->expectExceptionMessage('Inserting condition to nonexistent join x'); $this->queryBuilder->addJoinCondition('x', $this->queryBuilder->expr()->eq('j.removed', 1)); } public function testReplaceJoinCondition(): void { $this->queryBuilder->select('t.name') ->from('table1', 'l') ->leftJoin('l', 'table2', 'j', 'l.id = j.fid'); $this->queryBuilder->replaceJoinCondition('j', $this->queryBuilder->expr()->eq('j.removed', 1)); $this->assertSQL('SELECT t.name FROM table1 l LEFT JOIN table2 j ON j.removed = 1'); } public function testSetParametersPairsNonArray(): void { $queryBuilder = $this->queryBuilder->setParametersPairs('one', 'first'); Assert::assertSame($queryBuilder, $this->queryBuilder); $this->queryBuilder->setParametersPairs('two', 'second'); $this->queryBuilder->setParametersPairs(':three', 'third'); Assert::assertSame([ 'one' => 'first', 'two' => 'second', 'three' => 'third', ], $this->queryBuilder->getParameters()); } public function testSetParametersPairsWithArray(): void { $queryBuilder = $this->queryBuilder->setParametersPairs(['one', 'three', ':five'], ['first', 'third', 'fifth']); Assert::assertSame($queryBuilder, $this->queryBuilder); Assert::assertSame([ 'one' => 'first', 'three' => 'third', 'five' => 'fifth', ], $this->queryBuilder->getParameters()); } public function testGetTableAlias(): void { $this->queryBuilder->select('1') ->from('tableFrom', 'f') ->leftJoin('f', 'leftJoinTable', 'l', 'f.id = l.fid') ->rightJoin('l', 'rightJoinTable', 'r', 'l.id = r.lid') ->innerJoin('f', 'innerJoinTable', 'i', 'f.id = i.fid') ->where('t.enabled = 1') ->groupBy('t.type') ->having('t.salary > 5000') ->orderBy('t.id', 'DESC') ->setFirstResult(30) ->setMaxResults(10); Assert::assertFalse($this->queryBuilder->getTableAlias('nonExistent')); Assert::assertFalse($this->queryBuilder->getTableAlias('nonExistent', 'inner')); Assert::assertFalse($this->queryBuilder->getTableAlias('nonExistent', 'left')); Assert::assertFalse($this->queryBuilder->getTableAlias('nonExistent', 'right')); Assert::assertSame('f', $this->queryBuilder->getTableAlias('tableFrom')); Assert::assertFalse($this->queryBuilder->getTableAlias('tableFrom', 'inner')); Assert::assertFalse($this->queryBuilder->getTableAlias('tableFrom', 'left')); Assert::assertFalse($this->queryBuilder->getTableAlias('tableFrom', 'right')); Assert::assertSame('l', $this->queryBuilder->getTableAlias('leftJoinTable')); Assert::assertFalse($this->queryBuilder->getTableAlias('leftJoinTable', 'inner')); Assert::assertSame('l', $this->queryBuilder->getTableAlias('leftJoinTable', 'left')); Assert::assertFalse($this->queryBuilder->getTableAlias('leftJoinTable', 'right')); Assert::assertSame('r', $this->queryBuilder->getTableAlias('rightJoinTable')); Assert::assertFalse($this->queryBuilder->getTableAlias('rightJoinTable', 'inner')); Assert::assertFalse($this->queryBuilder->getTableAlias('rightJoinTable', 'left')); Assert::assertSame('r', $this->queryBuilder->getTableAlias('rightJoinTable', 'right')); Assert::assertSame('i', $this->queryBuilder->getTableAlias('innerJoinTable')); Assert::assertSame('i', $this->queryBuilder->getTableAlias('innerJoinTable', 'inner')); Assert::assertFalse($this->queryBuilder->getTableAlias('innerJoinTable', 'left')); Assert::assertFalse($this->queryBuilder->getTableAlias('innerJoinTable', 'right')); } public function testGetTableJoins(): void { $this->queryBuilder->select('1') ->from('tableFrom', 'f') ->leftJoin('f', 'leftJoinTable', 'l', 'f.id = l.fid') ->rightJoin('l', 'rightJoinTable', 'r', 'l.id = r.lid') ->innerJoin('f', 'innerJoinTable', 'i', 'f.id = i.fid') ->innerJoin('f', 'innerJoinTable', 'i2', 'f.id = i2.fid') ->where('t.enabled = 1') ->groupBy('t.type') ->having('t.salary > 5000') ->orderBy('t.id', 'DESC') ->setFirstResult(30) ->setMaxResults(10); Assert::assertSame([], $this->queryBuilder->getTableJoins('nonExistent')); Assert::assertSame([], $this->queryBuilder->getTableJoins('tableFrom')); Assert::assertSame([ [ 'joinType' => 'left', 'joinTable' => 'leftJoinTable', 'joinAlias' => 'l', 'joinCondition' => 'f.id = l.fid', ], ], $this->queryBuilder->getTableJoins('leftJoinTable')); Assert::assertSame([ [ 'joinType' => 'right', 'joinTable' => 'rightJoinTable', 'joinAlias' => 'r', 'joinCondition' => 'l.id = r.lid', ], ], $this->queryBuilder->getTableJoins('rightJoinTable')); Assert::assertSame([ [ 'joinType' => 'inner', 'joinTable' => 'innerJoinTable', 'joinAlias' => 'i', 'joinCondition' => 'f.id = i.fid', ], [ 'joinType' => 'inner', 'joinTable' => 'innerJoinTable', 'joinAlias' => 'i2', 'joinCondition' => 'f.id = i2.fid', ], ], $this->queryBuilder->getTableJoins('innerJoinTable')); } public function testGuessPrimaryLeadContactIdColumnWithOrphanedLeads(): void { $this->queryBuilder->select('1') ->from('lead_lists_leads', 'orp'); Assert::assertSame('orp.lead_id', $this->queryBuilder->guessPrimaryLeadContactIdColumn()); } public function testGuessPrimaryLeadContactIdColumnWithoutJoins(): void { $this->queryBuilder->select('1') ->from('leads', 'l'); Assert::assertSame('l.id', $this->queryBuilder->guessPrimaryLeadContactIdColumn()); } public function testGuessPrimaryLeadContactIdColumnWithNonRightJoin(): void { $this->queryBuilder->select('1') ->from('leads', 'l') ->leftJoin('l', 'leftJoinTable', 'lj', 'l.id = lj.lid') ->innerJoin('l', 'innerJoinTable', 'ij', 'l.id = ij.lid'); Assert::assertSame('l.id', $this->queryBuilder->guessPrimaryLeadContactIdColumn()); } public function testGuessPrimaryLeadContactIdColumnWithNonMatchingRightJoin(): void { $this->queryBuilder->select('1') ->from('leads', 'l') ->rightJoin('l', 'rightJoinTable', 'r', 'l.name = r.name'); Assert::assertSame('l.id', $this->queryBuilder->guessPrimaryLeadContactIdColumn()); } public function testGuessPrimaryLeadContactIdColumnWithMatchingRightJoin(): void { $this->queryBuilder->select('1') ->from('leads', 'l') ->rightJoin('l', 'rightJoinTable', 'r', 'l.id = r.lid'); Assert::assertSame('r.lid', $this->queryBuilder->guessPrimaryLeadContactIdColumn()); } public function testIsJoinTable(): void { $this->queryBuilder->select('1') ->from('leads', 'l') ->leftJoin('l', 'leftJoinTable', 'lj', 'l.id = lj.lid') ->rightJoin('l', 'rightJoinTable', 'rj', 'l.id = rj.lid') ->innerJoin('l', 'innerJoinTable', 'ij', 'l.id = ij.lid'); Assert::assertFalse($this->queryBuilder->isJoinTable('nonExistent')); Assert::assertFalse($this->queryBuilder->isJoinTable('leads')); Assert::assertTrue($this->queryBuilder->isJoinTable('leftJoinTable')); Assert::assertTrue($this->queryBuilder->isJoinTable('rightJoinTable')); Assert::assertTrue($this->queryBuilder->isJoinTable('innerJoinTable')); } public function testGetDebugOutput(): void { $this->queryBuilder->select('t.name') ->from('table1', 't') ->leftJoin('t', 'table2', 'j', 't.id = j.fid') ->where('t.enabled = :enabled') ->andWhere('t.state IN (:states)') ->groupBy('t.type') ->having('t.salary > :salary AND t.flag = :flag') ->orderBy('t.id', 'DESC') ->setParameter('enabled', true) ->setParameter(':salary', 5000) ->setParameter('states', ['new', 'active'], ArrayParameterType::STRING) ->setParameter('flag', 'internal') ->setFirstResult(30) ->setMaxResults(10); Assert::assertSame("SELECT t.name FROM table1 t LEFT JOIN table2 j ON t.id = j.fid WHERE (t.enabled = 1) AND (t.state IN ('new', 'active')) GROUP BY t.type HAVING t.salary > 5000 AND t.flag = 'internal' ORDER BY t.id DESC LIMIT 10 OFFSET 30", $this->queryBuilder->getDebugOutput()); } public function testHasLogicStack(): void { $this->queryBuilder->select('t.name') ->from('table1', 't') ->where('t.enabled = 1'); Assert::assertFalse($this->queryBuilder->hasLogicStack()); $this->queryBuilder->addLogic($this->queryBuilder->expr()->eq('a.name', 'John'), 'OR'); Assert::assertTrue($this->queryBuilder->hasLogicStack()); } public function testGetLogicStack(): void { $this->queryBuilder->select('t.name') ->from('table1', 't') ->where('t.enabled = 1'); Assert::assertSame([], $this->queryBuilder->getLogicStack()); $this->queryBuilder->addLogic($this->queryBuilder->expr()->eq('a.name', 'John'), 'OR'); $this->queryBuilder->addLogic($this->queryBuilder->expr()->lt('a.salary', 3000), 'AND'); Assert::assertSame([ 'a.name = John', 'a.salary < 3000', ], $this->queryBuilder->getLogicStack()); } public function testPopLogicStack(): void { $this->queryBuilder->select('t.name') ->from('table1', 't') ->where('t.enabled = 1'); $this->queryBuilder->addLogic($this->queryBuilder->expr()->eq('a.name', 'John'), 'OR'); $this->queryBuilder->addLogic($this->queryBuilder->expr()->lt('a.salary', 3000), 'AND'); Assert::assertSame([ 'a.name = John', 'a.salary < 3000', ], $this->queryBuilder->popLogicStack()); Assert::assertSame([], $this->queryBuilder->getLogicStack()); } public function testAddLogicOrWithEmptyWhere(): void { $this->queryBuilder->select('t.name') ->from('table1', 't'); $this->queryBuilder->addLogic($this->queryBuilder->expr()->eq('a.name', 'John'), 'OR'); Assert::assertSame([], $this->queryBuilder->getLogicStack()); $this->assertSQL('SELECT t.name FROM table1 t WHERE a.name = John'); $this->queryBuilder->applyStackLogic(); $this->assertSQL('SELECT t.name FROM table1 t WHERE a.name = John'); } public function testAddLogicOrWithExistingWhereWithEmptyStack(): void { $this->queryBuilder->select('t.name') ->from('table1', 't') ->where('t.enabled = 1'); $this->queryBuilder->addLogic($this->queryBuilder->expr()->eq('a.name', 'John'), 'OR'); Assert::assertSame(['a.name = John'], $this->queryBuilder->getLogicStack()); $this->assertSQL('SELECT t.name FROM table1 t WHERE t.enabled = 1'); $this->queryBuilder->applyStackLogic(); $this->assertSQL('SELECT t.name FROM table1 t WHERE (t.enabled = 1) OR (a.name = John)'); } public function testAddLogicOrWithExistingWhereWithExistingStack(): void { $this->queryBuilder->select('t.name') ->from('table1', 't') ->where('t.enabled = 1'); $this->queryBuilder->addLogic($this->queryBuilder->expr()->eq('a.name', 'John'), 'OR'); $this->queryBuilder->addLogic($this->queryBuilder->expr()->eq('a.flag', 'active'), 'OR'); Assert::assertSame(['a.flag = active'], $this->queryBuilder->getLogicStack()); $this->assertSQL('SELECT t.name FROM table1 t WHERE (t.enabled = 1) OR (a.name = John)'); $this->queryBuilder->applyStackLogic(); $this->assertSQL('SELECT t.name FROM table1 t WHERE (t.enabled = 1) OR (a.name = John) OR (a.flag = active)'); } public function testAddLogicAndWithEmptyWhere(): void { $this->queryBuilder->select('t.name') ->from('table1', 't'); $this->queryBuilder->addLogic($this->queryBuilder->expr()->eq('a.name', 'John'), 'AND'); Assert::assertSame([], $this->queryBuilder->getLogicStack()); $this->assertSQL('SELECT t.name FROM table1 t WHERE a.name = John'); $this->queryBuilder->applyStackLogic(); $this->assertSQL('SELECT t.name FROM table1 t WHERE a.name = John'); } public function testAddLogicAndWithExistingWhereWithEmptyStack(): void { $this->queryBuilder->select('t.name') ->from('table1', 't') ->where('t.enabled = 1'); $this->queryBuilder->addLogic($this->queryBuilder->expr()->eq('a.name', 'John'), 'AND'); Assert::assertSame([], $this->queryBuilder->getLogicStack()); $this->assertSQL('SELECT t.name FROM table1 t WHERE (t.enabled = 1) AND (a.name = John)'); $this->queryBuilder->applyStackLogic(); $this->assertSQL('SELECT t.name FROM table1 t WHERE (t.enabled = 1) AND (a.name = John)'); } public function testAddLogicAndWithExistingWhereWithExistingStack(): void { $this->queryBuilder->select('t.name') ->from('table1', 't') ->where('t.enabled = 1'); $this->queryBuilder->addLogic($this->queryBuilder->expr()->eq('a.name', 'John'), 'OR'); $this->queryBuilder->addLogic($this->queryBuilder->expr()->eq('a.flag', 'active'), 'AND'); Assert::assertSame([ 'a.name = John', 'a.flag = active', ], $this->queryBuilder->getLogicStack()); $this->assertSQL('SELECT t.name FROM table1 t WHERE t.enabled = 1'); $this->queryBuilder->applyStackLogic(); $this->assertSQL('SELECT t.name FROM table1 t WHERE (t.enabled = 1) OR ((a.name = John) AND (a.flag = active))'); } public function testApplyStackLogicWithEmptyStack(): void { $this->queryBuilder->select('t.name') ->from('table1', 't') ->where('t.enabled = 1'); $queryBuilder = $this->queryBuilder->applyStackLogic(); Assert::assertSame($queryBuilder, $this->queryBuilder); $this->assertSQL('SELECT t.name FROM table1 t WHERE t.enabled = 1'); } public function testApplyStackLogicWithExistingStack(): void { $this->queryBuilder->select('t.name') ->from('table1', 't') ->where('t.enabled = 1'); $this->queryBuilder->addLogic($this->queryBuilder->expr()->eq('a.name', 'John'), 'AND'); $this->queryBuilder->addLogic($this->queryBuilder->expr()->eq('a.flag', 'active'), 'AND'); $queryBuilder = $this->queryBuilder->applyStackLogic(); Assert::assertSame($queryBuilder, $this->queryBuilder); $this->assertSQL('SELECT t.name FROM table1 t WHERE (t.enabled = 1) AND (a.name = John) AND (a.flag = active)'); } private function assertSQL(string $sql, int $repeat = 1): void { for ($i = 0; $i < $repeat; ++$i) { Assert::assertSame($sql, $this->queryBuilder->getSQL()); } } private function createConnectionFake(): Connection { return new class() extends Connection { /** @noinspection PhpMissingParentConstructorInspection */ public function __construct() { } public function getDatabasePlatform() { return new MySQLPlatform(); } }; } }