PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.12.3
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.12.3
1.8.13.1 1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 All 212 releases
charitable / includes / queries / class-charitable-donor-query.php

class-charitable-donor-query.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.12.3, at includes/queries/class-charitable-donor-query.php

337 lines 10.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The class responsible for querying donors.
4 *
5 * @package Charitable/Classes/Charitable_Donor_Query
6 * @version 1.0.0
7 * @author David Bisset
8 * @copyright Copyright (c) 2023, WP Charitable LLC
9 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
10 */
11
12 // Exit if accessed directly.
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 if ( ! class_exists( 'Charitable_Donor_Query' ) ) :
18
19 /**
20 * Charitable_Donor_Query
21 *
22 * @since 1.0.0
23 */
24 class Charitable_Donor_Query extends Charitable_Query {
25
26 /**
27 * Create new query object.
28 *
29 * @since 1.0.0
30 *
31 * @param array $args Query arguments.
32 */
33 public function __construct( $args = array() ) {
34 /**
35 * Filter the default arguments for Charitable_Donor_Query.
36 *
37 * @since 1.0.0
38 *
39 * @param array $args The default arguments.
40 */
41 $defaults = apply_filters(
42 'charitable_donor_query_default_args',
43 array(
44 'output' => 'donors',
45 'status' => array( 'charitable-completed', 'charitable-preapproved' ),
46 'orderby' => 'date',
47 'order' => 'DESC',
48 'number' => 20,
49 'paged' => 1,
50 'fields' => 'all',
51 'campaign' => 0,
52 'distinct_donors' => true,
53 'donor_id' => 0,
54 'include_erased' => 1,
55 'date_query' => array(),
56 'meta_query' => array(),
57 )
58 );
59
60 $this->args = wp_parse_args( $args, $defaults );
61 $this->args['campaign'] = $this->sanitize_campaign();
62 $this->position = 0;
63
64 $this->prepare_query();
65
66 $this->results = $this->get_donors();
67 }
68
69 /**
70 * Return list of donor IDs together with the number of donations they have made.
71 *
72 * @since 1.0.0
73 *
74 * @return object[]
75 */
76 public function get_donors() {
77 $records = $this->query();
78
79 if ( 'donors' != $this->get( 'output' ) ) {
80 return $records;
81 }
82
83 return array_map( array( $this, 'get_donor_object' ), $records );
84 }
85
86 /**
87 * Returns a Charitable_Donor object for a database record.
88 *
89 * @since 1.5.0
90 *
91 * @param object $record Database record for the donor.
92 * @return Charitable_Donor
93 */
94 public function get_donor_object( $record ) {
95 $donation_id = isset( $record->donation_id ) ? $record->donation_id : false;
96 $donor = new Charitable_Donor( $record->donor_id, $donation_id );
97
98 /**
99 * Filter the returned object.
100 *
101 * Note that this should always return a Charitable_Donor object.
102 *
103 * @since 1.5.7
104 *
105 * @param Charitable_Donor $donor The instance of `Charitable_Donor`.
106 * @param object $record Database record for the donor.
107 * @param Charitable_Donor_Query $query This query object.
108 */
109 return apply_filters( 'charitable_donor_query_donor_object', $donor, $record, $this );
110 }
111
112 /**
113 * When retrieving the donor count, get distinct donor IDs.
114 *
115 * @since 1.5.4
116 *
117 * @return string
118 */
119 public function select_donor_count() {
120 return 'COUNT(DISTINCT d.donor_id)';
121 }
122
123 /**
124 * Set up fields query argument.
125 *
126 * @since 1.0.0
127 *
128 * @return void
129 */
130 public function setup_fields() {
131 if ( ! $this->get( 'distinct_donors', true ) ) {
132 add_filter( 'charitable_query_fields', array( $this, 'donation_fields' ), 4 );
133 }
134
135 $fields = $this->get( 'fields', 'all' );
136
137 if ( 'all' == $fields ) {
138 add_filter( 'charitable_query_fields', array( $this, 'donation_calc_fields' ), 5 );
139 add_filter( 'charitable_query_fields', array( $this, 'donor_fields' ), 6 );
140 }
141
142 if ( is_array( $fields ) ? in_array( 'amount', $fields ) : 'amount' == $fields ) {
143 add_filter( 'charitable_query_fields', array( $this, 'donation_amount_sum_field' ), 6 );
144 }
145 }
146
147 /**
148 * Set up orderby query argument.
149 *
150 * @since 1.0.0
151 *
152 * @return void
153 */
154 public function setup_orderby() {
155 $orderby = $this->get( 'orderby', false );
156
157 if ( ! $orderby ) {
158 return;
159 }
160
161 switch ( $orderby ) {
162 case 'date':
163 add_filter( 'charitable_query_orderby', array( $this, 'orderby_date' ) );
164 break;
165
166 case 'donations':
167 add_filter( 'charitable_query_orderby', array( $this, 'orderby_count' ) );
168 break;
169
170 case 'amount':
171 add_filter( 'charitable_query_orderby', array( $this, 'orderby_donation_amount' ) );
172 break;
173
174 case 'name':
175 add_filter( 'charitable_query_orderby', array( $this, 'orderby_name' ) );
176 break;
177 }
178 }
179
180 /**
181 * Order the results by name.
182 *
183 * @since 1.5.0
184 *
185 * @return string
186 */
187 public function orderby_name() {
188 return 'ORDER BY d.last_name, d.first_name';
189 }
190
191 /**
192 * Set up query grouping.
193 *
194 * @since 1.0.0
195 *
196 * @return void
197 */
198 public function setup_grouping() {
199 if ( ! $this->get( 'distinct_donors', false ) ) {
200
201 add_filter( 'charitable_query_groupby', array( $this, 'groupby_ID' ) );
202 return;
203 }
204
205 add_filter( 'charitable_query_groupby', array( $this, 'groupby_donor_id' ) );
206 }
207
208 /**
209 * Do not include donors that have had their data erased.
210 *
211 * @since 1.6.0
212 *
213 * @param string $where The WHERE SQL query part.
214 * @return string
215 */
216 public function where_donor_is_not_erased( $where ) {
217 if ( ! $this->get( 'include_erased' ) ) {
218 $where .= ' AND d.data_erased = "0000-00-00 00:00:00"';
219 }
220
221 return $where;
222 }
223
224 /**
225 * Remove any hooks that have been attached by the class to prevent contaminating other queries.
226 *
227 * @since 1.0.0
228 * @version 1.8.8
229 *
230 * @return void
231 */
232 public function unhook_callbacks() {
233 remove_action( 'charitable_pre_query', array( $this, 'setup_fields' ) );
234 remove_action( 'charitable_pre_query', array( $this, 'setup_orderby' ) );
235 remove_action( 'charitable_pre_query', array( $this, 'setup_grouping' ) );
236 remove_filter( 'charitable_select_count_fields', array( $this, 'select_donor_count' ) );
237 remove_filter( 'charitable_query_fields', array( $this, 'donation_fields' ), 4 );
238 remove_filter( 'charitable_query_fields', array( $this, 'donation_calc_fields' ), 5 );
239 remove_filter( 'charitable_query_fields', array( $this, 'donor_fields' ), 6 );
240 remove_filter( 'charitable_query_fields', array( $this, 'donation_amount_sum_field' ), 6 );
241 remove_filter( 'charitable_query_join', array( $this, 'join_campaign_donations_table_on_donation' ), 5 );
242 remove_filter( 'charitable_query_join', array( $this, 'join_donors_table' ), 6 );
243 remove_filter( 'charitable_query_join', array( $this, 'join_meta' ), 7 );
244
245 // Check if we should skip donation-based filters when including donors without donations.
246 $skip_donation_filters = apply_filters( 'charitable_donor_query_skip_donation_filters', false, $this );
247
248 // Only remove donation-based filters if they were added.
249 if ( ! $this->get( 'include_donors_without_donations' ) || ! $skip_donation_filters ) {
250 remove_filter( 'charitable_query_where', array( $this, 'where_status_is_in' ), 5 );
251 remove_filter( 'charitable_query_where', array( $this, 'where_campaign_is_in' ), 6 );
252 remove_filter( 'charitable_query_where', array( $this, 'where_date' ), 9 );
253 remove_filter( 'charitable_query_where', array( $this, 'where_meta' ), 10 );
254 }
255
256 remove_filter( 'charitable_query_where', array( $this, 'where_donor_id_is_in' ), 7 );
257 remove_filter( 'charitable_query_where', array( $this, 'where_donor_is_not_erased' ), 8 );
258 remove_filter( 'charitable_query_groupby', array( $this, 'groupby_donor_id' ) );
259 remove_filter( 'charitable_query_orderby', array( $this, 'orderby_date' ) );
260 remove_filter( 'charitable_query_orderby', array( $this, 'orderby_count' ) );
261 remove_filter( 'charitable_query_orderby', array( $this, 'orderby_donation_amount' ) );
262 remove_filter( 'charitable_query_orderby', array( $this, 'orderby_name' ) );
263 remove_action( 'charitable_post_query', array( $this, 'unhook_callbacks' ) );
264 }
265
266 /**
267 * Set up callbacks for WP_Query filters.
268 *
269 * @since 1.0.0
270 * @version 1.8.8
271 *
272 * @return void
273 */
274 protected function prepare_query() {
275 add_action( 'charitable_pre_query', array( $this, 'setup_fields' ) );
276 add_action( 'charitable_pre_query', array( $this, 'setup_orderby' ) );
277 add_action( 'charitable_pre_query', array( $this, 'setup_grouping' ) );
278 add_filter( 'charitable_select_count_fields', array( $this, 'select_donor_count' ) );
279 add_filter( 'charitable_query_join', array( $this, 'join_campaign_donations_table_on_donation' ), 5 );
280 add_filter( 'charitable_query_join', array( $this, 'join_donors_table' ), 6 );
281 add_filter( 'charitable_query_join', array( $this, 'join_meta' ), 7 );
282
283 /**
284 * Check if we should skip donation-based filters when including donors without donations.
285 *
286 * This filter allows you to opt into the new behavior where donation-based filters
287 * are skipped when querying donors without donations. By default, this is false
288 * to maintain backward compatibility.
289 *
290 * Usage:
291 * add_filter( 'charitable_donor_query_skip_donation_filters', '__return_true' );
292 *
293 * @since 1.8.8
294 *
295 * @param boolean $skip_donation_filters Whether to skip donation-based filters.
296 * @param Charitable_Donor_Query $query The current query instance.
297 */
298 $skip_donation_filters = apply_filters( 'charitable_donor_query_skip_donation_filters', false, $this );
299
300 // Only apply donation-based filters when not including donors without donations
301 // or when the skip filter is not enabled.
302 if ( ! $this->get( 'include_donors_without_donations' ) || ! $skip_donation_filters ) {
303 add_filter( 'charitable_query_where', array( $this, 'where_status_is_in' ), 5 );
304 add_filter( 'charitable_query_where', array( $this, 'where_campaign_is_in' ), 6 );
305 add_filter( 'charitable_query_where', array( $this, 'where_date' ), 9 );
306 add_filter( 'charitable_query_where', array( $this, 'where_meta' ), 10 );
307 }
308
309 add_filter( 'charitable_query_where', array( $this, 'where_donor_id_is_in' ), 7 );
310 add_filter( 'charitable_query_where', array( $this, 'where_donor_is_not_erased' ), 8 );
311 add_action( 'charitable_post_query', array( $this, 'unhook_callbacks' ) );
312 }
313
314 /**
315 * Sanitize the campaign argument.
316 *
317 * @since 1.5.0
318 *
319 * @return int
320 */
321 protected function sanitize_campaign() {
322 switch ( $this->args['campaign'] ) {
323 case '':
324 case 'all':
325 return 0;
326
327 case 'current':
328 return charitable_get_current_campaign_id();
329
330 default:
331 return $this->args['campaign'];
332 }
333 }
334 }
335
336 endif;
337