Skip to content

Commit

Permalink
IBX-7653: Allow filtering for loadSubtreeAction (#1161)
Browse files Browse the repository at this point in the history
  • Loading branch information
tischsoic authored May 16, 2024
1 parent 0d22dfd commit ca438ee
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 25 deletions.
5 changes: 5 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -8210,6 +8210,11 @@ parameters:
count: 1
path: src/lib/Notification/TranslatableNotificationHandler.php

-
message: "#^Cannot access offset mixed on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\>\\.$#"
count: 1
path: src/lib/Pagination/Mapper/AbstractPagerContentToDataMapper.php

-
message: "#^Method Ibexa\\\\AdminUi\\\\Pagination\\\\Mapper\\\\AbstractPagerContentToDataMapper\\:\\:setTranslatedContentTypesNames\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#"
count: 1
Expand Down
3 changes: 2 additions & 1 deletion src/bundle/Controller/Content/ContentTreeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ public function loadSubtreeAction(Request $request): Root
true,
0,
$sortClause,
$sortOrder
$sortOrder,
$loadSubtreeRequest->filter,
);
}

Expand Down
33 changes: 29 additions & 4 deletions src/lib/REST/Input/Parser/ContentTree/LoadSubtreeRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
namespace Ibexa\AdminUi\REST\Input\Parser\ContentTree;

use Ibexa\AdminUi\REST\Value\ContentTree\LoadSubtreeRequest as LoadSubtreeRequestValue;
use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion;
use Ibexa\Contracts\Rest\Exceptions;
use Ibexa\Contracts\Rest\Input\ParsingDispatcher;
use Ibexa\Rest\Input\BaseParser;
use Ibexa\Rest\Server\Input\Parser\Criterion as CriterionParser;

class LoadSubtreeRequest extends BaseParser
class LoadSubtreeRequest extends CriterionParser
{
/**
* {@inheritdoc}
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException
*/
public function parse(array $data, ParsingDispatcher $parsingDispatcher): LoadSubtreeRequestValue
{
Expand All @@ -31,7 +32,31 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher): LoadSu
$nodes[] = $parsingDispatcher->parse($node, $node['_media-type']);
}

return new LoadSubtreeRequestValue($nodes);
$filter = null;
if (array_key_exists('Filter', $data) && is_array($data['Filter'])) {
$filter = $this->processCriteriaArray($data['Filter'], $parsingDispatcher);
}

return new LoadSubtreeRequestValue($nodes, $filter);
}

/**
* @param array<string, mixed> $criteriaArray
*
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException
*/
private function processCriteriaArray(array $criteriaArray, ParsingDispatcher $parsingDispatcher): ?Criterion
{
if (count($criteriaArray) === 0) {
return null;
}

$criteria = [];
foreach ($criteriaArray as $criterionName => $criterionData) {
$criteria[] = $this->dispatchCriterion($criterionName, $criterionData, $parsingDispatcher);
}

return (count($criteria) === 1) ? $criteria[0] : new Criterion\LogicalAnd($criteria);
}
}

Expand Down
8 changes: 6 additions & 2 deletions src/lib/REST/Value/ContentTree/LoadSubtreeRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,23 @@

namespace Ibexa\AdminUi\REST\Value\ContentTree;

use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion;
use Ibexa\Rest\Value as RestValue;

class LoadSubtreeRequest extends RestValue
{
/** @var \Ibexa\AdminUi\REST\Value\ContentTree\LoadSubtreeRequestNode[] */
public $nodes;
public array $nodes;

public ?Criterion $filter;

/**
* @param array $nodes
*/
public function __construct(array $nodes = [])
public function __construct(array $nodes = [], ?Criterion $filter = null)
{
$this->nodes = $nodes;
$this->filter = $filter;
}
}

Expand Down
59 changes: 41 additions & 18 deletions src/lib/UI/Module/ContentTree/NodeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
final class NodeFactory
{
private const TOP_NODE_CONTENT_ID = 0;

/**
* @var array<string, class-string<\Ibexa\Contracts\Core\Repository\Values\Filter\FilteringSortClause>>
*/
private const SORT_CLAUSE_MAP = [
'DatePublished' => SortClause\DatePublished::class,
'ContentName' => SortClause\ContentName::class,
Expand Down Expand Up @@ -97,7 +101,8 @@ public function createNode(
bool $loadChildren = false,
int $depth = 0,
?string $sortClause = null,
string $sortOrder = Query::SORT_ASC
string $sortOrder = Query::SORT_ASC,
?Criterion $requestFilter = null
): Node {
$uninitializedContentInfoList = [];
$containerLocations = [];
Expand All @@ -114,17 +119,18 @@ public function createNode(
$depth,
$sortClause,
$sortOrder,
$bookmarkedLocations
$bookmarkedLocations,
$requestFilter
);
$versionInfoById = $this->contentService->loadVersionInfoListByContentInfo($uninitializedContentInfoList);

$aggregatedChildrenCount = null;
if ($this->searchService->supports(SearchService::CAPABILITY_AGGREGATIONS)) {
$aggregatedChildrenCount = $this->countAggregatedSubitems($containerLocations);
$aggregatedChildrenCount = $this->countAggregatedSubitems($containerLocations, $requestFilter);
}

$this->supplyTranslatedContentName($node, $versionInfoById);
$this->supplyChildrenCount($node, $aggregatedChildrenCount);
$this->supplyChildrenCount($node, $aggregatedChildrenCount, $requestFilter);

return $node;
}
Expand All @@ -149,9 +155,10 @@ private function findSubitems(
int $limit = 10,
int $offset = 0,
?string $sortClause = null,
string $sortOrder = Query::SORT_ASC
string $sortOrder = Query::SORT_ASC,
?Criterion $requestFilter = null
): SearchResult {
$searchQuery = $this->getSearchQuery($parentLocation->id);
$searchQuery = $this->getSearchQuery($parentLocation->id, $requestFilter);

$searchQuery->limit = $limit;
$searchQuery->offset = $offset;
Expand All @@ -163,7 +170,7 @@ private function findSubitems(
/**
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $parentLocation
*/
private function getSearchQuery(int $parentLocationId): LocationQuery
private function getSearchQuery(int $parentLocationId, ?Criterion $requestFilter = null): LocationQuery
{
$searchQuery = new LocationQuery();
$searchQuery->filter = new Criterion\ParentLocationId($parentLocationId);
Expand All @@ -184,6 +191,10 @@ private function getSearchQuery(int $parentLocationId): LocationQuery
$searchQuery->filter = new Criterion\LogicalAnd([$searchQuery->filter, $contentTypeCriterion]);
}

if (null !== $requestFilter) {
$searchQuery->filter = new Criterion\LogicalAnd([$searchQuery->filter, $requestFilter]);
}

return $searchQuery;
}

Expand All @@ -201,9 +212,9 @@ private function findChild(int $locationId, LoadSubtreeRequestNode $loadSubtreeR
/**
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
*/
private function countSubitems(int $parentLocationId): int
private function countSubitems(int $parentLocationId, ?Criterion $requestFilter = null): int
{
$searchQuery = $this->getSearchQuery($parentLocationId);
$searchQuery = $this->getSearchQuery($parentLocationId, $requestFilter);

$searchQuery->limit = 0;
$searchQuery->offset = 0;
Expand All @@ -214,8 +225,11 @@ private function countSubitems(int $parentLocationId): int

/**
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Location[] $containerLocations
*
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException
*/
private function countAggregatedSubitems(array $containerLocations): array
private function countAggregatedSubitems(array $containerLocations, ?Criterion $requestFilter): array
{
if (empty($containerLocations)) {
return [];
Expand All @@ -226,7 +240,7 @@ private function countAggregatedSubitems(array $containerLocations): array

$result = [];
foreach ($containerLocationsChunks as $containerLocationsChunk) {
$result = array_replace($result, $this->countAggregatedSubitems($containerLocationsChunk));
$result = array_replace($result, $this->countAggregatedSubitems($containerLocationsChunk, $requestFilter));
}

return $result;
Expand All @@ -240,6 +254,10 @@ private function countAggregatedSubitems(array $containerLocations): array
$locationChildrenTermAggregation->setLimit(\count($parentLocationIds));
$searchQuery->aggregations[] = $locationChildrenTermAggregation;

if (null !== $requestFilter) {
$searchQuery->filter = new Criterion\LogicalAnd([$searchQuery->filter, $requestFilter]);
}

$result = $this->searchService->findLocations($searchQuery);

if ($result->aggregations->has('childrens')) {
Expand Down Expand Up @@ -325,7 +343,8 @@ private function buildNode(
int $depth = 0,
?string $sortClause = null,
string $sortOrder = Query::SORT_ASC,
array $bookmarkLocations = []
array $bookmarkLocations = [],
?Criterion $requestFilter = null
): Node {
$contentInfo = $location->getContentInfo();
$contentId = $location->contentId;
Expand Down Expand Up @@ -353,7 +372,7 @@ private function buildNode(
$totalChildrenCount = 0;
$children = [];
if ($loadChildren && $depth < $this->getSetting('tree_max_depth')) {
$searchResult = $this->findSubitems($location, $limit, $offset, $sortClause, $sortOrder);
$searchResult = $this->findSubitems($location, $limit, $offset, $sortClause, $sortOrder, $requestFilter);
$totalChildrenCount = (int) $searchResult->totalCount;

/** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $childLocation */
Expand All @@ -371,7 +390,8 @@ private function buildNode(
$depth + 1,
null,
Query::SORT_ASC,
$bookmarkLocations
$bookmarkLocations,
$requestFilter
);
}
}
Expand Down Expand Up @@ -429,20 +449,23 @@ private function supplyTranslatedContentName(Node $node, array $versionInfoById)
/**
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
*/
private function supplyChildrenCount(Node $node, ?array $aggregationResult = null): void
{
private function supplyChildrenCount(
Node $node,
?array $aggregationResult = null,
?Criterion $requestFilter = null
): void {
if ($node->isContainer) {
if ($aggregationResult !== null) {
$totalCount = $aggregationResult[$node->locationId] ?? 0;
} else {
$totalCount = $this->countSubitems($node->locationId);
$totalCount = $this->countSubitems($node->locationId, $requestFilter);
}

$node->totalChildrenCount = $totalCount;
}

foreach ($node->children as $child) {
$this->supplyChildrenCount($child, $aggregationResult);
$this->supplyChildrenCount($child, $aggregationResult, $requestFilter);
}
}

Expand Down

0 comments on commit ca438ee

Please sign in to comment.