Skip to content

Commit

Permalink
2nd example for camp specific URL (deeply nested entity)
Browse files Browse the repository at this point in the history
  • Loading branch information
usu committed Jul 18, 2023
1 parent 3dbbd5d commit 2353348
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 17 deletions.
8 changes: 7 additions & 1 deletion api/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,19 @@ services:
- { name: doctrine.event_listener, event: onFlush }
- { name: doctrine.event_listener, event: postFlush }

# Entity Filter
# Entity Filters
App\Doctrine\FilterByCurrentUserExtension:
tags:
# FilterEagerLoadingExtension has Priority -17 and breaks the DQL generated in ContentNodeRepository => Priority -20 ensures this runs after FilterEagerLoadingExtension
- { name: api_platform.doctrine.orm.query_extension.collection, priority: -20 }
- { name: api_platform.doctrine.orm.query_extension.item }

App\Doctrine\FilterByCampExtension:
tags:
- { name: api_platform.doctrine.orm.query_extension.collection, priority: -19 }
- { name: api_platform.doctrine.orm.query_extension.item }


App\Metadata\Resource\Factory\UriTemplateFactory:
arguments:
- '@api_platform.filter_locator'
Expand Down
45 changes: 45 additions & 0 deletions api/src/Doctrine/FilterByCampExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace App\Doctrine;

use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Operation;
use App\Repository\CanFilterByCampInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;

final class FilterByCampExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface {
private EntityManagerInterface $entityManager;

public function __construct(EntityManagerInterface $entityManager) {
$this->entityManager = $entityManager;
}

public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass = null, Operation $operation = null, array $context = []): void {
if (!isset($context['uri_variables']['campId'])) {
return;
}

$this->addWhere($queryBuilder, $queryNameGenerator, $resourceClass, $context['uri_variables']['campId']);
}

public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, Operation $operation = null, array $context = []): void {
if (!isset($context['uri_variables']['campId'])) {
return;
}

$this->addWhere($queryBuilder, $queryNameGenerator, $resourceClass, $context['uri_variables']['campId']);
}

private function addWhere(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $campId): void {
$repository = $this->entityManager->getRepository($resourceClass);

if (!($repository instanceof CanFilterByCampInterface) || null === $campId) {
return;
}

$repository->filterByCamp($queryBuilder, $queryNameGenerator, $campId);
}
}
24 changes: 10 additions & 14 deletions api/src/Entity/Category.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,25 +55,21 @@
normalizationContext: self::ITEM_NORMALIZATION_CONTEXT,
securityPostDenormalize: 'is_granted("CAMP_MEMBER", object) or is_granted("CAMP_MANAGER", object)'
),
new GetCollection(
security: 'is_authenticated()',
uriTemplate: '/camps/{campId}/categories.{_format}',
uriVariables: [
'campId' => new Link(
fromClass: Camp::class,
expandedValue: '{campId}'
),
],
),
],
denormalizationContext: ['groups' => ['write']],
normalizationContext: ['groups' => ['read']],
order: ['camp.id', 'short'],
)]
#[ApiResource(
uriTemplate: '/camps/{campId}/categories.{_format}',
uriVariables: [
'campId' => new Link(
fromClass: Camp::class,
fromProperty: 'categories'
),
],
operations: [
new GetCollection(
security: 'is_authenticated()',
),
]
)]
#[ApiFilter(filterClass: SearchFilter::class, properties: ['camp'])]
#[ORM\Entity(repositoryClass: CategoryRepository::class)]
class Category extends BaseEntity implements BelongsToCampInterface, CopyFromPrototypeInterface {
Expand Down
11 changes: 11 additions & 0 deletions api/src/Entity/ScheduleEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use App\Doctrine\Filter\ExpressionDateTimeFilter;
Expand Down Expand Up @@ -48,6 +49,16 @@
securityPostDenormalize: 'is_granted("CAMP_MEMBER", object) or is_granted("CAMP_MANAGER", object)',
validationContext: ['groups' => ScheduleEntryPostGroupSequence::class]
),
new GetCollection(
security: 'is_authenticated()',
uriTemplate: '/camps/{campId}/schedule_entries.{_format}',
uriVariables: [
'campId' => new Link(
fromClass: Camp::class,
expandedValue: '{campId}'
),
],
),
],
denormalizationContext: ['groups' => ['write']],
normalizationContext: ['groups' => ['read']]
Expand Down
10 changes: 10 additions & 0 deletions api/src/Repository/CanFilterByCampInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App\Repository;

use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Doctrine\ORM\QueryBuilder;

interface CanFilterByCampInterface {
public function filterByCamp(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $campId): void;
}
11 changes: 10 additions & 1 deletion api/src/Repository/CategoryRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Repository;

use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use App\Entity\Category;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
Expand All @@ -14,7 +15,7 @@
* @method Category[] findAll()
* @method Category[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class CategoryRepository extends ServiceEntityRepository implements CanFilterByUserInterface {
class CategoryRepository extends ServiceEntityRepository implements CanFilterByUserInterface, CanFilterByCampInterface {
use FiltersByCampCollaboration;

public function __construct(ManagerRegistry $registry) {
Expand All @@ -26,4 +27,12 @@ public function filterByUser(QueryBuilder $queryBuilder, User $user): void {
$queryBuilder->innerJoin("{$rootAlias}.camp", 'camp');
$this->filterByCampCollaboration($queryBuilder, $user);
}

public function filterByCamp(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $campId): void {
$rootAlias = $queryBuilder->getRootAliases()[0];
$queryBuilder->andWhere(
$queryBuilder->expr()->eq("{$rootAlias}.camp", ':camp')
);
$queryBuilder->setParameter('camp', $campId);
}
}
14 changes: 13 additions & 1 deletion api/src/Repository/ScheduleEntryRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Repository;

use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use App\Entity\ScheduleEntry;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
Expand All @@ -14,7 +15,7 @@
* @method ScheduleEntry[] findAll()
* @method ScheduleEntry[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ScheduleEntryRepository extends ServiceEntityRepository implements CanFilterByUserInterface {
class ScheduleEntryRepository extends ServiceEntityRepository implements CanFilterByUserInterface, CanFilterByCampInterface {
use FiltersByCampCollaboration;

public function __construct(ManagerRegistry $registry) {
Expand All @@ -39,4 +40,15 @@ public function filterByUser(QueryBuilder $queryBuilder, User $user): void {
$queryBuilder->innerJoin('activity.camp', 'camp');
$this->filterByCampCollaboration($queryBuilder, $user);
}

public function filterByCamp(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $campId): void {
$activityJoinAlias = $queryNameGenerator->generateJoinAlias('activity');

$rootAlias = $queryBuilder->getRootAliases()[0];
$queryBuilder->innerJoin("{$rootAlias}.activity", $activityJoinAlias);
$queryBuilder->andWhere(
$queryBuilder->expr()->eq("{$activityJoinAlias}.camp", ':camp')
);
$queryBuilder->setParameter('camp', $campId);
}
}

0 comments on commit 2353348

Please sign in to comment.