Skip to content

Commit 92ef1cb

Browse files
author
Julien Neuhart
committed
Add SearchProducts use case and tests
1 parent 7411331 commit 92ef1cb

File tree

6 files changed

+379
-0
lines changed

6 files changed

+379
-0
lines changed

src/api/src/Domain/Dao/ProductDao.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
namespace App\Domain\Dao;
1010

1111
use App\Domain\Dao\Generated\BaseProductDao;
12+
use App\Domain\Model\Filter\ProductsFilters;
1213
use App\Domain\Model\Product;
1314
use App\Domain\Throwable\Exists\ProductWithNameExists;
1415
use App\Domain\Throwable\Invalid\InvalidProduct;
16+
use App\Domain\Throwable\Invalid\InvalidProductsFilters;
1517
use App\Domain\Throwable\NotFound\ProductNotFoundById;
1618
use Symfony\Component\Validator\Validator\ValidatorInterface;
19+
use TheCodingMachine\TDBM\ResultIterator;
1720
use TheCodingMachine\TDBM\TDBMService;
1821

1922
/**
@@ -67,4 +70,29 @@ public function mustNotFindOneByName(string $name): void
6770

6871
throw new ProductWithNameExists($name);
6972
}
73+
74+
/**
75+
* @return Product[]|ResultIterator
76+
*
77+
* @throws InvalidProductsFilters
78+
*/
79+
public function search(ProductsFilters $filters): ResultIterator
80+
{
81+
$violations = $this->validator->validate($filters);
82+
InvalidProductsFilters::throwException($violations);
83+
84+
return $this->find(
85+
[
86+
'name LIKE :search',
87+
'price >= :lowerPrice',
88+
'price <= :upperPrice',
89+
],
90+
[
91+
'search' => '%' . $filters->getSearch() . '%',
92+
'lowerPrice' => $filters->getLowerPrice(),
93+
'upperPrice' => $filters->getUpperPrice(),
94+
],
95+
$filters->getSortBy() . ' ' . $filters->getSortOrder()
96+
);
97+
}
7098
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Domain\Enum\Filter;
6+
7+
use App\Domain\Enum\StringEnum;
8+
9+
final class ProductsSortByEnum implements StringEnum
10+
{
11+
public const NAME = 'name';
12+
public const PRICE = 'price';
13+
14+
/**
15+
* @return string[]
16+
*/
17+
public static function values(): array
18+
{
19+
return [
20+
self::NAME,
21+
self::PRICE,
22+
];
23+
}
24+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Domain\Model\Filter;
6+
7+
use App\Domain\Enum\Filter\ProductsSortByEnum;
8+
use Symfony\Component\Validator\Constraints as Assert;
9+
10+
final class ProductsFilters extends Filters
11+
{
12+
private ?string $search;
13+
private ?float $lowerPrice;
14+
private ?float $upperPrice;
15+
16+
public function __construct(
17+
?string $search = null,
18+
?float $lowerPrice = null,
19+
?float $upperPrice = null,
20+
?string $sortBy = null,
21+
?string $sortOrder = null
22+
) {
23+
$this->search = $search;
24+
$this->lowerPrice = $lowerPrice;
25+
$this->upperPrice = $upperPrice;
26+
parent::__construct($sortBy, $sortOrder);
27+
}
28+
29+
public function getSearch(): ?string
30+
{
31+
return $this->search;
32+
}
33+
34+
public function getLowerPrice(): ?float
35+
{
36+
return $this->lowerPrice;
37+
}
38+
39+
public function getUpperPrice(): ?float
40+
{
41+
return $this->upperPrice;
42+
}
43+
44+
/**
45+
* @Assert\Choice(callback={"App\Domain\Enum\Filter\ProductsSortByEnum", "values"})
46+
*/
47+
public function getSortBy(): string
48+
{
49+
return $this->sortBy;
50+
}
51+
52+
protected function getDefaultSortBy(): string
53+
{
54+
return ProductsSortByEnum::PRICE;
55+
}
56+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Domain\Throwable\Invalid;
6+
7+
use Symfony\Component\Validator\ConstraintViolationListInterface;
8+
9+
final class InvalidProductsFilters extends Invalid
10+
{
11+
/**
12+
* @param ConstraintViolationListInterface<mixed> $constraintViolationList
13+
*
14+
* @throws InvalidProductsFilters
15+
*/
16+
public static function throwException(ConstraintViolationListInterface $constraintViolationList): void
17+
{
18+
if ($constraintViolationList->count() > 0) {
19+
throw new self($constraintViolationList);
20+
}
21+
}
22+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\UseCase\Product;
6+
7+
use App\Domain\Dao\ProductDao;
8+
use App\Domain\Model\Filter\ProductsFilters;
9+
use App\Domain\Model\Product;
10+
use App\Domain\Throwable\Invalid\InvalidProductsFilters;
11+
use TheCodingMachine\GraphQLite\Annotations\Query;
12+
use TheCodingMachine\TDBM\ResultIterator;
13+
14+
final class SearchProducts
15+
{
16+
private ProductDao $productDao;
17+
18+
public function __construct(ProductDao $productDao)
19+
{
20+
$this->productDao = $productDao;
21+
}
22+
23+
/**
24+
* @return Product[]|ResultIterator
25+
*
26+
* @throws InvalidProductsFilters
27+
*
28+
* @Query
29+
*/
30+
public function searchProducts(
31+
?string $search = null,
32+
?float $lowerPrice = null,
33+
?float $upperPrice = null,
34+
?string $sortBy = null,
35+
?string $sortOrder = null
36+
): ResultIterator {
37+
$filters = new ProductsFilters(
38+
$search,
39+
$lowerPrice,
40+
$upperPrice,
41+
$sortBy,
42+
$sortOrder
43+
);
44+
45+
return $this->productDao->search($filters);
46+
}
47+
}

0 commit comments

Comments
 (0)