Skip to content

Commit 7171ba8

Browse files
committed
add place collection provider
1 parent 9dc4921 commit 7171ba8

File tree

10 files changed

+257
-0
lines changed

10 files changed

+257
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Field\Infrastructure\ApiPlatform\Filter;
6+
7+
use ApiPlatform\Metadata\FilterInterface;
8+
use App\Field\Domain\Enum\FieldCommunity;
9+
use Symfony\Component\PropertyInfo\Type;
10+
11+
final class FieldParentCommunityIdFilter implements FilterInterface
12+
{
13+
public function getDescription(string $resourceClass): array
14+
{
15+
return [
16+
'parentCommunityId' => [
17+
'property' => FieldCommunity::PARENT_COMMUNITY_ID->value,
18+
'type' => Type::BUILTIN_TYPE_STRING,
19+
'required' => false,
20+
],
21+
];
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Field\Infrastructure\ApiPlatform\Filter;
6+
7+
use ApiPlatform\Metadata\FilterInterface;
8+
use App\Field\Domain\Enum\FieldCommunity;
9+
use Symfony\Component\PropertyInfo\Type;
10+
11+
final class FieldParentWikidataIdFilter implements FilterInterface
12+
{
13+
public function getDescription(string $resourceClass): array
14+
{
15+
return [
16+
'parentWikidataId' => [
17+
'property' => FieldCommunity::PARENT_WIKIDATA_ID->value,
18+
'type' => Type::BUILTIN_TYPE_INT,
19+
'required' => false,
20+
],
21+
];
22+
}
23+
}

src/FieldHolder/Community/Infrastructure/ApiPlatform/Resource/CommunityResource.php

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use ApiPlatform\Metadata\Put;
1414
use App\Field\Domain\Model\Field;
1515
use App\Field\Infrastructure\ApiPlatform\Filter\FieldNameFilter;
16+
use App\Field\Infrastructure\ApiPlatform\Filter\FieldParentWikidataIdFilter;
1617
use App\Field\Infrastructure\ApiPlatform\Filter\FieldTypeFilter;
1718
use App\Field\Infrastructure\ApiPlatform\Filter\FieldWikidataIdFilter;
1819
use App\FieldHolder\Community\Domain\Model\Community;
@@ -57,6 +58,7 @@
5758
filters: [
5859
FieldTypeFilter::class,
5960
FieldWikidataIdFilter::class,
61+
FieldParentWikidataIdFilter::class,
6062
FieldNameFilter::class,
6163
],
6264
provider: CommunityCollectionProvider::class,

src/FieldHolder/Community/Infrastructure/ApiPlatform/State/Provider/CommunityCollectionProvider.php

+6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
3636
/** @var string|null $type */
3737
$type = $context['filters'][FieldCommunity::TYPE->value] ?? null;
3838
$wikidataId = $context['filters'][FieldCommunity::WIKIDATA_ID->value] ?? null;
39+
$parentWikidataId = $context['filters'][FieldCommunity::PARENT_WIKIDATA_ID->value] ?? null;
3940

4041
$name = $context['filters'][FieldCommunity::NAME->value] ?? null;
4142
$page = $itemsPerPage = null;
@@ -61,11 +62,16 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
6162
return [];
6263
}
6364
}
65+
66+
if ($parentWikidataId) {
67+
$parentCommunity = $this->communityRepo->withWikidataId(intval($parentWikidataId))->asCollection()->first();
68+
}
6469

6570
$models = $this->communityRepo
6671
->ofIds(array_map(fn (string $entityId) => Uuid::fromString($entityId), $entityIds ?? []))
6772
->withType($type)
6873
->withWikidataId(intval($wikidataId))
74+
->withParentCommunityId($parentCommunity->id ?? null)
6975
->withPagination($page, $itemsPerPage);
7076

7177
$resources = [];

src/FieldHolder/Place/Domain/Model/Place.php

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class Place extends FieldHolder
3232
* @var Collection<int, Field>
3333
*/
3434
#[ORM\OneToMany(targetEntity: Field::class, mappedBy: 'place')]
35+
#[Groups(['places'])]
3536
public Collection $fields;
3637

3738
/**

src/FieldHolder/Place/Domain/Repository/PlaceRepositoryInterface.php

+2
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ public function withWikidataId(?int $value): static;
2525
* @param array<int> $wikidataIds
2626
*/
2727
public function withWikidataIds(array $wikidataIds): static;
28+
29+
public function withParentCommunityId(Uuid $parentId): static;
2830
}

src/FieldHolder/Place/Infrastructure/ApiPlatform/Resource/PlaceResource.php

+11
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@
77
use ApiPlatform\Metadata\ApiProperty;
88
use ApiPlatform\Metadata\ApiResource;
99
use ApiPlatform\Metadata\Get;
10+
use ApiPlatform\Metadata\GetCollection;
1011
use ApiPlatform\Metadata\Patch;
1112
use ApiPlatform\Metadata\Post;
1213
use ApiPlatform\Metadata\Put;
1314
use App\Field\Domain\Model\Field;
15+
use App\Field\Infrastructure\ApiPlatform\Filter\FieldParentCommunityIdFilter;
1416
use App\FieldHolder\Place\Domain\Model\Place;
1517
use App\FieldHolder\Place\Infrastructure\ApiPlatform\Input\PlaceWikidataInput;
1618
use App\FieldHolder\Place\Infrastructure\ApiPlatform\State\Processor\CreatePlaceProcessor;
1719
use App\FieldHolder\Place\Infrastructure\ApiPlatform\State\Processor\UpdatePlaceProcessor;
1820
use App\FieldHolder\Place\Infrastructure\ApiPlatform\State\Processor\UpsertPlaceProcessor;
21+
use App\FieldHolder\Place\Infrastructure\ApiPlatform\State\Provider\PlaceCollectionProvider;
1922
use App\FieldHolder\Place\Infrastructure\ApiPlatform\State\Provider\PlaceItemProvider;
2023
use Symfony\Component\Serializer\Annotation\Groups;
2124
use Symfony\Component\Uid\Uuid;
@@ -53,6 +56,14 @@
5356
processor: UpsertPlaceProcessor::class,
5457
input: PlaceWikidataInput::class,
5558
),
59+
new GetCollection(
60+
uriTemplate: '/places',
61+
filters: [
62+
FieldParentCommunityIdFilter::class,
63+
],
64+
provider: PlaceCollectionProvider::class,
65+
normalizationContext: ['groups' => ['places']],
66+
),
5667
],
5768
)]
5869
final class PlaceResource
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\FieldHolder\Place\Infrastructure\ApiPlatform\State\Provider;
6+
7+
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
8+
use ApiPlatform\Metadata\Operation;
9+
use ApiPlatform\State\Pagination\Pagination;
10+
use ApiPlatform\State\ProviderInterface;
11+
use App\Field\Domain\Enum\FieldCommunity;
12+
use App\FieldHolder\Place\Domain\Repository\PlaceRepositoryInterface;
13+
use App\FieldHolder\Place\Infrastructure\ApiPlatform\Resource\PlaceResource;
14+
use App\Shared\Infrastructure\ApiPlatform\State\Paginator;
15+
use Symfony\Component\Uid\Uuid;
16+
17+
/**
18+
* @implements ProviderInterface<PlaceResource>
19+
*/
20+
final class PlaceCollectionProvider implements ProviderInterface
21+
{
22+
public function __construct(
23+
private Pagination $pagination,
24+
private PlaceRepositoryInterface $placeRepo,
25+
) {
26+
}
27+
28+
/**
29+
* @return Paginator<PlaceResource>|list<PlaceResource>
30+
*/
31+
public function provide(Operation $operation, array $uriVariables = [], array $context = []): Paginator|array
32+
{
33+
$parentCommunityId = $context['filters'][FieldCommunity::PARENT_COMMUNITY_ID->value] ?? null;
34+
if ($parentCommunityId && !Uuid::isValid($parentCommunityId)) {
35+
throw new InvalidArgumentException(sprintf('provided parentCommunityId %s is not a valid uuid', $parentCommunityId));
36+
}
37+
38+
$page = $itemsPerPage = null;
39+
40+
if ($this->pagination->isEnabled($operation, $context)) {
41+
$page = $this->pagination->getPage($context);
42+
$itemsPerPage = $this->pagination->getLimit($operation, $context);
43+
}
44+
45+
$models = $this->placeRepo
46+
->withParentCommunityId(Uuid::fromString($parentCommunityId))
47+
->withPagination($page, $itemsPerPage);
48+
49+
$resources = [];
50+
foreach ($models as $model) {
51+
$resources[] = PlaceResource::fromModel($model);
52+
}
53+
54+
if (null !== $paginator = $models->paginator()) {
55+
$resources = new Paginator(
56+
new \ArrayIterator($resources),
57+
(float) $paginator->getCurrentPage(),
58+
(float) $paginator->getItemsPerPage(),
59+
(float) $paginator->getLastPage(),
60+
(float) $paginator->getTotalItems(),
61+
);
62+
}
63+
64+
return $resources;
65+
}
66+
}

src/FieldHolder/Place/Infrastructure/Doctrine/DoctrinePlaceRepository.php

+21
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,25 @@ public function withWikidataIds(?array $wikidataIds): static
9292
->setParameter('valueWikidataIds', $wikidataIds);
9393
});
9494
}
95+
96+
public function withParentCommunityId(?Uuid $parentId): static
97+
{
98+
if (!$parentId) {
99+
return $this;
100+
}
101+
102+
return
103+
$this->filter(static function (QueryBuilder $qb) use ($parentId): void {
104+
$qb->andWhere("
105+
EXISTS (
106+
SELECT 1 FROM App\Field\Domain\Model\Field f_community_parent_id
107+
JOIN f_community_parent_id.communitiesVal communities
108+
WHERE f_community_parent_id.place = place AND
109+
f_community_parent_id.name = 'parentCommunities' AND
110+
communities.id = :valueParentCommunity
111+
)
112+
")
113+
->setParameter('valueParentCommunity', $parentId->toBinary());
114+
});
115+
}
95116
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php
2+
3+
namespace App\Tests\FieldHolder\Place\Acceptance;
4+
5+
use App\Field\Domain\Enum\FieldCommunity;
6+
use App\Field\Domain\Enum\FieldPlace;
7+
use App\Field\Domain\Model\Field;
8+
use App\FieldHolder\Community\Domain\Enum\CommunityType;
9+
use App\Tests\Field\DummyFactory\DummyFieldFactory;
10+
use App\Tests\FieldHolder\Community\DummyFactory\DummyCommunityFactory;
11+
use App\Tests\FieldHolder\Place\DummyFactory\DummyPlaceFactory;
12+
use App\Tests\Helper\AcceptanceTestHelper;
13+
use Doctrine\Common\Collections\ArrayCollection;
14+
use Symfony\Component\HttpFoundation\Response as HttpFoundationResponse;
15+
use Zenstruck\Foundry\Test\Factories;
16+
17+
class GetPlacesTest extends AcceptanceTestHelper
18+
{
19+
use Factories;
20+
21+
protected function setUp(): void
22+
{
23+
parent::setUp();
24+
}
25+
26+
public function testFilterByParentCommunityId(): void
27+
{
28+
[$community1, $community2] = DummyCommunityFactory::createMany(2,
29+
[
30+
'fields' => [
31+
DummyFieldFactory::createOne([
32+
'name' => FieldCommunity::TYPE->value,
33+
Field::getPropertyName(FieldCommunity::TYPE) => CommunityType::PARISH->value,
34+
]),
35+
],
36+
]
37+
);
38+
39+
$church1 = DummyPlaceFactory::createOne([
40+
'fields' => [
41+
DummyFieldFactory::createOne([
42+
'name' => FieldPlace::PARENT_COMMUNITIES->value,
43+
Field::getPropertyName(FieldPlace::PARENT_COMMUNITIES) => new ArrayCollection([$community1->_real()]),
44+
]),
45+
],
46+
]);
47+
48+
$church2 = DummyPlaceFactory::createOne([
49+
'fields' => [
50+
DummyFieldFactory::createOne([
51+
'name' => FieldPlace::PARENT_COMMUNITIES->value,
52+
Field::getPropertyName(FieldPlace::PARENT_COMMUNITIES) => new ArrayCollection([$community1->_real()]),
53+
]),
54+
],
55+
]);
56+
57+
$church3 = DummyPlaceFactory::createOne([
58+
'fields' => [
59+
DummyFieldFactory::createOne([
60+
'name' => FieldPlace::PARENT_COMMUNITIES->value,
61+
Field::getPropertyName(FieldPlace::PARENT_COMMUNITIES) => new ArrayCollection([$community2->_real()]),
62+
]),
63+
],
64+
]);
65+
66+
$church4 = DummyPlaceFactory::createOne([
67+
'fields' => [
68+
DummyFieldFactory::createOne([
69+
'name' => FieldPlace::PARENT_COMMUNITIES->value,
70+
Field::getPropertyName(FieldPlace::PARENT_COMMUNITIES) => new ArrayCollection([$community2->_real()]),
71+
]),
72+
],
73+
]);
74+
75+
$response = self::assertResponse($this->get('/places', querystring: [
76+
FieldCommunity::PARENT_COMMUNITY_ID->value => $community1->id->toString(),
77+
]), HttpFoundationResponse::HTTP_OK);
78+
$churchIds = array_map(fn (array $church) => $church['id'], $response);
79+
self::assertCount(2, $churchIds);
80+
self::assertContains($church1->id->toString(), $churchIds);
81+
self::assertContains($church2->id->toString(), $churchIds);
82+
83+
$response = self::assertResponse($this->get('/places', querystring: [
84+
FieldCommunity::PARENT_COMMUNITY_ID->value => $community2->id->toString(),
85+
]), HttpFoundationResponse::HTTP_OK);
86+
$churchIds = array_map(fn (array $church) => $church['id'], $response);
87+
self::assertCount(2, $churchIds);
88+
self::assertContains($church3->id->toString(), $churchIds);
89+
self::assertContains($church4->id->toString(), $churchIds);
90+
}
91+
92+
public function testShouldErrorIfParentCommunityIdNotAUuid(): void
93+
{
94+
$response = self::assertErrorResponse(
95+
$this->get('/places', querystring: [
96+
FieldCommunity::PARENT_COMMUNITY_ID->value => 123,
97+
]),
98+
HttpFoundationResponse::HTTP_BAD_REQUEST,
99+
sprintf('provided parentCommunityId %s is not a valid uuid', 123)
100+
);
101+
}
102+
}

0 commit comments

Comments
 (0)