symfony 如何集成搜索过滤器在实体的多个属性搜索使用Api平台?

uinbv5nw  于 2023-02-16  发布在  其他
关注(0)|答案(1)|浏览(190)

表中有4个字段
识别号|姓名|欧洲航空公司|汉
这些是我的一个实体的属性那么,我如何配置实体与searchFilter,以便当我搜索一个字符串,它应该检查它在所有4列,并返回匹配的结果.
例如:-如果我搜索“汽车”,它将匹配名称属性并返回,但当我键入id说“219”,它将匹配id属性并相应地返回我。
我想只使用api平台来实现这一点,我的项目是在symfony。
因此,搜索应在所有4个字段中查找,然后返回结果。

9wbgstp7

9wbgstp71#

你可以用一个自定义的过滤器将这些条件与OR结合起来。我在第6章tutorial的排序和自定义过滤器中做了一个。我在下面包含了它的代码。
您可以在ApiFilter标记中配置它搜索的属性。在您的情况下,这将是:

#[ApiFilter(filterClass: SimpleSearchFilter::class,
properties: ['ID', 'Name', 'EAN', 'HAN'])]

查询字符串如下所示:

?simplesearch=car

将搜索指定的所有属性,不区分大小写,如%car%(如果您以编程方式创建查询字符串,则需要对参数值进行URL编码)
您还可以在ApiFilter标记中配置参数名称。例如:

#[ApiFilter(filterClass: SimpleSearchFilter::class,
properties: ['ID', 'Name', 'EAN', 'HAN'])]
arguments: ['searchParameterName'='search'])]

将允许查询字符串,如:

?search=car

我必须决定如何将这个过滤器与其他过滤器合并。为了与现有的过滤器兼容,我使用了AND。因此,如果您也添加了一个标准的DateFilter到同一个实体类,您将能够在指定的字段中搜索指定日期之后的AND,但不能在指定日期之后的OR中搜索某些单词。例如:

?simplesearch=car&produced[after]=2018-03-31

它将搜索字符串拆分为单词,并搜索每个单词的每个属性,因此

?simplesearch=car 219

将在所有指定的属性中搜索.. LIKE %car%或.. LIKE %219%
是的,它确实假设所有属性都是string类型,或者你的数据库支持LIKE的其他属性类型(MySQL支持,Postgres不支持,所以如果ID是一个int,这将不适用于Postgres).过滤器确实按相关性排序(这就是为什么它被称为SimpleSearchFilter).
当然,您可以随意更改代码,如果您知道如何使用Doctrine中的元数据,那么为具有其他数据类型的属性添加不同的处理并不太困难。
下面是代码(适用于APIP 3.0):

<?php

namespace App\Filter;

use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Operation;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;
use Psr\Log\LoggerInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use ApiPlatform\Exception\InvalidArgumentException;

/**
 * Selects entities where each search term is found somewhere
 * in at least one of the specified properties.
 * Search terms must be separated by spaces.
 * Search is case insensitive.
 * All specified properties type must be string. Nested properties are supported.
 * @package App\Filter
 */
class SimpleSearchFilter extends AbstractFilter
{
    private $searchParameterName;

    /**
     * Add configuration parameter
     * {@inheritdoc}
     * @param string $searchParameterName The parameter whose value this filter searches for
     */
    public function __construct(ManagerRegistry $managerRegistry, LoggerInterface $logger = null, array $properties = null, NameConverterInterface $nameConverter = null, string $searchParameterName = 'simplesearch')
    {
        parent::__construct($managerRegistry, $logger, $properties, $nameConverter);

        $this->searchParameterName = $searchParameterName;
    }

    /** {@inheritdoc} */
    protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
    {
        if (null === $value || $property !== $this->searchParameterName) {
            return;
        }

        $words = explode(' ', $value);
        foreach ($words as $word) {
            if (empty($word)) continue;

            $this->addWhere($queryBuilder, $word, $queryNameGenerator->generateParameterName($property), $queryNameGenerator, $resourceClass);
        }
    }

    private function addWhere($queryBuilder, $word, $parameterName, $queryNameGenerator, $resourceClass)
    {
        // Build OR expression
        $orExp = $queryBuilder->expr()->orX();
        foreach ($this->getProperties() as $prop => $ignoored) {
            $alias = $queryBuilder->getRootAliases()[0];
            // Thanks to https://stackoverflow.com/users/8451711/hasbert and https://stackoverflow.com/users/11599673/polo
            if ($this->isPropertyNested($prop, $resourceClass)) {
                [$alias, $prop] = $this->addJoinsForNestedProperty($prop, $alias, $queryBuilder, $queryNameGenerator, $resourceClass, Join::INNER_JOIN);
            }

            $orExp->add($queryBuilder->expr()->like('LOWER('. $alias. '.' . $prop. ')', ':' . $parameterName));
        }

        // Add it
        $queryBuilder
            ->andWhere('(' . $orExp . ')')
            ->setParameter($parameterName, '%' . strtolower($word). '%');
    }

    /** {@inheritdoc} */
    public function getDescription(string $resourceClass): array
    {
        $props = $this->getProperties();
        if (null===$props) {
            throw new InvalidArgumentException('Properties must be specified');
        }
        return [
            $this->searchParameterName => [
                'property' => implode(', ', array_keys($props)),
                'type' => 'string',
                'required' => false,
                'swagger' => [
                    'description' => 'Selects entities where each search term is found somewhere in at least one of the specified properties',
                ]
            ]
        ];
    }
}

由于构造函数参数“searchParameterName”,服务需要在API/config/services.yaml中进行配置

'App\Filter\SimpleSearchFilter':
    arguments:
        $searchParameterName: 'ignoored'

相关问题