Skip to content

Commit 2fc1ff3

Browse files
committed
Refactor building the filter
1 parent 72a235a commit 2fc1ff3

File tree

11 files changed

+244
-46
lines changed

11 files changed

+244
-46
lines changed

composer.json

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
},
8989
"autoload-dev": {
9090
"psr-4": {
91+
"TestApp\\": "tests/Application/src/",
9192
"Setono\\SyliusMeilisearchPlugin\\Tests\\": "tests/"
9293
},
9394
"classmap": [

src/Engine/SearchEngine.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@ public function execute(?string $query, array $parameters = []): SearchResult
2929
{
3030
$page = max(1, (int) ($parameters['p'] ?? 1));
3131
$sort = (string) ($parameters['sort'] ?? '');
32+
$facetsFilter = $parameters['facets'] ?? [];
3233

3334
$metadata = $this->metadataFactory->getMetadataFor($this->index->document);
3435
$indexUid = $this->indexNameResolver->resolve($this->index);
3536
$query = $query ?? '';
3637
$facets = array_map(static fn (Facet $facet) => $facet->name, $metadata->getFacets());
37-
$filter = $this->filterBuilder->build($parameters);
38+
$filter = $this->filterBuilder->build($facetsFilter);
3839

3940
$mainQuery = $this->buildSearchQuery($indexUid, $query, $facets, $filter)
4041
->setHitsPerPage($this->hitsPerPage)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Setono\SyliusMeilisearchPlugin\Meilisearch\Builder;
6+
7+
final class CompositeFilterBuilder implements FilterBuilderInterface
8+
{
9+
/** @var array<FilterBuilderInterface> */
10+
private array $filterBuilders;
11+
12+
public function __construct (
13+
iterable $filterBuilders,
14+
) {
15+
$this->filterBuilders = $filterBuilders instanceof \Traversable ? iterator_to_array($filterBuilders) : $filterBuilders;
16+
}
17+
18+
public function build(array $facets): string|array
19+
{
20+
$filters = [];
21+
22+
foreach ($this->filterBuilders as $filterBuilder) {
23+
if ($filterBuilder->supports($facets)) {
24+
$filters[] = $filterBuilder->build($facets);
25+
}
26+
}
27+
28+
return $filters;
29+
}
30+
31+
public function supports(array $facets): bool
32+
{
33+
return true;
34+
}
35+
}

src/Meilisearch/Builder/FilterBuilder.php

-42
This file was deleted.

src/Meilisearch/Builder/FilterBuilderInterface.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@
66

77
interface FilterBuilderInterface
88
{
9-
public function build(array $parameters): array;
9+
public function build(array $facets): string|array;
10+
11+
public function supports(array $facets): bool;
1012
}

src/Resources/config/services/meilisearch.xml

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
<services>
55
<!-- Builder -->
66
<service id="Setono\SyliusMeilisearchPlugin\Meilisearch\Builder\FilterBuilderInterface"
7-
alias="Setono\SyliusMeilisearchPlugin\Meilisearch\Builder\FilterBuilder"/>
7+
alias="Setono\SyliusMeilisearchPlugin\Meilisearch\Builder\CompositeFilterBuilder"/>
88

9-
<service id="Setono\SyliusMeilisearchPlugin\Meilisearch\Builder\FilterBuilder"/>
9+
<service id="Setono\SyliusMeilisearchPlugin\Meilisearch\Builder\CompositeFilterBuilder">
10+
<argument type="tagged_iterator" tag="setono_sylius_meilisearch.filter_builder"/>
11+
</service>
1012

1113
<!-- Synonym resolver -->
1214
<service id="Setono\SyliusMeilisearchPlugin\Meilisearch\SynonymResolverInterface"

tests/Application/config/services.yaml

+13
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,16 @@
22
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
33
parameters:
44
locale: en_US
5+
6+
services:
7+
_defaults:
8+
autowire: true # Automatically injects dependencies in your services.
9+
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
10+
11+
TestApp\:
12+
resource: '../src/*'
13+
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
14+
15+
_instanceof:
16+
Setono\SyliusMeilisearchPlugin\Meilisearch\Builder\FilterBuilderInterface:
17+
tags: [ 'setono_sylius_meilisearch.filter_builder' ]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TestApp\FilterBuilder;
6+
7+
use Setono\SyliusMeilisearchPlugin\Meilisearch\Builder\FilterBuilderInterface;
8+
9+
final class BrandFilterBuilder implements FilterBuilderInterface
10+
{
11+
public function build(array $facets): string|array
12+
{
13+
$brandQuery = [];
14+
/** @var string $size */
15+
foreach ($facets['brand'] as $size) {
16+
$brandQuery[] = sprintf('brand = "%s"', $size);
17+
}
18+
19+
return '(' . implode(' OR ', $brandQuery) . ')';
20+
}
21+
22+
public function supports(array $facets): bool
23+
{
24+
return isset($facets['brand']);
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Setono\SyliusMeilisearchPlugin\Tests\Application\src\FilterBuilder;
6+
7+
use Setono\SyliusMeilisearchPlugin\Meilisearch\Builder\FilterBuilderInterface;
8+
9+
final class OnSaleFilterBuilder implements FilterBuilderInterface
10+
{
11+
public function build(array $facets): string|array
12+
{
13+
return 'onSale = true';
14+
}
15+
16+
public function supports(array $facets): bool
17+
{
18+
return isset($facets['onSale']);
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TestApp\FilterBuilder;
6+
7+
use Setono\SyliusMeilisearchPlugin\Meilisearch\Builder\FilterBuilderInterface;
8+
9+
final class SizeFilterBuilder implements FilterBuilderInterface
10+
{
11+
public function build(array $facets): string|array
12+
{
13+
$sizeQuery = [];
14+
/** @var string $size */
15+
foreach ($facets['size'] as $size) {
16+
$sizeQuery[] = sprintf('size = "%s"', $size);
17+
}
18+
19+
return '(' . implode(' OR ', $sizeQuery) . ')';
20+
}
21+
22+
public function supports(array $facets): bool
23+
{
24+
return isset($facets['size']);
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Setono\SyliusMeilisearchPlugin\Tests\Unit\Meilisearch\Builder;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Setono\SyliusMeilisearchPlugin\Meilisearch\Builder\CompositeFilterBuilder;
9+
use Setono\SyliusMeilisearchPlugin\Meilisearch\Builder\FilterBuilderInterface;
10+
11+
final class CompositeFilterBuilderTest extends TestCase
12+
{
13+
public function test_it_returns_filters(): void
14+
{
15+
$brandFilterBuilder = new class() implements FilterBuilderInterface {
16+
public function build(array $facets): string|array
17+
{
18+
$brandQuery = [];
19+
/** @var string $size */
20+
foreach ($facets['brand'] as $size) {
21+
$brandQuery[] = sprintf('brand = "%s"', $size);
22+
}
23+
24+
return '(' . implode(' OR ', $brandQuery) . ')';
25+
}
26+
27+
public function supports(array $facets): bool
28+
{
29+
return isset($facets['brand']);
30+
}
31+
};
32+
33+
$sizeFilterBuilder = new class() implements FilterBuilderInterface {
34+
public function build(array $facets): string|array
35+
{
36+
$sizeQuery = [];
37+
/** @var string $size */
38+
foreach ($facets['size'] as $size) {
39+
$sizeQuery[] = sprintf('size = "%s"', $size);
40+
}
41+
42+
return '(' . implode(' OR ', $sizeQuery) . ')';
43+
}
44+
45+
public function supports(array $facets): bool
46+
{
47+
return isset($facets['size']);
48+
}
49+
};
50+
51+
$compositeFilterBuilder = new CompositeFilterBuilder([$brandFilterBuilder, $sizeFilterBuilder]);
52+
53+
$filters = $compositeFilterBuilder->build([
54+
'onSale' => true,
55+
'brand' => ['brand1'],
56+
'size' => ['size1', 'size2'],
57+
]);
58+
59+
$this->assertSame([
60+
'(brand = "brand1")',
61+
'(size = "size1" OR size = "size2")',
62+
], $filters);
63+
}
64+
65+
public function test_it_uses_only_supported_filter_builders(): void
66+
{
67+
$brandFilterBuilder = new class() implements FilterBuilderInterface {
68+
public function build(array $facets): string|array
69+
{
70+
$brandQuery = [];
71+
/** @var string $size */
72+
foreach ($facets['brand'] as $size) {
73+
$brandQuery[] = sprintf('brand = "%s"', $size);
74+
}
75+
76+
return '(' . implode(' OR ', $brandQuery) . ')';
77+
}
78+
79+
public function supports(array $facets): bool
80+
{
81+
return isset($facets['brand']);
82+
}
83+
};
84+
85+
$sizeFilterBuilder = new class() implements FilterBuilderInterface {
86+
public function build(array $facets): string|array
87+
{
88+
$sizeQuery = [];
89+
/** @var string $size */
90+
foreach ($facets['size'] as $size) {
91+
$sizeQuery[] = sprintf('size = "%s"', $size);
92+
}
93+
94+
return '(' . implode(' OR ', $sizeQuery) . ')';
95+
}
96+
97+
public function supports(array $facets): bool
98+
{
99+
return isset($facets['size']);
100+
}
101+
};
102+
103+
$compositeFilterBuilder = new CompositeFilterBuilder([$brandFilterBuilder, $sizeFilterBuilder]);
104+
105+
$filters = $compositeFilterBuilder->build([
106+
'onSale' => true,
107+
'size' => ['size1', 'size2'],
108+
]);
109+
110+
$this->assertSame([
111+
'(size = "size1" OR size = "size2")',
112+
], $filters);
113+
}
114+
}

0 commit comments

Comments
 (0)