3
3
namespace Give \DonorDashboards \Repositories ;
4
4
5
5
use Give \Framework \Database \DB ;
6
+ use Give \Framework \Support \ValueObjects \Money ;
6
7
use Give \Receipt \DonationReceipt ;
7
8
use Give \Receipt \LineItem ;
8
- use Give \ValueObjects \Money ;
9
9
use Give_Payment ;
10
10
11
11
/**
12
12
* @since 2.10.0
13
13
*/
14
14
class Donations
15
15
{
16
+ /**
17
+ * Array of cached donor donation ids
18
+ *
19
+ * @unreleased
20
+ *
21
+ * @var array<int, int[]>
22
+ */
23
+ private static $ donationIdsCache = [];
24
+
16
25
/**
17
26
* Get donations count for donor
18
27
*
19
28
* @since 2.10.0
20
29
*
21
30
* @param int $donorId
22
31
*
23
- * @return int
32
+ * @return int|null
24
33
*/
25
34
public function getDonationCount ($ donorId )
26
35
{
@@ -36,7 +45,7 @@ public function getDonationCount($donorId)
36
45
*
37
46
* @param int $donorId
38
47
*
39
- * @return string
48
+ * @return string|null
40
49
*/
41
50
public function getRevenue ($ donorId )
42
51
{
@@ -45,7 +54,7 @@ public function getRevenue($donorId)
45
54
46
55
return $ aggregate ?
47
56
$ this ->getAmountWithSeparators (
48
- Money:: ofMinor ($ aggregate ->result , $ currencyCode )-> getAmount (),
57
+ ( new Money ($ aggregate ->result , $ currencyCode ))-> formatToDecimal (),
49
58
$ currencyCode
50
59
) :
51
60
null ;
@@ -58,7 +67,7 @@ public function getRevenue($donorId)
58
67
*
59
68
* @param int $donorId
60
69
*
61
- * @return string
70
+ * @return string|null
62
71
*/
63
72
public function getAverageRevenue ($ donorId )
64
73
{
@@ -67,12 +76,82 @@ public function getAverageRevenue($donorId)
67
76
68
77
return $ aggregate ?
69
78
$ this ->getAmountWithSeparators (
70
- Money:: ofMinor ($ aggregate ->result , $ currencyCode )-> getAmount (),
79
+ ( new Money ($ aggregate ->result , $ currencyCode ))-> formatToDecimal (),
71
80
$ currencyCode
72
81
) :
73
82
null ;
74
83
}
75
84
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
+
76
155
/**
77
156
* Generates a donation aggregate for a given donor
78
157
*
@@ -112,25 +191,33 @@ private function getDonationAggregate($rawAggregate, $donorId)
112
191
*/
113
192
protected function getDonationIDs ($ donorId )
114
193
{
194
+ if (isset (self ::$ donationIdsCache [$ donorId ])) {
195
+ return self ::$ donationIdsCache [$ donorId ];
196
+ }
197
+
115
198
$ statusKeys = give_get_payment_status_keys ();
116
199
$ statusQuery = "' " . implode ("',' " , $ statusKeys ) . "' " ;
117
200
118
201
global $ wpdb ;
119
202
120
- return DB ::get_col (
203
+ $ donationIds = DB ::get_col (
121
204
DB ::prepare (
122
205
"
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
127
209
WHERE donationmeta.meta_key = '_give_payment_donor_id'
128
210
AND donationmeta.meta_value = %d
211
+ AND posts.post_type = 'give_payment'
129
212
AND posts.post_status IN ( {$ statusQuery } )
130
213
" ,
131
214
$ donorId
132
215
)
133
216
);
217
+
218
+ self ::$ donationIdsCache [$ donorId ] = $ donationIds ;
219
+
220
+ return $ donationIds ;
134
221
}
135
222
136
223
/**
@@ -252,7 +339,7 @@ protected function getReceiptInfo($payment)
252
339
$ sectionIndex = 0 ;
253
340
foreach ($ receipt as $ section ) {
254
341
// Continue if section does not have line items.
255
- if ( ! $ section ->getLineItems ()) {
342
+ if (! $ section ->getLineItems ()) {
256
343
continue ;
257
344
}
258
345
@@ -273,7 +360,7 @@ protected function getReceiptInfo($payment)
273
360
/* @var LineItem $lineItem */
274
361
foreach ($ section as $ lineItem ) {
275
362
// Continue if line item does not have value.
276
- if ( ! $ lineItem ->value ) {
363
+ if (! $ lineItem ->value ) {
277
364
continue ;
278
365
}
279
366
0 commit comments