Skip to content

NGSTACK-906 eztags priority sorting #170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 45 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
c843d14
NGSTACK-906 add 'priority' property to Tag objects
AntePrkacin Jun 18, 2025
1c5b716
NGSTACK-906 add support for 'priority' property in the Mapper layer
AntePrkacin Jun 18, 2025
2d27fc0
NGSTACK-906 modify createTagFindQuery method to select 'priority' pro…
AntePrkacin Jun 18, 2025
a8cf605
NGSTACK-906 add 'priority' column to 'eztags' table in MySQL and Post…
AntePrkacin Jun 18, 2025
3a84111
NGSTACK-906 add translations for tag priority
AntePrkacin Jun 18, 2025
8e535ea
NGSTACK-906 show 'priority' column for tag's children
AntePrkacin Jun 18, 2025
7d59685
NGSTACK-906 add 'subitems' tab to the show tag template
AntePrkacin Jun 18, 2025
fe7ba81
NGSTACK-906 add 'subitems' tab template with dropdowns for sortBy and…
AntePrkacin Jun 18, 2025
9158a5d
NGSTACK-906 add translations for tag subitems sorting
AntePrkacin Jun 18, 2025
9ce9427
NGSTACK-906 update tag children sorting functionality in TagControlle…
AntePrkacin Jun 20, 2025
60cf2cf
NGSTACK-906 add support for sorting in loadTagChildren() method in Se…
AntePrkacin Jun 20, 2025
1041265
NGSTACK-906 add support for sorting in loadChildren() method for Tag …
AntePrkacin Jun 20, 2025
0955f35
NGSTACK-906 add support for sorting in getChildren() method for Gatew…
AntePrkacin Jun 20, 2025
43def26
NGSTACK-906 update subitems sort option value from 'tag_id' to 'id' i…
AntePrkacin Jun 20, 2025
f0aa890
NGSTACK-906 remove 'subitems' tag tab and add the tag sorting at the …
AntePrkacin Jun 20, 2025
f1e4e1a
NGSTACK-906 update tests to handle 'priority' property
AntePrkacin Jun 20, 2025
6139978
NGSTACK-906 add enums for tag sortField and sortOrder properties
AntePrkacin Jun 24, 2025
df8003b
NGSTACK-906 add sortField and sortOrder properties to Tag objects and…
AntePrkacin Jun 24, 2025
ef0d60a
NGSTACK-906 update Mappers with sortField and sortOrder fields
AntePrkacin Jun 24, 2025
be00504
NGSTACK-906 remove sortBy and sortOrder parameters from the getChildr…
AntePrkacin Jun 24, 2025
d88c066
NGSTACK-906 remove sortBy and sortOrder parameters from the loadChild…
AntePrkacin Jun 24, 2025
c0bf0f4
NGSTACK-906 remove sortBy and sortOrder parameters from the loadTagCh…
AntePrkacin Jun 24, 2025
c719cc4
NGSTACK-906 rename subitems template to sort_children_tags template a…
AntePrkacin Jun 24, 2025
93c1134
NGSTACK-906 add sortField and sortOrder properties to UpdateStruct an…
AntePrkacin Jun 24, 2025
41e6787
NGSTACK-906 remove sorting logic in ChildrenTagsAdapter
AntePrkacin Jun 24, 2025
705fc35
NGSTACK-906 add new route and controller method for updating sortFiel…
AntePrkacin Jun 24, 2025
2397fcc
NGSTACK-906 rename sortField property to sortBy property everywhere
AntePrkacin Jun 24, 2025
c6c4555
NGSTACK-906 add logic for sorting children by sortBy and sortOrder in…
AntePrkacin Jun 24, 2025
4a5391d
NGSTACK-906 fix tests regarding sortBy and sortOrder properties
AntePrkacin Jun 24, 2025
4224d33
NGSTACK-906 update TagSortBy and TagSortOrder enums, iterate through …
AntePrkacin Jun 24, 2025
e16f73f
NGSTACK-906 throw BadRequestHttpException if sortBy or sortOrder para…
AntePrkacin Jun 24, 2025
dba4689
NGSTACK-906 change sort_by and sort_order column types from enums to …
AntePrkacin Jun 25, 2025
a6100ae
NGSTACK-906 add config params for sorting all child tags and for sort…
AntePrkacin Jun 25, 2025
5733962
NGSTACK-906 update Mapper and DoctrineDatabase Gateway with logic for…
AntePrkacin Jun 25, 2025
5479257
NGSTACK-906 add support for 'sort' config params in tests
AntePrkacin Jun 25, 2025
148a3c3
NGSTACK-906 add ability to modify 'priority' property when editing a …
AntePrkacin Jun 25, 2025
3704566
NGSTACK-906 fix the update() method in DoctrineDatabase and the test …
AntePrkacin Jun 25, 2025
59beb45
NGSTACK-906 update 'sortBy' and 'sortOrder' properties to be null on …
AntePrkacin Jun 25, 2025
84d9758
NGSTACK-906 update getChildren() and update() methods in DoctrineData…
AntePrkacin Jun 25, 2025
fd507dd
NGSTACK-906 update Mapper to properly handle the case when sortBy or …
AntePrkacin Jun 25, 2025
a81d3f6
NGSTACK-906 update sort_children_tags template to check if sortBy or …
AntePrkacin Jun 25, 2025
0ce8c25
NGSTACK-906 add MySQL and PostgreSQL upgrade scripts for 'priority', …
AntePrkacin Jun 26, 2025
3e9628a
NGSTACK-906 don't show 'priority' info on synonyms and remove 'priori…
AntePrkacin Jun 26, 2025
c6d1f47
NGSTACK-906 add 'priority', 'sort_by' and 'sort_order' fields to lega…
AntePrkacin Jun 26, 2025
031c1aa
NGSTACK-906 add policy for sorting children tags
AntePrkacin Jun 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions bundle/API/Repository/Values/Enums/TagSortBy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Netgen\TagsBundle\API\Repository\Values\Enums;

enum TagSortBy: string
{
case Id = 'id';
case Keyword = 'keyword';
case Modified = 'modified';
case Priority = 'priority';
}
11 changes: 11 additions & 0 deletions bundle/API/Repository/Values/Enums/TagSortOrder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Netgen\TagsBundle\API\Repository\Values\Enums;

enum TagSortOrder: string
{
case Ascending = 'asc';
case Descending = 'desc';
}
22 changes: 22 additions & 0 deletions bundle/API/Repository/Values/Tags/Tag.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use DateTimeInterface;
use Ibexa\Contracts\Core\Repository\Values\ValueObject;
use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy;
use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder;

use function array_map;
use function count;
Expand All @@ -28,6 +30,9 @@
* @property-read bool $alwaysAvailable Indicates if the Tag object is shown in the main language if it is not present in an other requested language
* @property-read string $mainLanguageCode The main language code of the Tag object
* @property-read string[] $languageCodes List of languages in this Tag object
* @property-read int $priority Tag priority
* @property-read TagSortBy $sortBy Specifies by which property the child tags should be sorted on
* @property-read TagSortOrder $sortOrder Specifies whether the sort order should be ascending or descending
*/
final class Tag extends ValueObject
{
Expand Down Expand Up @@ -104,6 +109,23 @@ final class Tag extends ValueObject
*/
protected ?string $prioritizedLanguageCode;

/**
* Tag priority.
*
* Position of the Tag among its siblings when sorted by priority.
*/
protected int $priority;

/**
* Specifies by which property the child tags should be sorted on.
*/
protected ?TagSortBy $sortBy;

/**
* Specifies whether the sort order should be ascending or descending.
*/
protected ?TagSortOrder $sortOrder;

/**
* Construct object optionally with a set of properties.
*
Expand Down
19 changes: 19 additions & 0 deletions bundle/API/Repository/Values/Tags/TagStruct.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace Netgen\TagsBundle\API\Repository\Values\Tags;

use Ibexa\Contracts\Core\Repository\Values\ValueObject;
use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy;
use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder;

use function array_key_exists;

Expand All @@ -22,6 +24,23 @@ abstract class TagStruct extends ValueObject
*/
public ?string $remoteId = null;

/**
* Specifies by which property the child tags should be sorted on.
*/
public ?TagSortBy $sortBy;

/**
* Specifies whether the sort order should be ascending or descending.
*/
public ?TagSortOrder $sortOrder;

/**
* Tag priority.
*
* Position of the Tag among its siblings when sorted by priority.
*/
public ?int $priority;

/**
* Tag keywords in the target languages
* Eg. array( "cro-HR" => "Hrvatska", "eng-GB" => "Croatia" ).
Expand Down
28 changes: 27 additions & 1 deletion bundle/Controller/Admin/TagController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
use Ibexa\Contracts\Core\Repository\ContentTypeService;
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
use Netgen\TagsBundle\API\Repository\TagsService;
use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy;
use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder;
use Netgen\TagsBundle\API\Repository\Values\Tags\Tag;
use Netgen\TagsBundle\API\Repository\Values\Tags\TagUpdateStruct;
use Netgen\TagsBundle\Core\Pagination\Pagerfanta\SearchTagsAdapter;
use Netgen\TagsBundle\Form\Type\CopyTagsType;
use Netgen\TagsBundle\Form\Type\LanguageSelectType;
Expand All @@ -19,6 +22,7 @@
use Pagerfanta\Adapter\AdapterInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

use function count;
use function in_array;
Expand All @@ -44,7 +48,6 @@ public function showTagAction(Request $request, ?Tag $tag = null): Response

if (!$tag instanceof Tag || !$tag->isSynonym()) {
$configResolver = $this->getConfigResolver();

$currentPage = (int) $request->query->get('page');
$pager = $this->createPager(
$this->tagChildrenAdapter,
Expand Down Expand Up @@ -230,6 +233,7 @@ public function updateTagAction(Request $request, Tag $tag, string $languageCode
$tagUpdateStruct = $this->tagsService->newTagUpdateStruct();
$tagUpdateStruct->remoteId = $tag->remoteId;
$tagUpdateStruct->alwaysAvailable = $tag->alwaysAvailable;
$tagUpdateStruct->priority = $tag->priority;

foreach ($tag->keywords as $keywordLanguageCode => $keyword) {
$tagUpdateStruct->setKeyword($keyword ?? '', $keywordLanguageCode);
Expand Down Expand Up @@ -264,6 +268,28 @@ public function updateTagAction(Request $request, Tag $tag, string $languageCode
);
}

public function updateTagSortAction(Request $request, Tag $tag): Response
{
if (!$this->isCsrfTokenValid('netgen_tags_admin', (string) ($request->request->get('_csrf_token') ?? ''))) {
$this->addFlashMessage('errors', 'invalid_csrf_token');

return $this->redirectToTag($tag);
}

$sortBy = $request->request->get('sort_by');
$sortOrder = $request->request->get('sort_order');

$tagUpdateStruct = new TagUpdateStruct();
$tagUpdateStruct->sortBy = TagSortBy::tryFrom((string) $sortBy)
?? throw new BadRequestHttpException('Invalid enum value for sortBy when trying to update children sorting');
$tagUpdateStruct->sortOrder = TagSortOrder::tryFrom((string) $sortOrder)
?? throw new BadRequestHttpException('Invalid enum value for sortOrder when trying to update children sorting');

$this->tagsService->updateTag($tag, $tagUpdateStruct);

return $this->redirectToTag($tag);
}

/**
* This method is called for delete tag or synonym action.
* It shows a confirmation view.
Expand Down
9 changes: 6 additions & 3 deletions bundle/Core/Pagination/Pagerfanta/ChildrenTagsAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
final class ChildrenTagsAdapter implements AdapterInterface, TagAdapterInterface
{
private ?Tag $tag = null;

private int $nbResults;

public function __construct(private TagsService $tagsService) {}
public function __construct(private readonly TagsService $tagsService) {}

public function setTag(Tag $tag): void
{
Expand All @@ -30,7 +29,11 @@ public function getNbResults(): int

public function getSlice($offset, $length): iterable
{
$childrenTags = $this->tagsService->loadTagChildren($this->tag, $offset, $length);
$childrenTags = $this->tagsService->loadTagChildren(
$this->tag,
$offset,
$length,
);

$this->nbResults = $this->nbResults ?? $this->tagsService->getTagChildrenCount($this->tag);

Expand Down
57 changes: 46 additions & 11 deletions bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
use Doctrine\DBAL\Query\QueryBuilder;
use Doctrine\DBAL\Types\Types;
use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler;
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use Ibexa\Core\Base\Exceptions\NotFoundException;
use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator;
use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy;
use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder;
use Netgen\TagsBundle\Core\Persistence\Legacy\Tags\Gateway;
use Netgen\TagsBundle\SPI\Persistence\Tags\CreateStruct;
use Netgen\TagsBundle\SPI\Persistence\Tags\SynonymCreateStruct;
Expand All @@ -35,9 +38,10 @@
final class DoctrineDatabase extends Gateway
{
public function __construct(
private Connection $connection,
private LanguageHandler $languageHandler,
private LanguageMaskGenerator $languageMaskGenerator,
private readonly Connection $connection,
private readonly LanguageHandler $languageHandler,
private readonly LanguageMaskGenerator $languageMaskGenerator,
private readonly ConfigResolverInterface $configResolver,
) {}

public function getBasicTagData(int $tagId): array
Expand Down Expand Up @@ -128,6 +132,21 @@ public function getFullTagDataByKeywordAndParentId(string $keyword, int $parentI

public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array
{
if ($tagId === 0) {
$sortBy = TagSortBy::from($this->configResolver->getParameter('sort.root.by', 'netgen_tags'));
$sortOrder = TagSortOrder::from($this->configResolver->getParameter('sort.root.order', 'netgen_tags'));
} else {
$tagData = $this->getBasicTagData($tagId);
$sortBy = $tagData['sort_by'] === null
? TagSortBy::from($this->configResolver->getParameter('sort.by', 'netgen_tags'))
: TagSortBy::tryFrom($tagData['sort_by'])
?? TagSortBy::from($this->configResolver->getParameter('sort.by', 'netgen_tags'));
$sortOrder = $tagData['sort_order'] === null
? TagSortOrder::from($this->configResolver->getParameter('sort.order', 'netgen_tags'))
: TagSortOrder::tryFrom($tagData['sort_order'])
?? TagSortOrder::from($this->configResolver->getParameter('sort.order', 'netgen_tags'));
}

$tagIdsQuery = $this->createTagIdsQuery($translations, $useAlwaysAvailable);
$tagIdsQuery->andWhere(
$tagIdsQuery->expr()->andX(
Expand All @@ -137,10 +156,11 @@ public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array
),
$tagIdsQuery->expr()->eq('eztags.main_tag_id', 0),
),
)->setParameter('parent_id', $tagId, Types::INTEGER)
->orderBy('eztags.keyword', 'ASC')
->setFirstResult($offset)
->setMaxResults($limit > 0 ? $limit : PHP_INT_MAX);
)
->orderBy('eztags.' . $sortBy->value, $sortOrder->value)
->setParameter('parent_id', $tagId, Types::INTEGER)
->setFirstResult($offset)
->setMaxResults($limit > 0 ? $limit : PHP_INT_MAX);

$statement = $tagIdsQuery->execute();

Expand All @@ -160,8 +180,8 @@ public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array
[':id'],
),
)
->setParameter('id', $tagIds, Connection::PARAM_INT_ARRAY)
->orderBy('eztags_keyword.keyword', 'ASC');
->orderBy('eztags.' . $sortBy->value, $sortOrder->value)
->setParameter('id', $tagIds, Connection::PARAM_INT_ARRAY);

return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE);
}
Expand Down Expand Up @@ -446,6 +466,15 @@ public function update(UpdateStruct $updateStruct, int $tagId): void
)->set(
'language_mask',
':language_mask',
)->set(
'priority',
':priority',
)->set(
'sort_by',
':sort_by',
)->set(
'sort_order',
':sort_order',
)->where(
$query->expr()->eq(
'id',
Expand All @@ -469,7 +498,10 @@ public function update(UpdateStruct $updateStruct, int $tagId): void
is_bool($updateStruct->alwaysAvailable) ? $updateStruct->alwaysAvailable : true,
),
Types::INTEGER,
);
)
->setParameter('priority', $updateStruct->priority, Types::INTEGER)
->setParameter('sort_by', $updateStruct->sortBy?->value, Types::STRING)
->setParameter('sort_order', $updateStruct->sortOrder?->value, Types::STRING);

$query->execute();

Expand Down Expand Up @@ -847,7 +879,7 @@ public function deleteTag(int $tagId): void
private function createTagIdsQuery(?array $translations = null, bool $useAlwaysAvailable = true): QueryBuilder
{
$query = $this->connection->createQueryBuilder();
$query->select('DISTINCT eztags.id, eztags.keyword')
$query->select('DISTINCT eztags.id, eztags.keyword, eztags.modified, eztags.priority')
->from('eztags', 'eztags')
// @todo: Joining with eztags_keyword is probably a VERY bad way to gather that information
// since it creates an additional cartesian product with translations.
Expand Down Expand Up @@ -930,6 +962,9 @@ private function createTagFindQuery(?array $translations = null, bool $useAlways
'eztags.remote_id',
'eztags.main_language_id',
'eztags.language_mask',
'eztags.priority',
'eztags.sort_by',
'eztags.sort_order',
// Tag keywords
'eztags_keyword.keyword',
'eztags_keyword.locale',
Expand Down
33 changes: 32 additions & 1 deletion bundle/Core/Persistence/Legacy/Tags/Mapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
namespace Netgen\TagsBundle\Core\Persistence\Legacy\Tags;

use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler;
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator;
use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy;
use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder;
use Netgen\TagsBundle\SPI\Persistence\Tags\Tag;
use Netgen\TagsBundle\SPI\Persistence\Tags\TagInfo;

Expand All @@ -16,7 +19,11 @@
*/
class Mapper
{
public function __construct(private LanguageHandler $languageHandler, private LanguageMaskGenerator $languageMaskGenerator) {}
public function __construct(
private readonly LanguageHandler $languageHandler,
private readonly LanguageMaskGenerator $languageMaskGenerator,
private readonly ConfigResolverInterface $configResolver,
) {}

/**
* Creates a tag from a $data row.
Expand All @@ -35,6 +42,9 @@ public function createTagInfoFromRow(array $row): TagInfo
$tagInfo->alwaysAvailable = (bool) ((int) $row['language_mask'] & 1);
$tagInfo->mainLanguageCode = $this->languageHandler->load($row['main_language_id'])->languageCode;
$tagInfo->languageIds = $this->languageMaskGenerator->extractLanguageIdsFromMask((int) $row['language_mask']);
$tagInfo->priority = (int) $row['priority'];

$this->mapSortingFromRow($row, $tagInfo);

return $tagInfo;
}
Expand All @@ -60,6 +70,10 @@ public function extractTagListFromRows(array $rows): array
$tag->alwaysAvailable = (bool) ((int) $row['language_mask'] & 1);
$tag->mainLanguageCode = $this->languageHandler->load($row['main_language_id'])->languageCode;
$tag->languageIds = $this->languageMaskGenerator->extractLanguageIdsFromMask((int) $row['language_mask']);
$tag->priority = (int) $row['priority'];

$this->mapSortingFromRow($row, $tag);

$tagList[$tagId] = $tag;
}

Expand All @@ -68,4 +82,21 @@ public function extractTagListFromRows(array $rows): array

return array_values($tagList);
}

private function mapSortingFromRow(array $row, Tag|TagInfo $tag): void
{
$isRootTag = (int) $row['id'] === 0;
$sortByParam = $isRootTag ? 'sort.root.by' : 'sort.by';
$sortOrderParam = $isRootTag ? 'sort.root.order' : 'sort.order';

$tag->sortBy = $row['sort_by'] === null
? null
: TagSortBy::tryFrom($row['sort_by'])
?? TagSortBy::from($this->configResolver->getParameter($sortByParam, 'netgen_tags'));

$tag->sortOrder = $row['sort_order'] === null
? null
: TagSortOrder::tryFrom($row['sort_order'])
?? TagSortOrder::from($this->configResolver->getParameter($sortOrderParam, 'netgen_tags'));
}
}
3 changes: 3 additions & 0 deletions bundle/Core/Repository/TagsMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ public function buildTagDomainList(array $spiTags, array $prioritizedLanguages =
'mainLanguageCode' => $spiTag->mainLanguageCode,
'languageCodes' => $languageCodes,
'prioritizedLanguageCode' => $prioritizedLanguageCode,
'priority' => $spiTag->priority,
'sortBy' => $spiTag->sortBy,
'sortOrder' => $spiTag->sortOrder,
],
);
}
Expand Down
3 changes: 3 additions & 0 deletions bundle/Core/Repository/TagsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,9 @@ public function updateTag(Tag $tag, TagUpdateStruct $tagUpdateStruct): Tag
$updateStruct->remoteId = trim($tagUpdateStruct->remoteId ?? $spiTag->remoteId);
$updateStruct->mainLanguageCode = $mainLanguageCode;
$updateStruct->alwaysAvailable = $tagUpdateStruct->alwaysAvailable ?? $spiTag->alwaysAvailable;
$updateStruct->priority = $tagUpdateStruct->priority ?? $spiTag->priority;
$updateStruct->sortBy = $tagUpdateStruct->sortBy ?? $spiTag->sortBy;
$updateStruct->sortOrder = $tagUpdateStruct->sortOrder ?? $spiTag->sortOrder;

$this->repository->beginTransaction();

Expand Down
Loading