通过装饰 QueryBuilder 添加新的自定义排序工具
我们可以实现一个新的排序类,它将特定的 id 拉到前面,然后装饰CriteriaQueryBuilder
添加这个新的排序类型。
实施细节(在 Shopware 6.4.6.0 上测试)
首先我们定义一个类来保存新排序方法的信息:
CustomSorting.php
<?php declare(strict_types=1);
namespace ExampleProductListing\Framework\DataAbstractionLayer\Search\Sorting;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
class CustomSorting extends FieldSorting
{
private array $ids = [];
public function addId(string $id)
{
$this->ids[] = $id;
}
public function getIds()
{
return $this->ids;
}
}
接下来,我们为 定义一个装饰器CriteriaQueryBuilder
:
services.xml
<service id="ExampleProductListing\Framework\DataAbstractionLayer\Dbal\CriteriaQueryBuilderDecorator"
decorates="Shopware\Core\Framework\DataAbstractionLayer\Dbal\CriteriaQueryBuilder">
<argument type="service" id="ExampleProductListing\Framework\DataAbstractionLayer\Dbal\CriteriaQueryBuilderDecorator.inner"/>
<argument type="service" id="Shopware\Core\Framework\DataAbstractionLayer\Search\Parser\SqlQueryParser"/>
<argument type="service" id="Shopware\Core\Framework\DataAbstractionLayer\Dbal\EntityDefinitionQueryHelper"/>
<argument type="service" id="Shopware\Core\Framework\DataAbstractionLayer\Search\Term\SearchTermInterpreter"/>
<argument type="service" id="Shopware\Core\Framework\DataAbstractionLayer\Search\Term\EntityScoreQueryBuilder"/>
<argument type="service" id="Shopware\Core\Framework\DataAbstractionLayer\Dbal\JoinGroupBuilder"/>
<argument type="service"
id="Shopware\Core\Framework\DataAbstractionLayer\Dbal\FieldResolver\CriteriaPartResolver"/>
</service>
接下来,我们实现装饰器,它包含使用该FIELD()
方法生成 SQL 的新逻辑。
CriteriaQueryBuilderDecorator.php
<?php declare(strict_types=1);
namespace ExampleProductListing\Framework\DataAbstractionLayer\Dbal;
use ExampleProductListing\Framework\DataAbstractionLayer\Search\Sorting\CustomSorting;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\Dbal\CriteriaQueryBuilder;
use Shopware\Core\Framework\DataAbstractionLayer\Dbal\EntityDefinitionQueryHelper;
use Shopware\Core\Framework\DataAbstractionLayer\Dbal\FieldResolver\CriteriaPartResolver;
use Shopware\Core\Framework\DataAbstractionLayer\Dbal\JoinGroupBuilder;
use Shopware\Core\Framework\DataAbstractionLayer\Dbal\QueryBuilder;
use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\Filter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Parser\SqlQueryParser;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Term\EntityScoreQueryBuilder;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Term\SearchTermInterpreter;
class CriteriaQueryBuilderDecorator extends CriteriaQueryBuilder
{
private $decoratedService;
/***
* @var EntityDefinitionQueryHelper
*/
private $helper;
public function __construct(
CriteriaQueryBuilder $decoratedService,
SqlQueryParser $parser,
EntityDefinitionQueryHelper $helper,
SearchTermInterpreter $interpreter,
EntityScoreQueryBuilder $scoreBuilder,
JoinGroupBuilder $joinGrouper,
CriteriaPartResolver $criteriaPartResolver
)
{
$this->decoratedService = $decoratedService;
$this->helper = $helper;
parent::__construct($parser, $helper,$interpreter, $scoreBuilder, $joinGrouper, $criteriaPartResolver);
}
public function getDecorated(): CriteriaQueryBuilder
{
return $this->decoratedService;
}
public function addSortings(EntityDefinition $definition, Criteria $criteria, array $sortings, QueryBuilder $query, Context $context): void
{
foreach ($sortings as $sorting) {
if ($sorting instanceof CustomSorting) {
$accessor = $this->helper->getFieldAccessor($sorting->getField(), $definition, $definition->getEntityName(), $context);
$ids = implode(',', array_reverse($sorting->getIds()));
if (empty($ids)) {
continue;
}
$query->addOrderBy('FIELD(' . $accessor . ',' . $ids . ')', 'DESC');
} else {
$this->decoratedService->addSortings($definition, $criteria, [$sorting], $query, $context);
}
}
}
public function build(QueryBuilder $query, EntityDefinition $definition, Criteria $criteria, Context $context, array $paths = []): QueryBuilder
{
return parent::build($query, $definition, $criteria, $context, $paths);
}
public function addFilter(EntityDefinition $definition, ?Filter $filter, QueryBuilder $query, Context $context): void
{
parent::addFilter($definition, $filter, $query, $context);
}
}
如何使用新的排序方法
最后,在构建标准时(例如在 中ProductListingCriteriaEvent
),我们可以通过指定那里的 ID 将特定产品拉到前面。(这里是硬编码,在现实世界中它们来自不同的来源,这取决于选择的过滤器)
$customSorting = new CustomSorting('product.id');
$customSorting->addId('0x76f9a07e153645d7bd8ad62abd131234');
$customSorting->addId('0x76a890cb23ea433a97006e71cdb75678');
$event->getCriteria()
->addSorting($customSorting);
兼容性
这仅适用于 SQL 引擎。如果还应该支持 ElasticSearch,这可能也可以通过装饰 ElasticSearch Query Buidler 来实现。