-1

我们的目标是将产品列表中的某些促销产品拉到前面。

重要提示:促销产品因类别/过滤器而异,因此仅插入自定义字段或使用已内置的“促销产品”标志是行不通的。我们已经可以访问要拉到前面的产品 ID,我们只需要相应地对列表进行排序。

我们订阅ProductListingCriteriaEvent::class并尝试了一些东西 - 基于https://stackoverflow.com/a/6810827/288568 - 像这样:

$criteria = $event->getCriteria()
$sortings = $criteria->getSorting();
$criteria->resetSorting();
 
$criteria->addSorting(new FieldSorting('FIELD(id, 0x123456...)', FieldSorting::DESCENDING));

foreach($sortings as $sorting) {
    $criteria->addSorting($sorting);
}

其中 0x123456... 将是要拉到前面的产品的 UUID。

这当然行不通,因为 Shopware 需要一个字段。

出于这个原因,是否可以创建类似“虚拟”字段的内容,或者是否有其他方法可以插入这样的原始 SQL 部分?

4

2 回答 2

2

通过装饰 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 来实现。

于 2021-11-25T15:12:26.563 回答
0

设计上不支持原始 SQL,因为如果您使用 ElasticSearch 集成,ElasticSearch 也可以解析和执行具有该条件的查询。

您可以使用一个简单的自定义字段来进行排序,而不是在读取中处理所有内容。您可以为产品添加一个myCustomSorting自定义字段,并将该字段的值设置为 1,以便首先显示所有产品。然后您将条件扩展为首先按该字段排序。

于 2021-11-19T07:03:39.457 回答