PluginProbe
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management / 1.6.1
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management v1.6.1
1.6.1 1.6.0 1.5.1 1.5.0 1.4.0 1.3.0 trunk 0.0.1 1.0.0 1.1.0 1.1.1 1.1.2 1.2.0
suredonation / inc / import / charitable / provenance-dedupe.php

provenance-dedupe.php in SureDonation – Donation Forms, Fundraising Campaigns & Donor Management 1.6.1, at inc/import/charitable/provenance-dedupe.php

116 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Shared (donation_post_id, source_campaign_id) dedupe for the Charitable
4 * import paths.
5 *
6 * Both the direct-DB (Donation_Mapper) and CSV (Csv_Parser) paths import the
7 * same gift keyed on the (Charitable donation post, source campaign) pair,
8 * which both writers store in the indexed `import_provenance` column. Keeping
9 * the dedupe in one trait means the two paths can never drift apart on the key,
10 * the recurring guard, or the unknown-campaign (0) handling.
11 *
12 * Per-row dedupe is a single indexed lookup on
13 * idx_import_provenance (import_source, import_provenance) — not a scan +
14 * JSON-decode of every prior imported row. Each import batch is a separate HTTP
15 * request (fresh process), so there is no per-request set to preload; the
16 * in-request $seen cache only short-circuits rows this same request already
17 * wrote (a multi-campaign donation's repeated rows, or a re-uploaded CSV row).
18 *
19 * @package SureDonation
20 * @since 1.5.1
21 */
22
23 namespace SureDonation\Inc\Import\Charitable;
24
25 use SureDonation\Inc\Database\Tables\Donations;
26
27 // Exit if accessed directly.
28 defined( 'ABSPATH' ) || exit;
29
30 /**
31 * Provenance_Dedupe trait.
32 *
33 * @since 1.5.1
34 */
35 trait Provenance_Dedupe {
36
37 /**
38 * Keys ("<post>:<token>") written earlier in *this* request, so a repeated
39 * row skips without a second DB round-trip. Not a full prior-import set —
40 * that lives in the indexed column and is queried per row.
41 *
42 * @var array<string, bool>
43 * @since 1.5.1
44 */
45 protected $seen = [];
46
47 /**
48 * Stable dedupe key for a (donation post, source campaign) pair.
49 *
50 * Delegates to Donations::build_provenance_key() so the importer and the v6
51 * backfill derive byte-identical keys. The label is the blank-campaign-id
52 * fallback (a CSV export that left the Campaign ID cell empty): keying on the
53 * campaign title keeps the per-campaign rows of a multi-campaign donation
54 * distinct instead of collapsing to "<post>:0" and being deduped away.
55 *
56 * @param int $donation_post_id Charitable donation post ID.
57 * @param int $source_campaign_id Charitable campaign ID (0 when unknown).
58 * @param string $campaign_label Campaign title/name fallback (optional).
59 * @return string
60 * @since 1.5.1
61 */
62 protected function provenance_key( $donation_post_id, $source_campaign_id, $campaign_label = '' ) {
63 return Donations::build_provenance_key( $donation_post_id, $source_campaign_id, $campaign_label );
64 }
65
66 /**
67 * Whether a (donation post, source campaign) pair has already been imported
68 * — earlier in this request, or by any prior import (indexed column lookup).
69 *
70 * Restricted to one-time rows: a Pro subscription plan-post row (donation_type
71 * = 'recurring') never masks a one-time donation that shares a post id.
72 *
73 * @param int $donation_post_id Charitable donation post ID.
74 * @param int $source_campaign_id Charitable campaign ID (0 when unknown).
75 * @param string $campaign_label Campaign title/name fallback (optional).
76 * @return bool
77 * @since 1.5.1
78 */
79 protected function provenance_seen( $donation_post_id, $source_campaign_id, $campaign_label = '' ) {
80 $key = $this->provenance_key( $donation_post_id, $source_campaign_id, $campaign_label );
81 if ( isset( $this->seen[ $key ] ) ) {
82 return true;
83 }
84
85 global $wpdb;
86 $table = $wpdb->prefix . 'suredonation_donations';
87
88 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Indexed per-row dedupe lookup during a migration; not cacheable.
89 $found = $wpdb->get_var(
90 $wpdb->prepare(
91 'SELECT id FROM %i WHERE import_source = %s AND import_provenance = %s AND donation_type != %s LIMIT 1',
92 $table,
93 'charitable',
94 $key,
95 'recurring'
96 )
97 );
98
99 return null !== $found;
100 }
101
102 /**
103 * Record a (donation post, source campaign) pair as written this request so
104 * a later row in the same request skips it.
105 *
106 * @param int $donation_post_id Charitable donation post ID.
107 * @param int $source_campaign_id Charitable campaign ID (0 when unknown).
108 * @param string $campaign_label Campaign title/name fallback (optional).
109 * @return void
110 * @since 1.5.1
111 */
112 protected function mark_provenance_seen( $donation_post_id, $source_campaign_id, $campaign_label = '' ) {
113 $this->seen[ $this->provenance_key( $donation_post_id, $source_campaign_id, $campaign_label ) ] = true;
114 }
115 }
116