From c278afc0df13b06e882a11caf8682a2e8332394e Mon Sep 17 00:00:00 2001 From: Hajar RAHMOUNI Date: Wed, 27 Dec 2023 20:13:13 +0100 Subject: [PATCH 1/3] bar chart data --- api/config/routes.yaml | 9 ++++ api/src/Controller/BarChartController.php | 58 +++++++++++++++++++++ api/src/Dto/BarChart/BarChartInput.php | 8 +++ api/src/Dto/BarChart/BarChartOutput.php | 15 ++++++ api/src/Entity/Sale.php | 18 +++++-- api/src/Service/BarChartService.php | 61 +++++++++++++++++++++++ 6 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 api/src/Controller/BarChartController.php create mode 100644 api/src/Dto/BarChart/BarChartInput.php create mode 100644 api/src/Dto/BarChart/BarChartOutput.php create mode 100644 api/src/Service/BarChartService.php diff --git a/api/config/routes.yaml b/api/config/routes.yaml index 41ef814..031ef7c 100644 --- a/api/config/routes.yaml +++ b/api/config/routes.yaml @@ -3,3 +3,12 @@ controllers: path: ../src/Controller/ namespace: App\Controller type: attribute + +bar_chart_route: + path: '/sales/bar-chart/{startDate}/{endDate}/{granularity}' + controller: 'BarChartController::getChartData' + methods: ['GET'] + requirements: + startDate: '\d{4}-\d{2}-\d{2}' + endDate: '\d{4}-\d{2}-\d{2}' + granularity: 'day|month|year' diff --git a/api/src/Controller/BarChartController.php b/api/src/Controller/BarChartController.php new file mode 100644 index 0000000..f1d87c1 --- /dev/null +++ b/api/src/Controller/BarChartController.php @@ -0,0 +1,58 @@ +chartService = $chartService; + } + + /* #[Route('/sales/bar-chart/{startDate}/{endDate}/{granularity}', name: 'get_sales_for_bar_chart', requirements: [ + 'startDate' => '\d{4}-\d{2}-\d{2}', // Matches YYYY-MM-DD + 'endDate' => '\d{4}-\d{2}-\d{2}', // Matches YYYY-MM-DD + 'granularity' => 'day|month|year', // Matches day, month, or year + ])]*/ + public function getChartData(Request $request): JsonResponse + { + // Utilisez la méthode getContent directement pour obtenir le contenu JSON + $requestData = json_decode($request->getContent(), true); + + // Vérifiez si les clés correctes sont présentes + if (!isset($requestData['startDate'], $requestData['endDate'], $requestData['granularity'])) { + // Retournez une réponse d'erreur appropriée si des données sont manquantes + return $this->json(['error' => 'Invalid request data. Make sure to provide startDate, endDate, and granularity.'], 400); + } + + // Validate and transform the input data into a BarChartInput object. + $input = new BarChartInput(); + $input->start = $requestData['startDate']; + $input->end = $requestData['endDate']; + $input->granularity = $requestData['granularity']; + + // Call the service to get the chart data. + $chartData = $this->chartService->getBarChartData($input); + + // Transform the results into the desired output format. + $output = []; + foreach ($chartData as $data) { + $output[] = [ + 'date' => $data->date, + 'occurrences' => $data->occurrences, + ]; + } + return $this->json($output); + } + +} + diff --git a/api/src/Dto/BarChart/BarChartInput.php b/api/src/Dto/BarChart/BarChartInput.php new file mode 100644 index 0000000..becb0bb --- /dev/null +++ b/api/src/Dto/BarChart/BarChartInput.php @@ -0,0 +1,8 @@ +date = $date; + $this->occurrences = $occurrences; + } +} diff --git a/api/src/Entity/Sale.php b/api/src/Entity/Sale.php index 9ceb2c1..0524164 100644 --- a/api/src/Entity/Sale.php +++ b/api/src/Entity/Sale.php @@ -4,11 +4,21 @@ namespace App\Entity; use ApiPlatform\Metadata\ApiResource; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Validator\Constraints as Assert; - -/** A sale. */ +use ApiPlatform\Metadata\Get; +use App\Controller\BarChartController; + + +#[ApiResource( + operations: [ + new Get(), + new Get( + name: 'bar-chart', + uriTemplate: '/sales/bar-chart/{startDate}/{endDate}/{granularity}', + controller: BarChartController::class + ) + ] +)] #[ORM\Entity] -#[ApiResource] class Sale { diff --git a/api/src/Service/BarChartService.php b/api/src/Service/BarChartService.php new file mode 100644 index 0000000..5a982d8 --- /dev/null +++ b/api/src/Service/BarChartService.php @@ -0,0 +1,61 @@ +entityManager = $entityManager; + } + + public function getBarChartData(BarChartInput $input): array + { + $startDate = new \DateTimeImmutable($input->start); + $endDate = new \DateTimeImmutable($input->end); + echo "================================================================="; + $queryBuilder = $this->entityManager->createQueryBuilder(); + + // Sélectionnez la date avec le format approprié en fonction de la granularité + switch ($input->granularity) { + case 'day': + $selectExpression = 'DATE_FORMAT(s.date, \'%Y-%m-%d\')'; + $groupByExpression = 's.date'; + break; + case 'month': + $selectExpression = 'DATE_FORMAT(s.date, \'%Y-%m\')'; + $groupByExpression = 'DATE_FORMAT(s.date, \'%Y-%m\')'; + break; + case 'year': + $selectExpression = 'DATE_FORMAT(s.date, \'%Y\')'; + $groupByExpression = 'DATE_FORMAT(s.date, \'%Y\')'; + break; + default: + throw new \InvalidArgumentException('Invalid granularity'); + } + + $result = $queryBuilder + ->select($selectExpression . ' as date', 'COUNT(s.id) as occurrences') + ->from(Sale::class, 's') + ->where('s.date BETWEEN :start AND :end') + ->setParameter('start', $startDate) + ->setParameter('end', $endDate) + ->groupBy($groupByExpression) + ->orderBy('date') + ->getQuery() + ->getResult(); + + $output = []; + foreach ($result as $row) { + $output[] = new BarChartOutput($row['date'], (int)$row['occurrences']); + } + + return $output; + } +} -- GitLab From 452850ff1f13554504c619cb0927c77edb372722 Mon Sep 17 00:00:00 2001 From: Hajar RAHMOUNI Date: Thu, 28 Dec 2023 22:04:30 +0100 Subject: [PATCH 2/3] get bar chart data group by date --- api/composer.json | 1 + api/composer.lock | 65 ++++++++++++++++++++++- api/config/packages/doctrine.yaml | 24 +++++++++ api/config/services.yaml | 1 + api/public/index.php | 1 + api/src/Controller/BarChartController.php | 35 ++++-------- api/src/Entity/Sale.php | 14 +++-- api/src/Service/BarChartService.php | 32 ++++++----- 8 files changed, 128 insertions(+), 45 deletions(-) diff --git a/api/composer.json b/api/composer.json index 8eed869..eb987c7 100644 --- a/api/composer.json +++ b/api/composer.json @@ -6,6 +6,7 @@ "ext-ctype": "*", "ext-iconv": "*", "api-platform/core": "^3.2", + "beberlei/doctrineextensions": "dev-master", "doctrine/doctrine-bundle": "^2.7", "doctrine/doctrine-migrations-bundle": "^3.2", "doctrine/orm": "^2.12", diff --git a/api/composer.lock b/api/composer.lock index 4fc11b5..0663f0e 100644 --- a/api/composer.lock +++ b/api/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "934976d853cfcd0af63e3e1658634266", + "content-hash": "bd6b6c41a9c7342b985895c36f7ab96c", "packages": [ { "name": "api-platform/core", @@ -169,6 +169,65 @@ }, "time": "2023-11-30T13:51:25+00:00" }, + { + "name": "beberlei/doctrineextensions", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/beberlei/DoctrineExtensions.git", + "reference": "67f32c184e80085e170b6e4e8f4f94bc92394c72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beberlei/DoctrineExtensions/zipball/67f32c184e80085e170b6e4e8f4f94bc92394c72", + "reference": "67f32c184e80085e170b6e4e8f4f94bc92394c72", + "shasum": "" + }, + "require": { + "doctrine/orm": "^2.7", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/cache": "^1.11", + "friendsofphp/php-cs-fixer": "^2.14", + "nesbot/carbon": "*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "symfony/yaml": "^4.4 || ^5.3 || ^6.0", + "zf1/zend-date": "^1.12", + "zf1/zend-registry": "^1.12" + }, + "default-branch": true, + "type": "library", + "autoload": { + "psr-4": { + "DoctrineExtensions\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Steve Lacey", + "email": "steve@steve.ly" + } + ], + "description": "A set of extensions to Doctrine 2 that add support for additional query functions available in MySQL, Oracle, PostgreSQL and SQLite.", + "keywords": [ + "database", + "doctrine", + "orm" + ], + "support": { + "source": "https://github.com/beberlei/DoctrineExtensions/tree/master" + }, + "time": "2023-02-25T19:57:36+00:00" + }, { "name": "doctrine/cache", "version": "2.2.0", @@ -8207,7 +8266,9 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "beberlei/doctrineextensions": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/api/config/packages/doctrine.yaml b/api/config/packages/doctrine.yaml index 559641f..af40e7a 100644 --- a/api/config/packages/doctrine.yaml +++ b/api/config/packages/doctrine.yaml @@ -21,6 +21,30 @@ doctrine: dir: '%kernel.project_dir%/src/Entity' prefix: 'App\Entity' alias: App + dql: + datetime_functions: + second: DoctrineExtensions\Query\Postgresql\Second + minute: DoctrineExtensions\Query\Postgresql\Minute + hour: DoctrineExtensions\Query\Postgresql\Hour + day: DoctrineExtensions\Query\Postgresql\Day + month: DoctrineExtensions\Query\Postgresql\Month + year: DoctrineExtensions\Query\Postgresql\Year + date_format: DoctrineExtensions\Query\Postgresql\DateFormat + at_time_zone: DoctrineExtensions\Query\Postgresql\AtTimeZoneFunction + date_part: DoctrineExtensions\Query\Postgresql\DatePart + extract: DoctrineExtensions\Query\Postgresql\ExtractFunction + date_trunc: DoctrineExtensions\Query\Postgresql\DateTrunc + date: DoctrineExtensions\Query\Postgresql\Date + + string_functions: + str_to_date: DoctrineExtensions\Query\Postgresql\StrToDate + count_filter: DoctrineExtensions\Query\Postgresql\CountFilterFunction + string_agg: DoctrineExtensions\Query\Postgresql\StringAgg + greatest: DoctrineExtensions\Query\Postgresql\Greatest + least: DoctrineExtensions\Query\Postgresql\Least + regexp_replace: DoctrineExtensions\Query\Postgresql\RegexpReplace + + when@test: doctrine: diff --git a/api/config/services.yaml b/api/config/services.yaml index 2d6a76f..e8a165f 100644 --- a/api/config/services.yaml +++ b/api/config/services.yaml @@ -6,6 +6,7 @@ parameters: services: + DoctrineExtensions\Query\Mysql\DateFormat: '@doctrine_extensions.query_mysql.date_format' # default configuration for services in *this* file _defaults: autowire: true # Automatically injects dependencies in your services. diff --git a/api/public/index.php b/api/public/index.php index 9982c21..870bf7c 100644 --- a/api/public/index.php +++ b/api/public/index.php @@ -5,5 +5,6 @@ use App\Kernel; require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return function (array $context) { + ini_set('memory_limit', '8192M'); return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); }; diff --git a/api/src/Controller/BarChartController.php b/api/src/Controller/BarChartController.php index f1d87c1..4b62fa4 100644 --- a/api/src/Controller/BarChartController.php +++ b/api/src/Controller/BarChartController.php @@ -2,7 +2,7 @@ namespace App\Controller; -use App\Dto\BarChartInput; +use App\Dto\BarChart\BarChartInput; use App\Service\BarChartService; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; @@ -18,32 +18,19 @@ class BarChartController extends AbstractController $this->chartService = $chartService; } - /* #[Route('/sales/bar-chart/{startDate}/{endDate}/{granularity}', name: 'get_sales_for_bar_chart', requirements: [ - 'startDate' => '\d{4}-\d{2}-\d{2}', // Matches YYYY-MM-DD - 'endDate' => '\d{4}-\d{2}-\d{2}', // Matches YYYY-MM-DD - 'granularity' => 'day|month|year', // Matches day, month, or year - ])]*/ - public function getChartData(Request $request): JsonResponse + #[Route('/sales/bar-chart/{startDate}/{endDate}/{granularity}', name: 'bar-chart', requirements: [ + 'startDate' => '\d{4}-\d{2}-\d{2}', // YYYY-MM-DD + 'endDate' => '\d{4}-\d{2}-\d{2}', // YYYY-MM-DD + 'granularity' => 'day|month|year', // day, month, ou year + ])] + public function getChartData(string $startDate, string $endDate, string $granularity): JsonResponse { - // Utilisez la méthode getContent directement pour obtenir le contenu JSON - $requestData = json_decode($request->getContent(), true); - - // Vérifiez si les clés correctes sont présentes - if (!isset($requestData['startDate'], $requestData['endDate'], $requestData['granularity'])) { - // Retournez une réponse d'erreur appropriée si des données sont manquantes - return $this->json(['error' => 'Invalid request data. Make sure to provide startDate, endDate, and granularity.'], 400); - } - - // Validate and transform the input data into a BarChartInput object. $input = new BarChartInput(); - $input->start = $requestData['startDate']; - $input->end = $requestData['endDate']; - $input->granularity = $requestData['granularity']; + $input->start = $startDate; + $input->end = $endDate; + $input->granularity = $granularity; - // Call the service to get the chart data. $chartData = $this->chartService->getBarChartData($input); - - // Transform the results into the desired output format. $output = []; foreach ($chartData as $data) { $output[] = [ @@ -53,6 +40,4 @@ class BarChartController extends AbstractController } return $this->json($output); } - } - diff --git a/api/src/Entity/Sale.php b/api/src/Entity/Sale.php index 0524164..309712a 100644 --- a/api/src/Entity/Sale.php +++ b/api/src/Entity/Sale.php @@ -5,19 +5,25 @@ namespace App\Entity; use ApiPlatform\Metadata\ApiResource; use Doctrine\ORM\Mapping as ORM; use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; use App\Controller\BarChartController; #[ApiResource( operations: [ - new Get(), - new Get( - name: 'bar-chart', + new GetCollection( uriTemplate: '/sales/bar-chart/{startDate}/{endDate}/{granularity}', - controller: BarChartController::class + requirements: [ + 'startDate' => '\d{4}-\d{2}-\d{2}', + 'endDate' => '\d{4}-\d{2}-\d{2}', + 'granularity' => 'day|month|year', + ], + controller: BarChartController::class, + name: 'bar-chart' ) ] )] + #[ORM\Entity] class Sale { diff --git a/api/src/Service/BarChartService.php b/api/src/Service/BarChartService.php index 5a982d8..e02c1d9 100644 --- a/api/src/Service/BarChartService.php +++ b/api/src/Service/BarChartService.php @@ -5,6 +5,7 @@ namespace App\Service; use App\Dto\BarChart\BarChartInput; use App\Dto\BarChart\BarChartOutput; use Doctrine\ORM\EntityManagerInterface; +use App\Entity\Sale; class BarChartService { @@ -12,50 +13,53 @@ class BarChartService public function __construct(EntityManagerInterface $entityManager) { + $this->entityManager = $entityManager; + $emConfig = $this->entityManager->getConfiguration(); + $emConfig->addCustomDatetimeFunction('YEAR', 'DoctrineExtensions\Query\Postgresql\Year'); + $emConfig->addCustomDatetimeFunction('MONTH', 'DoctrineExtensions\Query\Postgresql\Month'); + } public function getBarChartData(BarChartInput $input): array { - $startDate = new \DateTimeImmutable($input->start); - $endDate = new \DateTimeImmutable($input->end); - echo "================================================================="; + $startDate = new \DateTime($input->start); + $endDate = new \DateTime($input->end); + $queryBuilder = $this->entityManager->createQueryBuilder(); - // Sélectionnez la date avec le format approprié en fonction de la granularité switch ($input->granularity) { case 'day': - $selectExpression = 'DATE_FORMAT(s.date, \'%Y-%m-%d\')'; $groupByExpression = 's.date'; break; case 'month': - $selectExpression = 'DATE_FORMAT(s.date, \'%Y-%m\')'; - $groupByExpression = 'DATE_FORMAT(s.date, \'%Y-%m\')'; + $groupByExpression = 'MONTH(s.date)'; break; case 'year': - $selectExpression = 'DATE_FORMAT(s.date, \'%Y\')'; - $groupByExpression = 'DATE_FORMAT(s.date, \'%Y\')'; + $groupByExpression = 'YEAR(s.date)'; break; default: throw new \InvalidArgumentException('Invalid granularity'); } $result = $queryBuilder - ->select($selectExpression . ' as date', 'COUNT(s.id) as occurrences') + ->select("{$groupByExpression} as date", 'COUNT(s.id) as occurrences') ->from(Sale::class, 's') ->where('s.date BETWEEN :start AND :end') - ->setParameter('start', $startDate) - ->setParameter('end', $endDate) - ->groupBy($groupByExpression) + ->setParameter('start', $startDate->format('Y-m-d')) + ->setParameter('end', $endDate->format('Y-m-d')) + ->groupBy("{$groupByExpression}") ->orderBy('date') ->getQuery() ->getResult(); $output = []; foreach ($result as $row) { - $output[] = new BarChartOutput($row['date'], (int)$row['occurrences']); + $dateString = $row['date'] instanceof \DateTimeInterface ? $row['date']->format('Y-m-d') : $row['date']; + $output[] = new BarChartOutput($dateString, (int)$row['occurrences']); } return $output; } + } -- GitLab From 920618a1e61b404ef85c4f8506463ad3de013e98 Mon Sep 17 00:00:00 2001 From: Hajar RAHMOUNI Date: Sat, 30 Dec 2023 11:50:06 +0100 Subject: [PATCH 3/3] get number of sales per year and per month --- api/config/packages/doctrine.yaml | 8 -------- api/src/Service/BarChartService.php | 15 +++++++++++---- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/api/config/packages/doctrine.yaml b/api/config/packages/doctrine.yaml index af40e7a..7492f19 100644 --- a/api/config/packages/doctrine.yaml +++ b/api/config/packages/doctrine.yaml @@ -23,17 +23,9 @@ doctrine: alias: App dql: datetime_functions: - second: DoctrineExtensions\Query\Postgresql\Second - minute: DoctrineExtensions\Query\Postgresql\Minute - hour: DoctrineExtensions\Query\Postgresql\Hour - day: DoctrineExtensions\Query\Postgresql\Day month: DoctrineExtensions\Query\Postgresql\Month year: DoctrineExtensions\Query\Postgresql\Year date_format: DoctrineExtensions\Query\Postgresql\DateFormat - at_time_zone: DoctrineExtensions\Query\Postgresql\AtTimeZoneFunction - date_part: DoctrineExtensions\Query\Postgresql\DatePart - extract: DoctrineExtensions\Query\Postgresql\ExtractFunction - date_trunc: DoctrineExtensions\Query\Postgresql\DateTrunc date: DoctrineExtensions\Query\Postgresql\Date string_functions: diff --git a/api/src/Service/BarChartService.php b/api/src/Service/BarChartService.php index e02c1d9..efa52f8 100644 --- a/api/src/Service/BarChartService.php +++ b/api/src/Service/BarChartService.php @@ -18,6 +18,7 @@ class BarChartService $emConfig = $this->entityManager->getConfiguration(); $emConfig->addCustomDatetimeFunction('YEAR', 'DoctrineExtensions\Query\Postgresql\Year'); $emConfig->addCustomDatetimeFunction('MONTH', 'DoctrineExtensions\Query\Postgresql\Month'); + $emConfig->addCustomDatetimeFunction('DATE_FORMAT', 'DoctrineExtensions\Query\Postgresql\DateFormat'); } @@ -31,31 +32,37 @@ class BarChartService switch ($input->granularity) { case 'day': $groupByExpression = 's.date'; + $groupByAlias = 'date'; + $dateFormat = 'Y-m-d'; break; case 'month': $groupByExpression = 'MONTH(s.date)'; + $groupByAlias = 'month'; + $dateFormat = 'Y-m'; break; case 'year': $groupByExpression = 'YEAR(s.date)'; + $groupByAlias = 'year'; + $dateFormat = 'Y'; break; default: throw new \InvalidArgumentException('Invalid granularity'); } $result = $queryBuilder - ->select("{$groupByExpression} as date", 'COUNT(s.id) as occurrences') + ->select("{$groupByExpression} as {$groupByAlias}", 'COUNT(s.id) as occurrences') ->from(Sale::class, 's') ->where('s.date BETWEEN :start AND :end') ->setParameter('start', $startDate->format('Y-m-d')) ->setParameter('end', $endDate->format('Y-m-d')) - ->groupBy("{$groupByExpression}") - ->orderBy('date') + ->groupBy("{$groupByAlias}") + ->orderBy("{$groupByAlias}") ->getQuery() ->getResult(); $output = []; foreach ($result as $row) { - $dateString = $row['date'] instanceof \DateTimeInterface ? $row['date']->format('Y-m-d') : $row['date']; + $dateString = $row[$groupByAlias] instanceof \DateTimeInterface ? $row[$groupByAlias]->format($dateFormat) : $row[$groupByAlias]; $output[] = new BarChartOutput($dateString, (int)$row['occurrences']); } -- GitLab