oont-contents/plugins/mailpoet/lib/Statistics/StatisticsWooCommercePurchasesRepository.php
2025-02-08 15:10:23 +01:00

136 lines
5.3 KiB
PHP

<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
namespace MailPoet\Statistics;
if (!defined('ABSPATH')) exit;
use MailPoet\Doctrine\Repository;
use MailPoet\Entities\NewsletterEntity;
use MailPoet\Entities\SendingQueueEntity;
use MailPoet\Entities\StatisticsClickEntity;
use MailPoet\Entities\StatisticsWooCommercePurchaseEntity;
use MailPoet\WooCommerce\Helper;
use MailPoetVendor\Doctrine\DBAL\ArrayParameterType;
use MailPoetVendor\Doctrine\DBAL\ParameterType;
use MailPoetVendor\Doctrine\ORM\EntityManager;
/**
* @extends Repository<StatisticsWooCommercePurchaseEntity>
*/
class StatisticsWooCommercePurchasesRepository extends Repository {
/** @var Helper */
private $wooCommerceHelper;
public function __construct(
EntityManager $entityManager,
Helper $wooCommerceHelper
) {
parent::__construct($entityManager);
$this->wooCommerceHelper = $wooCommerceHelper;
}
protected function getEntityClassName() {
return StatisticsWooCommercePurchaseEntity::class;
}
public function createOrUpdateByClickDataAndOrder(StatisticsClickEntity $click, \WC_Order $order) {
// search by subscriber and newsletter IDs (instead of click itself) to avoid duplicities
// when a new click from the subscriber appeared since last tracking for given newsletter
// (this will keep the originally tracked click - likely the click that led to the order)
$statistics = $this->findOneBy([
'orderId' => $order->get_id(),
'subscriber' => $click->getSubscriber(),
'newsletter' => $click->getNewsletter(),
]);
if (!$statistics instanceof StatisticsWooCommercePurchaseEntity) {
$newsletter = $click->getNewsletter();
$queue = $click->getQueue();
if ((!$newsletter instanceof NewsletterEntity) || (!$queue instanceof SendingQueueEntity)) return;
$statistics = new StatisticsWooCommercePurchaseEntity(
$newsletter,
$queue,
$click,
$order->get_id(),
$order->get_currency(),
(float)$order->get_remaining_refund_amount(),
$order->get_status()
);
$this->persist($statistics);
} else {
$statistics->setOrderCurrency($order->get_currency());
$statistics->setOrderPriceTotal((float)$order->get_remaining_refund_amount());
$statistics->setStatus($order->get_status());
}
$statistics->setSubscriber($click->getSubscriber());
$this->flush();
}
public function getRevenuesByCampaigns(string $currency): array {
$revenueStatus = $this->wooCommerceHelper->getPurchaseStates();
$revenueStatsTable = $this->entityManager->getClassMetadata(StatisticsWooCommercePurchaseEntity::class)->getTableName();
$newsletterTable = $this->entityManager->getClassMetadata(NewsletterEntity::class)->getTableName();
// The "SELECT MIN(click_id)..." sub-query is used to count each purchase only once.
// In the data we track a purchase to multiple newsletters if clicks from multiple newsletters occurred.
/** @var array<int, array{revenue: float|int, campaign_id:int, orders_count:int}> $data */
$data = $this->entityManager->getConnection()->executeQuery('
SELECT
SUM(swp.order_price_total) AS revenue,
COALESCE(n.parent_id, swp.newsletter_id) AS campaign_id,
(
CASE
WHEN n.type IS NULL THEN \'unknown\'
WHEN n.type = :notification_history_type THEN :notification_type
ELSE n.type
END
) AS campaign_type,
COUNT(order_id) as orders_count
FROM ' . $revenueStatsTable . ' swp
LEFT JOIN ' . $newsletterTable . ' n ON
n.id = swp.newsletter_id
WHERE
swp.order_currency = :currency
AND swp.status IN (:revenue_status)
AND swp.click_id IN (SELECT MIN(click_id) FROM ' . $revenueStatsTable . ' ss GROUP BY order_id)
GROUP BY campaign_id, n.type;
', [
'notification_history_type' => NewsletterEntity::TYPE_NOTIFICATION_HISTORY,
'notification_type' => NewsletterEntity::TYPE_NOTIFICATION,
'currency' => $currency,
'revenue_status' => $revenueStatus,
], [
'notification_history_type' => ParameterType::STRING,
'notification_type' => ParameterType::STRING,
'currency' => ParameterType::STRING,
'revenue_status' => ArrayParameterType::STRING,
])->fetchAllAssociative();
$data = array_map(function($row) {
$row['revenue'] = round(floatval($row['revenue']), 2);
$row['orders_count'] = intval($row['orders_count']);
return $row;
}, $data);
return $data;
}
/** @param int[] $ids */
public function removeNewsletterDataByNewsletterIds(array $ids): void {
$this->entityManager->createQueryBuilder()
->update(StatisticsWooCommercePurchaseEntity::class, 'swp')
->set('swp.newsletter', ':newsletter')
->where('swp.newsletter IN (:ids)')
->setParameter('newsletter', null)
->setParameter('ids', $ids)
->getQuery()
->execute();
// update was done via DQL, make sure the entities are also refreshed in the entity manager
$this->refreshAll(function (StatisticsWooCommercePurchaseEntity $entity) use ($ids) {
$newsletter = $entity->getNewsletter();
return $newsletter && in_array($newsletter->getId(), $ids, true);
});
}
}