Skip to content

Commit 2cffbe4

Browse files
authored
Merge pull request #30 from maltehuebner/fix/entity-not-found-404
Throw 404 EntityNotFoundException when query entity cannot be resolved
2 parents 3be287e + de407f5 commit 2cffbe4

6 files changed

Lines changed: 249 additions & 2 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MalteHuebner\DataQueryBundle\Exception;
4+
5+
use Symfony\Component\HttpFoundation\Response;
6+
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
7+
8+
class EntityNotFoundException extends DataQueryException implements HttpExceptionInterface
9+
{
10+
public function __construct(string $parameterName, string $parameterValue)
11+
{
12+
$message = sprintf('Could not find entity for query parameter "%s" with value "%s"', $parameterName, $parameterValue);
13+
14+
parent::__construct($message);
15+
}
16+
17+
public function getStatusCode(): int
18+
{
19+
return Response::HTTP_NOT_FOUND;
20+
}
21+
22+
public function getHeaders(): array
23+
{
24+
return [];
25+
}
26+
}

src/Factory/ValueAssigner/ValueAssigner.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace MalteHuebner\DataQueryBundle\Factory\ValueAssigner;
44

55
use Doctrine\Persistence\ManagerRegistry;
6+
use MalteHuebner\DataQueryBundle\Exception\EntityNotFoundException;
67
use MalteHuebner\DataQueryBundle\FieldList\ParameterFieldList\ParameterField;
78
use MalteHuebner\DataQueryBundle\FieldList\QueryFieldList\QueryField;
89
use MalteHuebner\DataQueryBundle\Parameter\ParameterInterface;
@@ -117,13 +118,13 @@ protected function assignEntityValueFromRepository(RequestParameterList $request
117118

118119
$entity = $repository->$methodName($queryParameterValue);
119120

120-
if ($queryField->getAccessor()) {
121+
if (null !== $entity && $queryField->getAccessor()) {
121122
$accessMethodName = $queryField->getAccessor();
122123
$entity = $entity->$accessMethodName();
123124
}
124125

125126
if (null === $entity) {
126-
return $query;
127+
throw new EntityNotFoundException($parameterName, (string) $queryParameterValue);
127128
}
128129

129130
$setMethodName = $queryField->getMethodName();

tests/Factory/ValueAssigner/ValueAssignerTest.php

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace MalteHuebner\DataQueryBundle\Tests\Factory\ValueAssigner;
44

55
use Doctrine\Persistence\ManagerRegistry;
6+
use Doctrine\Persistence\ObjectRepository;
7+
use MalteHuebner\DataQueryBundle\Exception\EntityNotFoundException;
68
use MalteHuebner\DataQueryBundle\Factory\ValueAssigner\ValueAssigner;
79
use MalteHuebner\DataQueryBundle\Factory\ValueAssigner\ValueAssignerInterface;
810
use MalteHuebner\DataQueryBundle\Factory\ValueAssigner\ValueType;
@@ -11,6 +13,10 @@
1113
use MalteHuebner\DataQueryBundle\Query\BoundingBoxQuery;
1214
use MalteHuebner\DataQueryBundle\Parameter\SizeParameter;
1315
use MalteHuebner\DataQueryBundle\RequestParameterList\RequestParameterList;
16+
use MalteHuebner\DataQueryBundle\Tests\Fixtures\SimpleEntity;
17+
use MalteHuebner\DataQueryBundle\Tests\Fixtures\SimpleEntityContainer;
18+
use MalteHuebner\DataQueryBundle\Tests\Fixtures\SimpleEntityContainerRepository;
19+
use MalteHuebner\DataQueryBundle\Tests\Fixtures\SimpleEntityQuery;
1420
use PHPUnit\Framework\TestCase;
1521

1622
class ValueAssignerTest extends TestCase
@@ -309,4 +315,130 @@ public function testConvertToIntTrimsWhitespaceForParameter(): void
309315

310316
$this->assertTrue(true);
311317
}
318+
319+
public function testAssignQueryEntityValueAssignsEntityFromRepository(): void
320+
{
321+
$entity = new SimpleEntity();
322+
323+
$repository = $this->createMock(ObjectRepository::class);
324+
$repository->method('find')->with('42')->willReturn($entity);
325+
326+
$this->managerRegistry->method('getRepository')->with(SimpleEntity::class)->willReturn($repository);
327+
328+
$list = new RequestParameterList();
329+
$list->add('simpleEntityId', '42');
330+
331+
$query = new SimpleEntityQuery();
332+
333+
$queryField = new QueryField();
334+
$queryField
335+
->setMethodName('setSimpleEntity')
336+
->setParameterName('simpleEntityId')
337+
->setType(SimpleEntity::class);
338+
339+
$this->valueAssigner->assignQueryPropertyValueFromRequest($list, $query, $queryField);
340+
341+
$this->assertSame($entity, $query->getSimpleEntity());
342+
}
343+
344+
public function testAssignQueryEntityValueThrowsWhenEntityNotFound(): void
345+
{
346+
$repository = $this->createMock(ObjectRepository::class);
347+
$repository->method('find')->willReturn(null);
348+
349+
$this->managerRegistry->method('getRepository')->willReturn($repository);
350+
351+
$list = new RequestParameterList();
352+
$list->add('simpleEntityId', '42');
353+
354+
$query = new SimpleEntityQuery();
355+
356+
$queryField = new QueryField();
357+
$queryField
358+
->setMethodName('setSimpleEntity')
359+
->setParameterName('simpleEntityId')
360+
->setType(SimpleEntity::class);
361+
362+
$this->expectException(EntityNotFoundException::class);
363+
$this->expectExceptionMessage('Could not find entity for query parameter "simpleEntityId" with value "42"');
364+
365+
$this->valueAssigner->assignQueryPropertyValueFromRequest($list, $query, $queryField);
366+
}
367+
368+
public function testAssignQueryEntityValueWithAccessorAssignsAccessedEntity(): void
369+
{
370+
$entity = new SimpleEntity();
371+
$repository = new SimpleEntityContainerRepository(new SimpleEntityContainer($entity));
372+
373+
$this->managerRegistry->method('getRepository')->with(SimpleEntityContainer::class)->willReturn($repository);
374+
375+
$list = new RequestParameterList();
376+
$list->add('simpleEntitySlug', 'some-slug');
377+
378+
$query = new SimpleEntityQuery();
379+
380+
$queryField = new QueryField();
381+
$queryField
382+
->setMethodName('setSimpleEntity')
383+
->setParameterName('simpleEntitySlug')
384+
->setType(SimpleEntity::class)
385+
->setRepository(SimpleEntityContainer::class)
386+
->setRepositoryMethod('findOneBySlug')
387+
->setAccessor('getSimpleEntity');
388+
389+
$this->valueAssigner->assignQueryPropertyValueFromRequest($list, $query, $queryField);
390+
391+
$this->assertSame($entity, $query->getSimpleEntity());
392+
}
393+
394+
public function testAssignQueryEntityValueWithAccessorThrowsWhenLookupReturnsNull(): void
395+
{
396+
$repository = new SimpleEntityContainerRepository(null);
397+
398+
$this->managerRegistry->method('getRepository')->willReturn($repository);
399+
400+
$list = new RequestParameterList();
401+
$list->add('simpleEntitySlug', 'unknown-slug');
402+
403+
$query = new SimpleEntityQuery();
404+
405+
$queryField = new QueryField();
406+
$queryField
407+
->setMethodName('setSimpleEntity')
408+
->setParameterName('simpleEntitySlug')
409+
->setType(SimpleEntity::class)
410+
->setRepository(SimpleEntityContainer::class)
411+
->setRepositoryMethod('findOneBySlug')
412+
->setAccessor('getSimpleEntity');
413+
414+
$this->expectException(EntityNotFoundException::class);
415+
$this->expectExceptionMessage('Could not find entity for query parameter "simpleEntitySlug" with value "unknown-slug"');
416+
417+
$this->valueAssigner->assignQueryPropertyValueFromRequest($list, $query, $queryField);
418+
}
419+
420+
public function testAssignQueryEntityValueThrowsWhenAccessorReturnsNull(): void
421+
{
422+
$repository = new SimpleEntityContainerRepository(new SimpleEntityContainer(null));
423+
424+
$this->managerRegistry->method('getRepository')->willReturn($repository);
425+
426+
$list = new RequestParameterList();
427+
$list->add('simpleEntitySlug', 'some-slug');
428+
429+
$query = new SimpleEntityQuery();
430+
431+
$queryField = new QueryField();
432+
$queryField
433+
->setMethodName('setSimpleEntity')
434+
->setParameterName('simpleEntitySlug')
435+
->setType(SimpleEntity::class)
436+
->setRepository(SimpleEntityContainer::class)
437+
->setRepositoryMethod('findOneBySlug')
438+
->setAccessor('getSimpleEntity');
439+
440+
$this->expectException(EntityNotFoundException::class);
441+
442+
$this->valueAssigner->assignQueryPropertyValueFromRequest($list, $query, $queryField);
443+
}
312444
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MalteHuebner\DataQueryBundle\Tests\Fixtures;
4+
5+
class SimpleEntityContainer
6+
{
7+
public function __construct(
8+
private readonly ?SimpleEntity $simpleEntity = null
9+
)
10+
{
11+
12+
}
13+
14+
public function getSimpleEntity(): ?SimpleEntity
15+
{
16+
return $this->simpleEntity;
17+
}
18+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MalteHuebner\DataQueryBundle\Tests\Fixtures;
4+
5+
use Doctrine\Persistence\ObjectRepository;
6+
7+
/**
8+
* @implements ObjectRepository<SimpleEntityContainer>
9+
*/
10+
class SimpleEntityContainerRepository implements ObjectRepository
11+
{
12+
public function __construct(
13+
private readonly ?SimpleEntityContainer $simpleEntityContainer = null
14+
)
15+
{
16+
17+
}
18+
19+
public function findOneBySlug(string $slug): ?SimpleEntityContainer
20+
{
21+
return $this->simpleEntityContainer;
22+
}
23+
24+
public function find(mixed $id): ?object
25+
{
26+
return null;
27+
}
28+
29+
public function findAll(): array
30+
{
31+
return [];
32+
}
33+
34+
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
35+
{
36+
return [];
37+
}
38+
39+
public function findOneBy(array $criteria): ?object
40+
{
41+
return null;
42+
}
43+
44+
public function getClassName(): string
45+
{
46+
return SimpleEntityContainer::class;
47+
}
48+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MalteHuebner\DataQueryBundle\Tests\Fixtures;
4+
5+
use MalteHuebner\DataQueryBundle\Query\AbstractQuery;
6+
7+
class SimpleEntityQuery extends AbstractQuery
8+
{
9+
private ?SimpleEntity $simpleEntity = null;
10+
11+
public function setSimpleEntity(SimpleEntity $simpleEntity): self
12+
{
13+
$this->simpleEntity = $simpleEntity;
14+
15+
return $this;
16+
}
17+
18+
public function getSimpleEntity(): ?SimpleEntity
19+
{
20+
return $this->simpleEntity;
21+
}
22+
}

0 commit comments

Comments
 (0)