feat: gateway charges stats endpoint

This commit is contained in:
Pep Villalonga
2026-07-14 13:28:51 +02:00
parent 392fe38c8c
commit 9957302360
4 changed files with 93 additions and 0 deletions
+4
View File
@@ -95,6 +95,10 @@ services:
arguments:
- !tagged 'api_platform.doctrine.orm.query_extension.collection'
App\State\Gateway\ChargeStatsStateProvider:
arguments:
- !tagged 'api_platform.doctrine.orm.query_extension.collection'
App\Money\MoneyService:
arguments:
- '%env(CURRENCY_DEFAULT)%'
@@ -7,6 +7,7 @@ use ApiPlatform\Doctrine\Orm\State\Options;
use ApiPlatform\Metadata as API;
use App\ApiResource\Accounting\AccountingApiResource;
use App\ApiResource\MoneyOutput;
use App\Dto\Gateway\ChargeStats;
use App\Dto\Gateway\ChargeUpdationDto;
use App\Entity\Gateway\Charge;
use App\Filter\GatewayFilter;
@@ -14,6 +15,7 @@ use App\Gateway\ChargeStatus;
use App\Gateway\ChargeType;
use App\State\ApiResourceStateProvider;
use App\State\Gateway\ChargeStateProcessor;
use App\State\Gateway\ChargeStatsStateProvider;
use Symfony\Component\Validator\Constraints as Assert;
/**
@@ -26,6 +28,17 @@ use Symfony\Component\Validator\Constraints as Assert;
)]
#[API\Get()]
#[API\GetCollection()]
#[API\GetCollection(
uriTemplate: '/gateway_charges/stats',
priority: -1,
provider: ChargeStatsStateProvider::class,
output: ChargeStats::class,
paginationEnabled: false,
openapi: new \ApiPlatform\OpenApi\Model\Operation(
summary: 'Distinct project count for a charges filter set',
description: 'Returns the number of distinct projects targeted by charges matching the same filters as the charges collection.',
),
)]
#[API\Patch(
input: ChargeUpdationDto::class,
processor: ChargeStateProcessor::class,
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace App\Dto\Gateway;
/**
* Aggregate statistics for a filtered GatewayCharge collection.
*/
class ChargeStats
{
public function __construct(private int $distinctProjectCount) {}
/**
* The number of distinct Projects targeted by the charges matching the filter set.
*/
public function getDistinctProjectCount(): int
{
return $this->distinctProjectCount;
}
}
@@ -0,0 +1,57 @@
<?php
namespace App\State\Gateway;
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGenerator;
use ApiPlatform\Metadata\Exception\RuntimeException;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Dto\Gateway\ChargeStats;
use App\Entity\Gateway\Charge;
use Doctrine\Persistence\ManagerRegistry;
/**
* Provides the number of distinct Projects targeted by a filtered GatewayCharge collection.
*
* Reuses the same ApiFilters declared on the GatewayCharge collection (target, status,
* type, money range, dates, ...) by re-applying every registered ORM collection
* extension, then runs a single COUNT(DISTINCT project) query.
*/
class ChargeStatsStateProvider implements ProviderInterface
{
/**
* @param QueryCollectionExtensionInterface[] $collectionExtensions
*/
public function __construct(
private readonly iterable $collectionExtensions,
private ManagerRegistry $managerRegistry,
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): ChargeStats
{
$manager = $this->managerRegistry->getManagerForClass(Charge::class);
$repository = $manager->getRepository(Charge::class);
if (!method_exists($repository, 'createQueryBuilder')) {
throw new RuntimeException('The repository class must have a "createQueryBuilder" method.');
}
$queryBuilder = $repository->createQueryBuilder('o');
$queryNameGenerator = new QueryNameGenerator();
foreach ($this->collectionExtensions as $extension) {
$extension->applyToCollection($queryBuilder, $queryNameGenerator, Charge::class, $operation, $context);
}
$count = (int) $queryBuilder
->resetDQLPart('orderBy')
->join('o.target', 'stats_target')
->join('stats_target.project', 'stats_project')
->select('COUNT(DISTINCT stats_project.id)')
->getQuery()
->getSingleScalarResult();
return new ChargeStats($count);
}
}