Skip to content

Commit 9a23f73

Browse files
ikrynychanskyiyurio
authored andcommitted
BAP-14290: PostgresqlGridModifier add duplicate Order by identifier to final query (#9530)
- Added fixes to prevent fields duplication in OrderBy query part
1 parent 8784f61 commit 9a23f73

File tree

2 files changed

+152
-2
lines changed

2 files changed

+152
-2
lines changed

src/Oro/Bundle/DataGridBundle/Extension/Sorter/PostgresqlGridModifier.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public function visitDatasource(DatagridConfiguration $config, DatasourceInterfa
8989
if ($alias && $this->isAllowedAddingSorting($alias, $identifier, $queryBuilder)) {
9090
$field = $alias . '.' . $identifier;
9191
$orderBy = $queryBuilder->getDQLPart('orderBy');
92-
if (!isset($orderBy[$field])) {
92+
if (!$this->hasOrderByField($orderBy, $field)) {
9393
if ($this->isDistinct($queryBuilder)) {
9494
$this->ensureIdentifierSelected($queryBuilder, $field);
9595
}
@@ -98,6 +98,23 @@ public function visitDatasource(DatagridConfiguration $config, DatasourceInterfa
9898
}
9999
}
100100

101+
/**
102+
* Check field already exists in orders part
103+
*
104+
* @param array $orderBy
105+
* @param string $field
106+
* @return bool
107+
*/
108+
protected function hasOrderByField($orderBy, $field)
109+
{
110+
foreach ($orderBy as $order) {
111+
if (preg_match(sprintf('/(^|\s)%s\s/i', $field), $order)) {
112+
return true;
113+
}
114+
}
115+
return false;
116+
}
117+
101118
/**
102119
* @param DatagridConfiguration $config
103120
*
@@ -127,7 +144,7 @@ protected function isAllowedAddingSorting($alias, $identifier, QueryBuilder $que
127144

128145
$selectPartsWithAggregation = array_filter($selectParts, function ($selectPart) use ($forbiddenFunctions) {
129146
foreach ($forbiddenFunctions as $functionName) {
130-
if (false !== stripos($selectPart, $functionName)) {
147+
if (false !== stripos($selectPart, $functionName . '(')) {
131148
return true;
132149
}
133150
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
namespace Oro\Bundle\DataGridBundle\Tests\Unit\Extension\Sorter;
4+
5+
use Doctrine\ORM\EntityManager;
6+
use Doctrine\ORM\Mapping\ClassMetadata;
7+
use Doctrine\ORM\QueryBuilder;
8+
use Doctrine\ORM\Query\Expr\OrderBy;
9+
10+
use Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration;
11+
use Oro\Bundle\DataGridBundle\Datasource\Orm\OrmDatasource;
12+
use Oro\Bundle\DataGridBundle\Extension\Sorter\PostgresqlGridModifier;
13+
use Oro\Bundle\EntityBundle\ORM\EntityClassResolver;
14+
15+
class PostgresqlGridModifierTest extends \PHPUnit_Framework_TestCase
16+
{
17+
/**
18+
* @var PostgresqlGridModifier
19+
*/
20+
protected $extension;
21+
22+
protected $entityClassResolver;
23+
24+
public function setUp()
25+
{
26+
$this->entityClassResolver = $this->getMockBuilder(EntityClassResolver::class)
27+
->disableOriginalConstructor()
28+
->getMock();
29+
$this->extension = new PostgresqlGridModifier('pdo_pgsql', $this->entityClassResolver);
30+
}
31+
32+
/**
33+
* @dataProvider visitDatasourceDataProvider
34+
*/
35+
public function testVisitDatasource($orderBy, $expected)
36+
{
37+
$em = $this->getMockBuilder(EntityManager::class)
38+
->disableOriginalConstructor()
39+
->getMock();
40+
41+
$metadata = $this->getMockBuilder(ClassMetadata::class)
42+
->disableOriginalConstructor()
43+
->getMock();
44+
45+
$em->expects(self::once())
46+
->method('getClassMetadata')
47+
->with('Test\Entity')
48+
->willReturn($metadata);
49+
50+
$metadata->expects(self::once())
51+
->method('getSingleIdentifierFieldName')
52+
->willReturn('id');
53+
54+
$qb = new QueryBuilder($em);
55+
$qb->select('e.id')->from('Test\Entity', 'e');
56+
57+
if (!empty($orderBy)) {
58+
$orderByExpr = new OrderBy();
59+
foreach ($orderBy as $field => $des) {
60+
$orderByExpr->add($field, $des);
61+
}
62+
$qb->addOrderBy($orderByExpr);
63+
}
64+
65+
$datasource = $this->getMockBuilder(OrmDatasource::class)
66+
->disableOriginalConstructor()
67+
->getMock();
68+
69+
$datasource->expects(self::once())
70+
->method('getQueryBuilder')
71+
->willReturn($qb);
72+
73+
$this->entityClassResolver->expects($this->any())
74+
->method('getEntityClass')
75+
->willReturn('Test\Entity');
76+
77+
$dataGridConfig = DatagridConfiguration::create([
78+
'sorters' => [
79+
'columns' => []
80+
],
81+
'source' => [
82+
'type' => 'orm',
83+
'query' => [
84+
'from' => [
85+
[
86+
'table' => 'Test\Entity',
87+
'alias' => 'e'
88+
]
89+
]
90+
]
91+
]
92+
]);
93+
94+
$this->extension->visitDatasource(
95+
$dataGridConfig,
96+
$datasource
97+
);
98+
99+
self::assertEquals(
100+
$expected,
101+
$qb->getDQL()
102+
);
103+
}
104+
105+
/**
106+
* @return array
107+
*/
108+
public function visitDatasourceDataProvider()
109+
{
110+
return [
111+
'OrderBy has only primary key field' => [
112+
['e.id' => 'ASC'],
113+
'SELECT e.id FROM Test\Entity e ORDER BY e.id ASC'
114+
],
115+
'OrderBy has no primary key field' => [
116+
['e.name' => 'ASC'],
117+
'SELECT e.id FROM Test\Entity e ORDER BY e.name ASC, e.id ASC'
118+
],
119+
'OrderBy has no fields' => [
120+
[],
121+
'SELECT e.id FROM Test\Entity e ORDER BY e.id ASC'
122+
],
123+
'OrderBy has primary key field' => [
124+
['e.name' => 'DESC', 'e.id' => 'DESC'],
125+
'SELECT e.id FROM Test\Entity e ORDER BY e.name DESC, e.id DESC'
126+
],
127+
'OrderBy has primary key field as first' => [
128+
['e.id' => 'DESC', 'e.name' => 'ASC'],
129+
'SELECT e.id FROM Test\Entity e ORDER BY e.id DESC, e.name ASC'
130+
],
131+
];
132+
}
133+
}

0 commit comments

Comments
 (0)