src/Dao/OfferCode/PurchaseCountWithCorrectionDao.php line 123

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Slivki\Dao\OfferCode;
  4. use DateTimeImmutable;
  5. use Doctrine\DBAL\Connection;
  6. use Doctrine\DBAL\Query\QueryBuilder;
  7. use League\Period\Period;
  8. use Slivki\Dto\Statistic\Conversion\ConversionByOfferDto;
  9. use Slivki\Entity\Visit;
  10. use Slivki\Enum\OfferCode\PurchaseCountPeriod;
  11. use Slivki\Factory\Statistic\Conversion\ConversionByOfferDtoFactory;
  12. use function sprintf;
  13. use function array_map;
  14. use function array_column;
  15. final class PurchaseCountWithCorrectionDao implements PurchaseCountWithCorrectionDaoInterface
  16. {
  17.     private const COUNT_CODES_FROM_SAME_SUBSCRIPTION 0;
  18.     private Connection $connection;
  19.     private ConversionByOfferDtoFactory $conversionByOfferDtoFactory;
  20.     private CodePoolDaoInterface $codePoolDao;
  21.     public function __construct(
  22.         Connection $connection,
  23.         ConversionByOfferDtoFactory $conversionByOfferDtoFactory,
  24.         CodePoolDaoInterface $codePoolDao
  25.     ) {
  26.         $this->connection $connection;
  27.         $this->conversionByOfferDtoFactory $conversionByOfferDtoFactory;
  28.         $this->codePoolDao $codePoolDao;
  29.     }
  30.     public function findByPeriod(Period $period): array
  31.     {
  32.         $purchasesByPeriod $this->getByPeriodQueryBuilder($period)
  33.             ->execute()
  34.             ->fetchAllAssociative();
  35.         return array_column($purchasesByPeriod'codes_count''offer_id');
  36.     }
  37.     public function findTotal(): array
  38.     {
  39.         $queryBuilder $this->connection->createQueryBuilder();
  40.         $purchases =  $queryBuilder
  41.             ->addSelect([
  42.                 'offer_codes.offer_id',
  43.                 sprintf(
  44.                     'sum (case %s %s %s end) as codes_count',
  45.                     'when offer_codes.subscription_period_start_at is null then offer_codes.codes_count',
  46.                     'when offer_codes.codes_count > :codesFromSameSubscription then :codesFromSameSubscription',
  47.                     'else offer_codes.codes_count',
  48.                 )
  49.             ])
  50.             ->from(
  51.                 sprintf('(%s)'$this->getCodesCountQueryBuilder()->getSQL()),
  52.                 'offer_codes'
  53.             )
  54.             ->addGroupBy('offer_codes.offer_id')
  55.             ->setParameter('codesFromSameSubscription'self::COUNT_CODES_FROM_SAME_SUBSCRIPTION)
  56.             ->execute()
  57.             ->fetchAllAssociative();
  58.         return array_column($purchases'codes_count''offer_id');
  59.     }
  60.     public function findTotalByOfferId(int $offerId): int
  61.     {
  62.         $queryBuilder $this->connection->createQueryBuilder();
  63.         $expr $queryBuilder->expr();
  64.         $purchases $queryBuilder
  65.             ->addSelect([
  66.                 sprintf(
  67.                     'sum (case %s %s %s end) as codes_count',
  68.                     'when offer_codes.subscription_period_start_at is null then offer_codes.codes_count',
  69.                     'when offer_codes.codes_count > :codesFromSameSubscription then :codesFromSameSubscription',
  70.                     'else offer_codes.codes_count',
  71.                 )
  72.             ])
  73.             ->from(
  74.                 sprintf('(%s)'$this->getCodesCountQueryBuilder()->getSQL()),
  75.                 'offer_codes'
  76.             )
  77.             ->andWhere($expr->eq('offer_codes.offer_id'':offerId'))
  78.             ->andWhere($expr->eq('offer_codes.code_pool_id'':codePoolId'))
  79.             ->setParameters([
  80.                 'codesFromSameSubscription' => self::COUNT_CODES_FROM_SAME_SUBSCRIPTION,
  81.                 'offerId' => $offerId,
  82.                 'codePoolId' => $this->codePoolDao->findLastCodePoolIdByOfferId($offerId),
  83.             ])
  84.             ->execute()
  85.             ->fetchOne();
  86.         return false !== $purchases ? (int) $purchases 0;
  87.     }
  88.     public function findLastDayByOfferId(int $offerId): int
  89.     {
  90.         $queryBuilder $this->connection->createQueryBuilder();
  91.         $expr $queryBuilder->expr();
  92.         $period = new Period(
  93.             new DateTimeImmutable(sprintf('- %d days'PurchaseCountPeriod::LAST_DAY)),
  94.             new DateTimeImmutable(),
  95.             Period::INCLUDE_ALL,
  96.         );
  97.         $purchasesByPeriod $this->getByPeriodQueryBuilder($period)
  98.             ->andWhere($expr->eq('codes_by_period.offer_id'':offerId'))
  99.             ->setParameters([
  100.                 'offerId' => $offerId,
  101.                 'dateFrom' => $period->getStartDate()->format('Y-m-d H:i:s'),
  102.                 'dateTo' => $period->getEndDate()->format('Y-m-d H:i:s'),
  103.                 'codesFromSameSubscription' => self::COUNT_CODES_FROM_SAME_SUBSCRIPTION,
  104.             ])
  105.             ->execute()
  106.             ->fetchAssociative();
  107.         return (int) ($purchasesByPeriod['codes_count'] ?? 0);
  108.     }
  109.     public function findLastMonth(): array
  110.     {
  111.         return $this->findByPeriod(
  112.             new Period(
  113.                 new DateTimeImmutable(sprintf('- %d days'PurchaseCountPeriod::LAST_MONTH)),
  114.                 new DateTimeImmutable(),
  115.                 Period::INCLUDE_ALL,
  116.             )
  117.         );
  118.     }
  119.     public function findLastWeek(): array
  120.     {
  121.         return $this->findByPeriod(
  122.             new Period(
  123.                 new DateTimeImmutable(sprintf('- %d days'PurchaseCountPeriod::LAST_WEEK)),
  124.                 new DateTimeImmutable(),
  125.                 Period::INCLUDE_ALL,
  126.             )
  127.         );
  128.     }
  129.     public function findLastDay(): array
  130.     {
  131.         return $this->findByPeriod(
  132.             new Period(
  133.                 new DateTimeImmutable(sprintf('- %d days'PurchaseCountPeriod::LAST_DAY)),
  134.                 new DateTimeImmutable(),
  135.                 Period::INCLUDE_ALL,
  136.             )
  137.         );
  138.     }
  139.     public function findConversionByManagerIdAndPeriod(int $managerIdPeriod $period): array
  140.     {
  141.         $conversionByManagerIdAndPeriodQueryBuilder $this->getOfferConversionQueryBuilder($period);
  142.         $expr $conversionByManagerIdAndPeriodQueryBuilder->expr();
  143.         $conversionByManagerIdAndPeriod $conversionByManagerIdAndPeriodQueryBuilder
  144.             ->andWhere($expr->eq('offer.manager_id'':managerId'))
  145.             ->setParameter('managerId'$managerId)
  146.             ->execute()
  147.             ->fetchAllAssociative();
  148.         return array_map(
  149.             fn (array $conversionByOffer): ConversionByOfferDto =>
  150.             $this->conversionByOfferDtoFactory->create($conversionByOffer),
  151.             $conversionByManagerIdAndPeriod,
  152.         );
  153.     }
  154.     public function findConversionByPeriod(Period $period): array
  155.     {
  156.         $conversionByPeriod $this->getOfferConversionQueryBuilder($period)
  157.             ->execute()
  158.             ->fetchAllAssociative();
  159.         return array_map(
  160.             fn (array $conversionByOffer): ConversionByOfferDto =>
  161.             $this->conversionByOfferDtoFactory->create($conversionByOffer),
  162.             $conversionByPeriod,
  163.         );
  164.     }
  165.     private function getOfferConversionQueryBuilder(Period $period): QueryBuilder
  166.     {
  167.         $queryBuilder $this->connection->createQueryBuilder();
  168.         $expr $queryBuilder->expr();
  169.         return $queryBuilder
  170.             ->addSelect([
  171.                 'offer.id as offer_id',
  172.                 'offer.manager_id as manager_id',
  173.                 'coalesce(manager.manager_name, manager.email) as manager_name',
  174.                 'offer.title as offer_name',
  175.                 'coalesce(purchases.codes_count, 0) as codes_count',
  176.                 'count(*) as visit_count',
  177.             ])
  178.             ->from('offer')
  179.             ->innerJoin(
  180.                 'offer',
  181.                 'customer',
  182.                 'manager',
  183.                 $expr->eq('manager.id''offer.manager_id')
  184.             )
  185.             ->leftJoin(
  186.                 'offer',
  187.                 sprintf('(%s)'$this->getByPeriodQueryBuilder($period)->getSQL()),
  188.                 'purchases',
  189.                 $expr->eq('purchases.offer_id''offer.id')
  190.             )
  191.             ->leftJoin(
  192.                 'offer',
  193.                 'visit',
  194.                 'visit',
  195.                 $expr->and(
  196.                     $expr->eq('offer.id''visit.entity_id'),
  197.                     $expr->eq('visit.entity_type_id'':offerVisitType')
  198.                 )
  199.             )
  200.             ->andWhere('visit.created_on between :dateFrom and :dateTo')
  201.             ->andWhere($expr->or(
  202.                 ':dateFrom between offer.active_since and offer.active_till',
  203.                 ':dateTo between offer.active_since and offer.active_till',
  204.             ))
  205.             ->setParameters([
  206.                 'offerVisitType' => Visit::TYPE_OFFER,
  207.                 'dateFrom' => $period->getStartDate()->format('Y-m-d H:i:s'),
  208.                 'dateTo' => $period->getEndDate()->format('Y-m-d H:i:s'),
  209.                 'codesFromSameSubscription' => self::COUNT_CODES_FROM_SAME_SUBSCRIPTION,
  210.             ])
  211.             ->addGroupBy([
  212.                 'offer.id',
  213.                 'offer.manager_id',
  214.                 'coalesce(manager.manager_name, manager.email)',
  215.                 'offer.title',
  216.                 'coalesce(purchases.codes_count, 0)',
  217.             ]);
  218.     }
  219.     private function getByPeriodQueryBuilder(Period $period): QueryBuilder
  220.     {
  221.         $queryBuilder $this->connection->createQueryBuilder();
  222.         return $queryBuilder
  223.             ->addSelect([
  224.                 'codes_by_period.offer_id',
  225.                 sprintf(
  226.                     'sum (case %s %s %s %s %s end) as codes_count',
  227.                     'when codes_by_period.subscription_period_start_at is null then codes_by_period.codes_count',
  228.                     'when codes_by_period.previous_period_codes_count > :codesFromSameSubscription then 0',
  229.                     'when (codes_by_period.previous_period_codes_count + codes_by_period.codes_count) <= :codesFromSameSubscription then codes_by_period.codes_count',
  230.                     'when (codes_by_period.codes_count - codes_by_period.previous_period_codes_count) > :codesFromSameSubscription then (:codesFromSameSubscription - codes_by_period.previous_period_codes_count)',
  231.                     'else codes_by_period.codes_count - codes_by_period.previous_period_codes_count',
  232.                 )
  233.             ])
  234.             ->from(
  235.                 sprintf('(%s)'$this->getPurchasesWithPreviousSubscriptionQueryBuilder()->getSQL()),
  236.                 'codes_by_period'
  237.             )
  238.             ->setParameters([
  239.                 'dateFrom' => $period->getStartDate()->format('Y-m-d H:i:s'),
  240.                 'dateTo' => $period->getEndDate()->format('Y-m-d H:i:s'),
  241.                 'codesFromSameSubscription' => self::COUNT_CODES_FROM_SAME_SUBSCRIPTION,
  242.             ])
  243.             ->addGroupBy('codes_by_period.offer_id');
  244.     }
  245.     private function getPurchasesWithPreviousSubscriptionQueryBuilder(): QueryBuilder
  246.     {
  247.         $queryBuilder $this->connection->createQueryBuilder();
  248.         $expr $queryBuilder->expr();
  249.         return $queryBuilder
  250.             ->addSelect([
  251.                 'offer_order.offer_id',
  252.                 'offer_order.user_id',
  253.                 'offer_order.subscription_period_start_at',
  254.                 'coalesce(previous_count.count_by_subscription, 0) as previous_period_codes_count',
  255.                 'count(*) as codes_count',
  256.             ])
  257.             ->from('offer_code')
  258.             ->innerJoin(
  259.                 'offer_code',
  260.                 'offer_order',
  261.                 'offer_order',
  262.                 $expr->eq('offer_code.offer_order_id''offer_order.id')
  263.             )
  264.             ->leftJoin(
  265.                 'offer_code',
  266.                 sprintf('(%s)'$this->getPreviousSubscriptionPurchasesQueryBuilder()->getSQL()),
  267.                 'previous_count',
  268.                 $expr->and(
  269.                     $expr->eq('previous_count.offer_id''offer_order.offer_id'),
  270.                     $expr->eq('previous_count.user_id''offer_order.user_id'),
  271.                     $expr->eq('previous_count.subscription_period_start_at''offer_order.subscription_period_start_at'),
  272.                 )
  273.             )
  274.             ->andWhere('offer_order.created_on between :dateFrom and :dateTo')
  275.             ->addGroupBy([
  276.                 'offer_order.offer_id',
  277.                 'offer_order.user_id',
  278.                 'offer_order.subscription_period_start_at',
  279.                 'coalesce(previous_count.count_by_subscription, 0)',
  280.             ]);
  281.     }
  282.     private function getPreviousSubscriptionPurchasesQueryBuilder(): QueryBuilder
  283.     {
  284.         $queryBuilder $this->connection->createQueryBuilder();
  285.         $expr $queryBuilder->expr();
  286.         return $queryBuilder
  287.             ->addSelect([
  288.                 'offer_order.offer_id',
  289.                 'offer_order.user_id',
  290.                 'offer_order.subscription_period_start_at',
  291.                 'count(*) as count_by_subscription',
  292.             ])
  293.             ->from('offer_code')
  294.             ->innerJoin(
  295.                 'offer_code',
  296.                 'offer_order',
  297.                 'offer_order',
  298.                 $expr->eq('offer_code.offer_order_id''offer_order.id')
  299.             )
  300.             ->andWhere($expr->lt('offer_order.created_on'':dateFrom'))
  301.             ->andWhere(
  302.                 $expr->in(
  303.                     '(offer_order.user_id, subscription_period_start_at)',
  304.                     $this->getDistinctSubscriptionPurchasesQueryBuilder()->getSQL()
  305.                 )
  306.             )
  307.             ->addGroupBy([
  308.                 'offer_order.offer_id',
  309.                 'offer_order.user_id',
  310.                 'offer_order.subscription_period_start_at',
  311.             ]);
  312.     }
  313.     private function getDistinctSubscriptionPurchasesQueryBuilder(): QueryBuilder
  314.     {
  315.         $queryBuilder $this->connection->createQueryBuilder();
  316.         $expr $queryBuilder->expr();
  317.         return $queryBuilder
  318.             ->addSelect([
  319.                 'offer_order.user_id',
  320.                 'offer_order.subscription_period_start_at',
  321.             ])
  322.             ->from('offer_code')
  323.             ->innerJoin(
  324.                 'offer_code',
  325.                 'offer_order',
  326.                 'offer_order',
  327.                 $expr->eq('offer_code.offer_order_id''offer_order.id')
  328.             )
  329.             ->andWhere('offer_order.created_on between :dateFrom and :dateTo')
  330.             ->andWhere($expr->isNotNull('offer_order.subscription_period_start_at'))
  331.             ->distinct();
  332.     }
  333.     private function getCodesCountQueryBuilder(): QueryBuilder
  334.     {
  335.         $queryBuilder $this->connection->createQueryBuilder();
  336.         $expr $queryBuilder->expr();
  337.         return $queryBuilder
  338.             ->addSelect([
  339.                 'offer_order.offer_id',
  340.                 'offer_order.user_id',
  341.                 'offer_order.subscription_period_start_at',
  342.                 'offer_code.offer_code_pool_id as code_pool_id',
  343.                 'count(*) as codes_count',
  344.             ])
  345.             ->from('offer_code')
  346.             ->innerJoin(
  347.                 'offer_code',
  348.                 'offer_order',
  349.                 'offer_order',
  350.                 $expr->eq('offer_code.offer_order_id''offer_order.id')
  351.             )
  352.             ->addGroupBy([
  353.                 'offer_order.offer_id',
  354.                 'offer_order.user_id',
  355.                 'offer_order.subscription_period_start_at',
  356.                 'offer_code.offer_code_pool_id',
  357.             ]);
  358.     }
  359. }