From 9a23f7326572508cff5d1b72048af00c8545f196 Mon Sep 17 00:00:00 2001 From: Igor Krynychanskyi Date: Thu, 13 Apr 2017 13:26:36 +0300 Subject: [PATCH] BAP-14290: PostgresqlGridModifier add duplicate Order by identifier to final query (#9530) - Added fixes to prevent fields duplication in OrderBy query part --- .../Sorter/PostgresqlGridModifier.php | 21 ++- .../Sorter/PostgresqlGridModifierTest.php | 133 ++++++++++++++++++ 2 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 src/Oro/Bundle/DataGridBundle/Tests/Unit/Extension/Sorter/PostgresqlGridModifierTest.php diff --git a/src/Oro/Bundle/DataGridBundle/Extension/Sorter/PostgresqlGridModifier.php b/src/Oro/Bundle/DataGridBundle/Extension/Sorter/PostgresqlGridModifier.php index 611a00420eb..57ca266478e 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/Sorter/PostgresqlGridModifier.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/Sorter/PostgresqlGridModifier.php @@ -89,7 +89,7 @@ public function visitDatasource(DatagridConfiguration $config, DatasourceInterfa if ($alias && $this->isAllowedAddingSorting($alias, $identifier, $queryBuilder)) { $field = $alias . '.' . $identifier; $orderBy = $queryBuilder->getDQLPart('orderBy'); - if (!isset($orderBy[$field])) { + if (!$this->hasOrderByField($orderBy, $field)) { if ($this->isDistinct($queryBuilder)) { $this->ensureIdentifierSelected($queryBuilder, $field); } @@ -98,6 +98,23 @@ public function visitDatasource(DatagridConfiguration $config, DatasourceInterfa } } + /** + * Check field already exists in orders part + * + * @param array $orderBy + * @param string $field + * @return bool + */ + protected function hasOrderByField($orderBy, $field) + { + foreach ($orderBy as $order) { + if (preg_match(sprintf('/(^|\s)%s\s/i', $field), $order)) { + return true; + } + } + return false; + } + /** * @param DatagridConfiguration $config * @@ -127,7 +144,7 @@ protected function isAllowedAddingSorting($alias, $identifier, QueryBuilder $que $selectPartsWithAggregation = array_filter($selectParts, function ($selectPart) use ($forbiddenFunctions) { foreach ($forbiddenFunctions as $functionName) { - if (false !== stripos($selectPart, $functionName)) { + if (false !== stripos($selectPart, $functionName . '(')) { return true; } } diff --git a/src/Oro/Bundle/DataGridBundle/Tests/Unit/Extension/Sorter/PostgresqlGridModifierTest.php b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Extension/Sorter/PostgresqlGridModifierTest.php new file mode 100644 index 00000000000..800ac9a954b --- /dev/null +++ b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Extension/Sorter/PostgresqlGridModifierTest.php @@ -0,0 +1,133 @@ +entityClassResolver = $this->getMockBuilder(EntityClassResolver::class) + ->disableOriginalConstructor() + ->getMock(); + $this->extension = new PostgresqlGridModifier('pdo_pgsql', $this->entityClassResolver); + } + + /** + * @dataProvider visitDatasourceDataProvider + */ + public function testVisitDatasource($orderBy, $expected) + { + $em = $this->getMockBuilder(EntityManager::class) + ->disableOriginalConstructor() + ->getMock(); + + $metadata = $this->getMockBuilder(ClassMetadata::class) + ->disableOriginalConstructor() + ->getMock(); + + $em->expects(self::once()) + ->method('getClassMetadata') + ->with('Test\Entity') + ->willReturn($metadata); + + $metadata->expects(self::once()) + ->method('getSingleIdentifierFieldName') + ->willReturn('id'); + + $qb = new QueryBuilder($em); + $qb->select('e.id')->from('Test\Entity', 'e'); + + if (!empty($orderBy)) { + $orderByExpr = new OrderBy(); + foreach ($orderBy as $field => $des) { + $orderByExpr->add($field, $des); + } + $qb->addOrderBy($orderByExpr); + } + + $datasource = $this->getMockBuilder(OrmDatasource::class) + ->disableOriginalConstructor() + ->getMock(); + + $datasource->expects(self::once()) + ->method('getQueryBuilder') + ->willReturn($qb); + + $this->entityClassResolver->expects($this->any()) + ->method('getEntityClass') + ->willReturn('Test\Entity'); + + $dataGridConfig = DatagridConfiguration::create([ + 'sorters' => [ + 'columns' => [] + ], + 'source' => [ + 'type' => 'orm', + 'query' => [ + 'from' => [ + [ + 'table' => 'Test\Entity', + 'alias' => 'e' + ] + ] + ] + ] + ]); + + $this->extension->visitDatasource( + $dataGridConfig, + $datasource + ); + + self::assertEquals( + $expected, + $qb->getDQL() + ); + } + + /** + * @return array + */ + public function visitDatasourceDataProvider() + { + return [ + 'OrderBy has only primary key field' => [ + ['e.id' => 'ASC'], + 'SELECT e.id FROM Test\Entity e ORDER BY e.id ASC' + ], + 'OrderBy has no primary key field' => [ + ['e.name' => 'ASC'], + 'SELECT e.id FROM Test\Entity e ORDER BY e.name ASC, e.id ASC' + ], + 'OrderBy has no fields' => [ + [], + 'SELECT e.id FROM Test\Entity e ORDER BY e.id ASC' + ], + 'OrderBy has primary key field' => [ + ['e.name' => 'DESC', 'e.id' => 'DESC'], + 'SELECT e.id FROM Test\Entity e ORDER BY e.name DESC, e.id DESC' + ], + 'OrderBy has primary key field as first' => [ + ['e.id' => 'DESC', 'e.name' => 'ASC'], + 'SELECT e.id FROM Test\Entity e ORDER BY e.id DESC, e.name ASC' + ], + ]; + } +}