Commit fc7a1db8 authored by Адлан Шамавов's avatar Адлан Шамавов
Browse files

SYM-3 | Ранжирование и поиск по категориям

parent 2d1cbebd
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -25,3 +25,4 @@ services:

    App\Service\FilterService:
        $finder: '@fos_elastica.finder.product'
        $categoryFinder: '@fos_elastica.finder.category'
 No newline at end of file
+42 −0
Original line number Diff line number Diff line
@@ -8,6 +8,12 @@ class GetFilteredProductsRequest
{
    private array $filters;

    private ?string $category = null;

    private ?float $minPrice = null;

    private ?float $maxPrice = null;

    public function getFilters(): array
    {
        return $this->filters;
@@ -19,4 +25,40 @@ class GetFilteredProductsRequest

        return $this;
    }

    public function getCategory(): ?string
    {
        return $this->category;
    }

    public function setCategory(?string $category): self
    {
        $this->category = $category;

        return $this;
    }

    public function getMinPrice(): ?float
    {
        return $this->minPrice;
    }

    public function setMinPrice(?float $minPrice): self
    {
        $this->minPrice = $minPrice;

        return $this;
    }

    public function getMaxPrice(): ?float
    {
        return $this->maxPrice;
    }

    public function setMaxPrice(?float $maxPrice): self
    {
        $this->maxPrice = $maxPrice;

        return $this;
    }
}
+12 −10
Original line number Diff line number Diff line
@@ -30,27 +30,29 @@ readonly class RequestQueryArgumentResolver implements ValueResolverInterface
        try {
            $strQuery = [];
            $arrQuery = [];
            $props = [];

            foreach ($request->query->all() as $key => $value) {
                if (property_exists($argument->getType(), $key)) {
                    $props[$key] = ctype_digit($value) ? (int) $value : $value;
                    continue;
                }

                if (!is_array($value)) {
                    $strQuery[$key] = $value;
                    $strQuery[$key] = ctype_digit($value) ? (int) $value : $value;
                    continue;
                }

                $arrQuery[$key] = $value;
            }

            $query = array_map(
                static function (string $value) {
                    $int_value = ctype_digit($value) ? (int) $value : null;

                    return $int_value ?? $value;
                }, $strQuery);

            $query = array_merge($query, $arrQuery);
            $query = array_merge($strQuery, $arrQuery);

            $model = $this->serializer->deserialize(
                json_encode(['filters' => $query], JSON_THROW_ON_ERROR),
                json_encode(
                    array_merge(['filters' => $query], $props),
                    JSON_THROW_ON_ERROR
                ),
                $argument->getType(),
                JsonEncoder::FORMAT
            );
+45 −3
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ class FilterService
        private AttributeItemRepository $attributeItemRepository,
        private ProductAttributeRepository $productAttributeRepository,
        private FinderInterface $finder,
        private FinderInterface $categoryFinder,
    ) {
    }

@@ -50,9 +51,30 @@ class FilterService
    public function getProducts(GetFilteredProductsRequest $request): array
    {
        $filters = $request->getFilters();

        $andOuter = new Query\BoolQuery();

        if ($request->getCategory() !== null) {
            $category = $this->categoryFinder->find(
                new Query\MatchQuery('name', $request->getCategory())
            );
            $categories = $this->findChildrenCategories($category);

            foreach ($categories as $category) {
                $andOuter->addShould(new Query\MatchQuery('category.name', $category->getName()));
            }
        }

        $range = [];
        if ($request->getMinPrice() !== null) {
            $range['gte'] = $request->getMinPrice();
        }
        if ($request->getMaxPrice() !== null) {
            $range['lte'] = $request->getMaxPrice();
        }
        if (!empty($range)) {
            $andOuter->addMust(new Query\Range('price', $range));
        }

        foreach ($filters as $filterName => $filterValues) {
            $filterName = str_replace('_', ' ', $filterName);
            $orOuter = new Query\BoolQuery();
@@ -65,7 +87,7 @@ class FilterService
                $isDate = \DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $filterValue);

                $andInner = new Query\BoolQuery();
                $optionTerm = new Query\MatchQuery('product_attributes.attribute.name', $filterName);
                $keyTerm = new Query\MatchQuery('product_attributes.attribute.name', $filterName);

                if (ctype_digit($filterValue)) {
                    $valueTerm = (new Query\Term())->setTerm('product_attributes.value_int', $filterValue);
@@ -77,7 +99,7 @@ class FilterService
                    $valueTerm = new Query\MatchQuery('product_attributes.value_item.name', $filterValue);
                }

                $andInner->addMust($optionTerm);
                $andInner->addMust($keyTerm);
                $andInner->addMust($valueTerm);

                $orOuter->addShould($andInner);
@@ -88,4 +110,24 @@ class FilterService

        return $this->finder->find(new Query($andOuter));
    }

    private function findChildrenCategories(array $categories): array
    {
        $newCategories = $categories;

        foreach ($categories as $category) {
            $children = $this->categoryFinder->find(
                (new Query\Term())->setTerm('parentId', $category->getId())
            );

            if (empty($children) || in_array($children[0], $categories, true)) {
                continue;
            }

            $newCategories = array_merge($categories, $children);
            $newCategories = $this->findChildrenCategories($newCategories);
        }

        return $newCategories;
    }
}