Skip to content

Commit 1d1a08d

Browse files
committed
Solution 4: Join once
1 parent ebcef38 commit 1d1a08d

File tree

3 files changed

+107
-13
lines changed

3 files changed

+107
-13
lines changed

eZ/Publish/API/Repository/Tests/Values/User/Limitation/ObjectStateLimitationTest.php

+55
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
1313
use eZ\Publish\API\Repository\Values\Content\Query;
1414
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
15+
use eZ\Publish\API\Repository\Values\Filter\Filter;
1516
use eZ\Publish\API\Repository\Values\ObjectState\ObjectState;
1617
use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup;
1718
use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation;
@@ -198,6 +199,60 @@ public function testObjectStateLimitationSearch(): void
198199
self::assertEquals($searchResultsBefore->totalCount - 1, $searchResultsAfter->totalCount);
199200
}
200201

202+
/**
203+
* Checks if the Filtering API results are correctly filtered when using ObjectStateLimitation
204+
* with limitation values from two different StateGroups.
205+
*
206+
* @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
207+
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
208+
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
209+
*/
210+
public function testObjectStateLimitationFilter(): void
211+
{
212+
$repository = $this->getRepository();
213+
$permissionResolver = $repository->getPermissionResolver();
214+
215+
$objectStateGroup = $this->createObjectStateGroup();
216+
$objectState = $this->createObjectState($objectStateGroup);
217+
218+
$user = $this->createUserWithObjectStateLimitationOnContentRead(
219+
[
220+
self::OBJECT_STATE_NOT_LOCKED_STATE_ID,
221+
$objectState->id,
222+
]
223+
);
224+
$adminUser = $permissionResolver->getCurrentUserReference();
225+
226+
$wikiPage = $this->createWikiPage();
227+
228+
$this->loginAsUser($user);
229+
230+
$query = new Filter();
231+
$query->withLimit(50);
232+
$query->withCriterion(new Criterion\MatchAll());
233+
234+
$searchResultsBefore = $repository->getContentService()->find($query);
235+
236+
$this->loginAsUser($adminUser);
237+
238+
//change the Object State to the one that doesn't match the Limitation
239+
$stateService = $repository->getObjectStateService();
240+
$stateService->setContentState(
241+
$wikiPage->contentInfo,
242+
$stateService->loadObjectStateGroup(2),
243+
$stateService->loadObjectState(2)
244+
);
245+
246+
$this->loginAsUser($user);
247+
248+
$searchResultsAfter = $repository->getContentService()->find($query);
249+
250+
self::assertEquals(
251+
$searchResultsBefore->getTotalCount() - 1,
252+
$searchResultsAfter->getTotalCount()
253+
);
254+
}
255+
201256
/**
202257
* @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
203258
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException

eZ/Publish/Core/Limitation/ObjectStateLimitationType.php

+42-1
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,53 @@ public function getCriterion(APILimitationValue $value, APIUserReference $curren
213213
throw new RuntimeException('$value->limitationValues is empty');
214214
}
215215

216-
if (!isset($value->limitationValues[1])) {
216+
if (count($value->limitationValues) === 1) {
217217
// 1 limitation value: EQ operation
218218
return new Criterion\ObjectStateId($value->limitationValues[0]);
219219
}
220220

221221
return new Criterion\ObjectStateId($value->limitationValues);
222+
223+
$groupedLimitationValues = $this->groupLimitationValues($value->limitationValues);
224+
225+
if (count($groupedLimitationValues) === 1) {
226+
// one group, several limitation values: IN operation
227+
return new Criterion\ObjectStateId($groupedLimitationValues[0]);
228+
}
229+
230+
// limitations from different groups require logical AND between them
231+
$criterions = [];
232+
foreach ($groupedLimitationValues as $limitationGroup) {
233+
$criterions[] = new Criterion\ObjectStateId($limitationGroup);
234+
}
235+
236+
return new Criterion\LogicalAnd($criterions);
237+
}
238+
239+
/**
240+
* Groups limitation values by the State Group.
241+
*
242+
* @param string[] $limitationValues
243+
*
244+
* @return int[][]
245+
*/
246+
private function groupLimitationValues(array $limitationValues)
247+
{
248+
$objectStateHandler = $this->persistence->objectStateHandler();
249+
$stateGroups = $objectStateHandler->loadAllGroups();
250+
$groupedLimitationValues = [];
251+
foreach ($stateGroups as $stateGroup) {
252+
$states = $objectStateHandler->loadObjectStates($stateGroup->id);
253+
$stateIds = array_map(static function ($state) {
254+
return $state->id;
255+
}, $states);
256+
$limitationValuesGroup = array_intersect($stateIds, $limitationValues);
257+
if (!empty($limitationValuesGroup)) {
258+
$groupedLimitationValues[] = array_values($limitationValuesGroup);
259+
}
260+
}
261+
262+
return $groupedLimitationValues;
222263
}
223264

224265
/**

eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ObjectStateIdQueryBuilder.php

+10-12
Original file line numberDiff line numberDiff line change
@@ -25,35 +25,33 @@ public function accepts(FilteringCriterion $criterion): bool
2525
return $criterion instanceof ObjectStateId;
2626
}
2727

28-
29-
/**
30-
* @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ObjectStateId $criterion
31-
*/
3228
public function buildQueryConstraint(
3329
FilteringQueryBuilder $queryBuilder,
3430
FilteringCriterion $criterion
3531
): ?string {
3632
$value = (array)$criterion->value;
33+
$tableAlias = uniqid('osl_');
3734

35+
/** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ObjectStateId $criterion */
3836
$queryBuilder
39-
->joinOnce(
37+
->leftJoinOnce(
4038
'content',
4139
Gateway::OBJECT_STATE_LINK_TABLE,
42-
'osl',
40+
$tableAlias,
4341
(string) $queryBuilder->expr()->and(
44-
$queryBuilder->expr()->eq('content.id', 'osl.contentobject_id'),
42+
$queryBuilder->expr()->eq(
43+
'content.id',
44+
$tableAlias . '.contentobject_id'
45+
),
4546
$queryBuilder->expr()->in(
46-
'osl.contentobject_state_id',
47+
$tableAlias . '.contentobject_state_id',
4748
$queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY)
4849
)
4950
)
5051
);
5152

52-
53-
54-
5553
return $queryBuilder->expr()->isNotNull(
56-
'osl.contentobject_state_id',
54+
$tableAlias . '.contentobject_state_id',
5755
);
5856
}
5957
}

0 commit comments

Comments
 (0)