Skip to content

Commit 776b604

Browse files
committed
Merge remote-tracking branch 'remotes/dev/1.10' into 1.10
2 parents ad30034 + 958c548 commit 776b604

20 files changed

Lines changed: 419 additions & 829 deletions

File tree

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
<?php
2+
3+
namespace Oro\Bundle\DataGridBundle\Extension\Sorter;
4+
5+
use Doctrine\ORM\Query\Expr\From;
6+
use Doctrine\ORM\QueryBuilder;
7+
use Doctrine\ORM\Query\Expr\Select;
8+
9+
use Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration;
10+
use Oro\Bundle\DataGridBundle\Datasource\DatasourceInterface;
11+
use Oro\Bundle\DataGridBundle\Extension\AbstractExtension;
12+
use Oro\Bundle\DataGridBundle\Datasource\Orm\OrmDatasource;
13+
use Oro\Bundle\EntityBundle\ORM\DatabaseDriverInterface;
14+
use Oro\Bundle\EntityBundle\ORM\EntityClassResolver;
15+
16+
class PostgresqlGridModifier extends AbstractExtension
17+
{
18+
const PRIORITY = -261;
19+
20+
/** @var string */
21+
protected $databaseDriver;
22+
23+
/** @var EntityClassResolver */
24+
protected $entityClassResolver;
25+
26+
/**
27+
* @param string $databaseDriver
28+
* @param EntityClassResolver $entityClassResolver
29+
*/
30+
public function __construct($databaseDriver, EntityClassResolver $entityClassResolver)
31+
{
32+
$this->databaseDriver = $databaseDriver;
33+
$this->entityClassResolver = $entityClassResolver;
34+
}
35+
36+
/**
37+
* {@inheritdoc}
38+
*/
39+
public function isApplicable(DatagridConfiguration $config)
40+
{
41+
return $this->databaseDriver === DatabaseDriverInterface::DRIVER_POSTGRESQL;
42+
}
43+
44+
/**
45+
* {@inheritDoc}
46+
*/
47+
public function getPriority()
48+
{
49+
return self::PRIORITY;
50+
}
51+
52+
/**
53+
* Add sorting by identifier because postgresql return rows in different order on two the same sql, but
54+
* different LIMIT number
55+
*
56+
* @param DatagridConfiguration $config
57+
* @param DatasourceInterface $datasource
58+
* @return mixed|void
59+
*/
60+
public function visitDatasource(DatagridConfiguration $config, DatasourceInterface $datasource)
61+
{
62+
//getQueryBuilder exists only in datagrid orm datasource
63+
if (!$datasource instanceof OrmDatasource) {
64+
return;
65+
}
66+
67+
$entityClassName = $this->getEntityClassName($config);
68+
/** @var QueryBuilder $queryBuilder */
69+
$queryBuilder = $datasource->getQueryBuilder();
70+
71+
if (!$entityClassName) {
72+
return;
73+
}
74+
75+
$fromParts = $queryBuilder->getDQLPart('from');
76+
$alias = false;
77+
78+
$metadata = $queryBuilder->getEntityManager()->getClassMetadata($entityClassName);
79+
$identifier = $metadata->getSingleIdentifierFieldName();
80+
81+
/** @var From $fromPart */
82+
foreach ($fromParts as $fromPart) {
83+
if ($this->entityClassResolver->getEntityClass($fromPart->getFrom()) == $entityClassName) {
84+
$alias = $fromPart->getAlias();
85+
break;
86+
}
87+
}
88+
89+
if ($alias && $this->isAllowedAddingSorting($alias, $identifier, $queryBuilder)) {
90+
$field = $alias . '.' . $identifier;
91+
$orderBy = $queryBuilder->getDQLPart('orderBy');
92+
if (!isset($orderBy[$field])) {
93+
if ($this->isDistinct($queryBuilder)) {
94+
$this->ensureIdentifierSelected($queryBuilder, $field);
95+
}
96+
$queryBuilder->addOrderBy($field, 'ASC');
97+
}
98+
}
99+
}
100+
101+
/**
102+
* @param DatagridConfiguration $config
103+
*
104+
* @return null|string
105+
*/
106+
protected function getEntityClassName(DatagridConfiguration $config)
107+
{
108+
$entityClassName = $config->offsetGetByPath('[extended_entity_name]');
109+
if ($entityClassName) {
110+
return $entityClassName;
111+
}
112+
113+
$from = $config->offsetGetByPath('[source][query][from]');
114+
if (count($from) !== 0) {
115+
return $this->entityClassResolver->getEntityClass($from[0]['table']);
116+
}
117+
118+
return null;
119+
}
120+
121+
/**
122+
* @param string $alias
123+
* @param string $identifier
124+
* @param QueryBuilder $queryBuilder
125+
* @return bool
126+
*/
127+
protected function isAllowedAddingSorting($alias, $identifier, QueryBuilder $queryBuilder)
128+
{
129+
$groupByParts = $queryBuilder->getDQLPart('groupBy');
130+
131+
if (!count($groupByParts)) {
132+
return true;
133+
}
134+
135+
foreach ($groupByParts as $groupBy) {
136+
if (in_array($alias.'.'.$identifier, $groupBy->getParts(), true) !== false) {
137+
return true;
138+
}
139+
}
140+
141+
return false;
142+
}
143+
144+
/**
145+
* @param QueryBuilder $queryBuilder
146+
* @return bool
147+
*/
148+
protected function isDistinct(QueryBuilder $queryBuilder)
149+
{
150+
if ($queryBuilder->getDQLPart('distinct')) {
151+
return true;
152+
}
153+
154+
foreach ($queryBuilder->getDQLPart('select') as $select) {
155+
$selectString = ltrim(strtolower((string)$select));
156+
if (strpos($selectString, 'distinct ') === 0) {
157+
return true;
158+
}
159+
}
160+
161+
return false;
162+
}
163+
164+
/**
165+
* @param QueryBuilder $queryBuilder
166+
* @param string $field
167+
*/
168+
protected function ensureIdentifierSelected(QueryBuilder $queryBuilder, $field)
169+
{
170+
$isSelected = false;
171+
/** @var Select $select */
172+
foreach ($queryBuilder->getDQLPart('select') as $select) {
173+
$selectString = ltrim(strtolower((string)$select));
174+
if (strpos($selectString, 'distinct ') === 0) {
175+
$selectString = substr($selectString, 9);
176+
}
177+
// if field itself or field with alias
178+
if ($selectString === $field ||
179+
(
180+
strpos($selectString, $field) === 0 &&
181+
strpos(strtolower(ltrim(substr($selectString, strlen($field)))), 'as ') === 0
182+
)
183+
) {
184+
$isSelected = true;
185+
break;
186+
}
187+
}
188+
189+
if (!$isSelected) {
190+
$queryBuilder->addSelect($field);
191+
}
192+
}
193+
}

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

Lines changed: 0 additions & 88 deletions
This file was deleted.

src/Oro/Bundle/DataGridBundle/Resources/config/extensions.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ parameters:
1313
oro_datagrid.extension.totals.class: Oro\Bundle\DataGridBundle\Extension\Totals\OrmTotalsExtension
1414
oro_datagrid.extension.columns.class: Oro\Bundle\DataGridBundle\Extension\Columns\ColumnsExtension
1515
oro_datagrid.extension.mode.class: Oro\Bundle\DataGridBundle\Extension\Mode\ModeExtension
16+
oro_datagrid.extension.postgresql_grid_modifier.class: Oro\Bundle\DataGridBundle\Extension\Sorter\PostgresqlGridModifier
1617
oro_datagrid.extension.board.class: Oro\Bundle\DataGridBundle\Extension\Board\BoardExtension
1718
oro_datagrid.extension.appearance.class: Oro\Bundle\DataGridBundle\Extension\Appearance\AppearanceExtension
1819

@@ -135,10 +136,11 @@ services:
135136
tags:
136137
- { name: oro_datagrid.extension }
137138

138-
oro_datagrid.extension.precise_order_by:
139-
class: Oro\Bundle\DataGridBundle\Extension\Sorter\PreciseOrderByExtension
139+
oro_datagrid.extension.postgresql_grid_modifier:
140+
class: %oro_datagrid.extension.postgresql_grid_modifier.class%
140141
arguments:
141-
- '@oro_entity.query_hint_resolver'
142+
- %database_driver%
143+
- '@oro_entity.orm.entity_class_resolver'
142144
tags:
143145
- { name: oro_datagrid.extension }
144146

src/Oro/Bundle/DataGridBundle/Resources/doc/backend/datasources/orm.md

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,6 @@ datagrid:
2222
from:
2323
- { table: OroCRMContactBundle:Group, alias: g }
2424
```
25-
Important notes
26-
---------------
27-
28-
By default all datagrids that use ORM datasource is marked by [HINT_PRECISE_ORDER_BY](../../../../../../Component/DoctrineUtils/README.md#preciseorderbywalker-class) query hint. This guarantee that rows are sorted in the same way independent from a state of SQL server and from values of OFFSET and LIMIT clauses. More details you can find in [PostgreSQL documentation](https://www.postgresql.org/docs/8.1/static/queries-limit.html).
29-
30-
If you need to disable this behaviour for your datagrid the following configuration can be used:
31-
32-
```yaml
33-
34-
datagrids:
35-
DATAGRID_NAME_HERE:
36-
source:
37-
type: orm
38-
query:
39-
...
40-
hints:
41-
- { name: HINT_PRECISE_ORDER_BY, value: false }
42-
```
4325
4426
Query hints
4527
-----------

0 commit comments

Comments
 (0)