Skip to content

Commit 84322b2

Browse files
committed
refactor: Optimize donor dashboard queries
1 parent ddf88a9 commit 84322b2

File tree

2 files changed

+106
-19
lines changed

2 files changed

+106
-19
lines changed

src/DonorDashboards/Repositories/Donations.php

Lines changed: 100 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,33 @@
33
namespace Give\DonorDashboards\Repositories;
44

55
use Give\Framework\Database\DB;
6+
use Give\Framework\Support\ValueObjects\Money;
67
use Give\Receipt\DonationReceipt;
78
use Give\Receipt\LineItem;
8-
use Give\ValueObjects\Money;
99
use Give_Payment;
1010

1111
/**
1212
* @since 2.10.0
1313
*/
1414
class Donations
1515
{
16+
/**
17+
* Array of cached donor donation ids
18+
*
19+
* @unreleased
20+
*
21+
* @var array<int, int[]>
22+
*/
23+
private static $donationIdsCache = [];
24+
1625
/**
1726
* Get donations count for donor
1827
*
1928
* @since 2.10.0
2029
*
2130
* @param int $donorId
2231
*
23-
* @return int
32+
* @return int|null
2433
*/
2534
public function getDonationCount($donorId)
2635
{
@@ -36,7 +45,7 @@ public function getDonationCount($donorId)
3645
*
3746
* @param int $donorId
3847
*
39-
* @return string
48+
* @return string|null
4049
*/
4150
public function getRevenue($donorId)
4251
{
@@ -45,7 +54,7 @@ public function getRevenue($donorId)
4554

4655
return $aggregate ?
4756
$this->getAmountWithSeparators(
48-
Money::ofMinor($aggregate->result, $currencyCode)->getAmount(),
57+
(new Money($aggregate->result, $currencyCode))->formatToDecimal(),
4958
$currencyCode
5059
) :
5160
null;
@@ -58,7 +67,7 @@ public function getRevenue($donorId)
5867
*
5968
* @param int $donorId
6069
*
61-
* @return string
70+
* @return string|null
6271
*/
6372
public function getAverageRevenue($donorId)
6473
{
@@ -67,12 +76,82 @@ public function getAverageRevenue($donorId)
6776

6877
return $aggregate ?
6978
$this->getAmountWithSeparators(
70-
Money::ofMinor($aggregate->result, $currencyCode)->getAmount(),
79+
(new Money($aggregate->result, $currencyCode))->formatToDecimal(),
7180
$currencyCode
7281
) :
7382
null;
7483
}
7584

85+
/**
86+
* Get formatted donations summary for a donor.
87+
*
88+
* This summary includes count, total revenue, and average donation amount.
89+
* The revenue and average are formatted as decimal strings.
90+
*
91+
* @unreleased
92+
*
93+
* @param int $donorId
94+
*
95+
* @return array{count: int|null, revenue: string|null, average: string|null}
96+
*/
97+
public function getFormattedDonationsSummary($donorId)
98+
{
99+
$summary = $this->getDonationsSummary($donorId);
100+
$currencyCode = give_get_option('currency');
101+
102+
return [
103+
'count' => ($summary->count ?? null),
104+
'revenue' => $summary->revenue ? $this->getAmountWithSeparators(
105+
(new Money($summary->revenue, $currencyCode))->formatToDecimal(),
106+
$currencyCode
107+
) : null,
108+
'average' => $summary->average ? $this->getAmountWithSeparators(
109+
(new Money($summary->average, $currencyCode))->formatToDecimal(),
110+
$currencyCode
111+
) : null,
112+
];
113+
}
114+
115+
/**
116+
* Get donations summary for a donor
117+
*
118+
* @unreleased
119+
*
120+
* @param int $donorId
121+
*
122+
* @return object{count: int|null, revenue: string|null, average: string|null}
123+
*/
124+
private function getDonationsSummary($donorId)
125+
{
126+
$donationIds = $this->getDonationIDs($donorId);
127+
128+
if (empty($donationIds)) {
129+
return (object) [
130+
'count' => null,
131+
'revenue' => null,
132+
'average' => null,
133+
];
134+
}
135+
136+
global $wpdb;
137+
138+
$ids_format = implode(',', array_fill(0, count($donationIds), '%d'));
139+
140+
return DB::get_row(
141+
$wpdb->prepare(
142+
"
143+
SELECT
144+
COUNT(id) AS count,
145+
SUM(amount) AS revenue,
146+
AVG(amount) AS average
147+
FROM {$wpdb->give_revenue}
148+
WHERE donation_id IN ( $ids_format )
149+
",
150+
$donationIds
151+
)
152+
);
153+
}
154+
76155
/**
77156
* Generates a donation aggregate for a given donor
78157
*
@@ -112,25 +191,33 @@ private function getDonationAggregate($rawAggregate, $donorId)
112191
*/
113192
protected function getDonationIDs($donorId)
114193
{
194+
if (isset(self::$donationIdsCache[$donorId])) {
195+
return self::$donationIdsCache[$donorId];
196+
}
197+
115198
$statusKeys = give_get_payment_status_keys();
116199
$statusQuery = "'" . implode("','", $statusKeys) . "'";
117200

118201
global $wpdb;
119202

120-
return DB::get_col(
203+
$donationIds = DB::get_col(
121204
DB::prepare(
122205
"
123-
SELECT revenue.donation_id as id
124-
FROM {$wpdb->give_revenue} as revenue
125-
INNER JOIN {$wpdb->posts} as posts ON revenue.donation_id = posts.ID
126-
INNER JOIN {$wpdb->prefix}give_donationmeta as donationmeta ON revenue.donation_id = donationmeta.donation_id
206+
SELECT posts.ID as id
207+
FROM {$wpdb->posts} as posts
208+
INNER JOIN {$wpdb->prefix}give_donationmeta as donationmeta ON posts.ID = donationmeta.donation_id
127209
WHERE donationmeta.meta_key = '_give_payment_donor_id'
128210
AND donationmeta.meta_value = %d
211+
AND posts.post_type = 'give_payment'
129212
AND posts.post_status IN ( {$statusQuery} )
130213
",
131214
$donorId
132215
)
133216
);
217+
218+
self::$donationIdsCache[$donorId] = $donationIds;
219+
220+
return $donationIds;
134221
}
135222

136223
/**
@@ -252,7 +339,7 @@ protected function getReceiptInfo($payment)
252339
$sectionIndex = 0;
253340
foreach ($receipt as $section) {
254341
// Continue if section does not have line items.
255-
if ( ! $section->getLineItems()) {
342+
if (! $section->getLineItems()) {
256343
continue;
257344
}
258345

@@ -273,7 +360,7 @@ protected function getReceiptInfo($payment)
273360
/* @var LineItem $lineItem */
274361
foreach ($section as $lineItem) {
275362
// Continue if line item does not have value.
276-
if ( ! $lineItem->value) {
363+
if (! $lineItem->value) {
277364
continue;
278365
}
279366

src/DonorDashboards/Tabs/DonationHistoryTab/DonationsRoute.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Give\DonorDashboards\Repositories\Donations as DonationsRepository;
66
use Give\DonorDashboards\Tabs\Contracts\Route as RouteAbstract;
7+
use Give\Framework\Support\ValueObjects\Money;
78
use Give\Log\Log;
89
use WP_REST_Request;
910
use WP_REST_Response;
@@ -60,9 +61,8 @@ protected function getData(DonationsRepository $repository, $donorId)
6061
// If the provided donor ID is valid, attempt to query data
6162
try {
6263
$donations = $repository->getDonations($donorId);
63-
$count = $repository->getDonationCount($donorId);
64-
$revenue = $repository->getRevenue($donorId);
65-
$average = $repository->getAverageRevenue($donorId);
64+
$summary = $repository->getFormattedDonationsSummary($donorId);
65+
6666
$currency = [
6767
'symbol' => give_currency_symbol(give_get_currency(), true),
6868
'position' => give_get_currency_position(),
@@ -75,9 +75,9 @@ protected function getData(DonationsRepository $repository, $donorId)
7575
'body_response' => [
7676
[
7777
'donations' => $donations,
78-
'count' => $count,
79-
'revenue' => $revenue,
80-
'average' => $average,
78+
'count' => $summary['count'],
79+
'revenue' => $summary['revenue'],
80+
'average' => $summary['average'],
8181
'currency' => $currency,
8282
],
8383
],

0 commit comments

Comments
 (0)