PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.16.9
GiveWP – Donation Plugin and Fundraising Platform v4.16.9
4.16.9 4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 All 255 releases
give / src / DonationForms / Repositories / DonationFormRepository.php

DonationFormRepository.php in GiveWP – Donation Plugin and Fundraising Platform 4.16.9, at src/DonationForms/Repositories/DonationFormRepository.php

536 lines 16.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Give\DonationForms\Repositories;
4
5 use Closure;
6 use Give\DonationForms\Actions\ConvertDonationFormBlocksToFieldsApi;
7 use Give\DonationForms\DonationQuery;
8 use Give\DonationForms\Models\DonationForm;
9 use Give\DonationForms\ValueObjects\DonationFormMetaKeys;
10 use Give\Donations\ValueObjects\DonationMetaKeys;
11 use Give\Framework\Blocks\BlockCollection;
12 use Give\Framework\Database\DB;
13 use Give\Framework\Exceptions\Primitives\Exception;
14 use Give\Framework\Exceptions\Primitives\InvalidArgumentException;
15 use Give\Framework\FieldsAPI\DonationForm as DonationFormNode;
16 use Give\Framework\FieldsAPI\Exceptions\NameCollisionException;
17 use Give\Framework\FieldsAPI\Hidden;
18 use Give\Framework\FieldsAPI\Section;
19 use Give\Framework\Models\ModelQueryBuilder;
20 use Give\Framework\PaymentGateways\PaymentGateway;
21 use Give\Framework\PaymentGateways\PaymentGatewayRegister;
22 use Give\Framework\Support\Facades\DateTime\Temporal;
23 use Give\Helpers\Form\Utils;
24 use Give\Helpers\Hooks;
25 use Give\Log\Log;
26
27 /**
28 * @since 3.0.0
29 */
30 class DonationFormRepository
31 {
32 /**
33 * @var PaymentGatewayRegister
34 */
35 private $paymentGatewayRegister;
36
37 /**
38 * @since 3.0.0
39 *
40 * @var string[]
41 */
42 private $requiredProperties = [
43 'status',
44 'title',
45 'blocks',
46 ];
47
48 /**
49 * @since 3.0.0
50 *
51 * @param PaymentGatewayRegister $paymentGatewayRegister
52 */
53 public function __construct(PaymentGatewayRegister $paymentGatewayRegister)
54 {
55 $this->paymentGatewayRegister = $paymentGatewayRegister;
56 }
57
58 /**
59 * Get Donation Form By ID
60 *
61 * @since 3.0.0
62 *
63 * @return DonationForm|null
64 */
65 public function getById(int $id)
66 {
67 return $this->prepareQuery()
68 ->where('ID', $id)
69 ->get();
70 }
71
72 /**
73 * @since 3.7.0 Add post_excerpt to the list of fields being inserted
74 * @since 3.0.0
75 *
76 * @return void
77 * @throws Exception|InvalidArgumentException
78 */
79 public function insert(DonationForm $donationForm)
80 {
81 $this->validateProperties($donationForm);
82
83 Hooks::doAction('givewp_donation_form_creating', $donationForm);
84
85 $dateCreated = Temporal::withoutMicroseconds($donationForm->createdAt ?: Temporal::getCurrentDateTime());
86 $dateCreatedFormatted = Temporal::getFormattedDateTime($dateCreated);
87
88 $donationForm->settings->pageSlug = wp_unique_post_slug(
89 $donationForm->settings->pageSlug ?: sanitize_title($donationForm->title),
90 $donationForm->id,
91 $donationForm->status->getValue(),
92 'give_forms',
93 0
94 );
95
96 DB::query('START TRANSACTION');
97
98 try {
99 DB::table('posts')
100 ->insert([
101 'post_date' => $dateCreatedFormatted,
102 'post_date_gmt' => get_gmt_from_date($dateCreatedFormatted),
103 'post_modified' => $dateCreatedFormatted,
104 'post_modified_gmt' => get_gmt_from_date($dateCreatedFormatted),
105 'post_status' => $donationForm->status->getValue(),
106 'post_type' => 'give_forms',
107 'post_excerpt' => $donationForm->settings->formExcerpt,
108 'post_parent' => 0,
109 'post_title' => $donationForm->title,
110 'post_content' => (new BlockCollection([]))->toJson(), // @todo Repurpose as form page.
111 'post_name' => $donationForm->settings->pageSlug,
112 ]);
113
114 $donationFormId = DB::last_insert_id();
115
116 DB::table('give_formmeta')
117 ->insert([
118 'form_id' => $donationFormId,
119 'meta_key' => DonationFormMetaKeys::SETTINGS()->getValue(),
120 'meta_value' => $donationForm->settings->toJson(),
121 ]);
122
123 DB::table('give_formmeta')
124 ->insert([
125 'form_id' => $donationFormId,
126 'meta_key' => DonationFormMetaKeys::FIELDS()->getValue(),
127 'meta_value' => $donationForm->blocks->toJson(),
128 ]);
129 } catch (Exception $exception) {
130 DB::query('ROLLBACK');
131
132 Log::error('Failed creating a donation form', compact('donationForm'));
133
134 throw new $exception('Failed creating a donation form');
135 }
136
137 DB::query('COMMIT');
138
139 $donationForm->id = $donationFormId;
140
141 $donationForm->createdAt = $dateCreated;
142
143 if (!isset($donation->updatedAt)) {
144 $donationForm->updatedAt = $donationForm->createdAt;
145 }
146
147 Hooks::doAction('givewp_donation_form_created', $donationForm);
148 }
149
150 /**
151 * @since 3.7.0 Add post_excerpt to the list of fields being updated
152 * @since 3.0.0
153 *
154 * @param DonationForm $donationForm
155 *
156 * @return void
157 * @throws Exception|InvalidArgumentException
158 */
159 public function update(DonationForm $donationForm)
160 {
161 $this->validateProperties($donationForm);
162
163 Hooks::doAction('givewp_donation_form_updating', $donationForm);
164
165 $date = Temporal::getCurrentFormattedDateForDatabase();
166
167 $donationForm->settings->pageSlug = wp_unique_post_slug(
168 $donationForm->settings->pageSlug ?: sanitize_title($donationForm->title),
169 $donationForm->id,
170 $donationForm->status->getValue(),
171 'give_forms',
172 0
173 );
174
175 DB::query('START TRANSACTION');
176
177 try {
178 DB::table('posts')
179 ->where('ID', $donationForm->id)
180 ->update([
181 'post_modified' => $date,
182 'post_modified_gmt' => get_gmt_from_date($date),
183 'post_status' => $donationForm->status->getValue(),
184 'post_title' => $donationForm->title,
185 'post_excerpt' => $donationForm->settings->formExcerpt,
186 'post_content' => (new BlockCollection([]))->toJson(), // @todo Repurpose as form page.
187 'post_name' => $donationForm->settings->pageSlug,
188 ]);
189
190 DB::table('give_formmeta')
191 ->where('form_id', $donationForm->id)
192 ->where('meta_key', DonationFormMetaKeys::SETTINGS()->getValue())
193 ->update([
194 'meta_value' => $donationForm->settings->toJson(),
195 ]);
196
197
198 DB::table('give_formmeta')
199 ->where('form_id', $donationForm->id)
200 ->where('meta_key', DonationFormMetaKeys::FIELDS()->getValue())
201 ->update([
202 'meta_value' => $donationForm->blocks->toJson(),
203 ]);
204 } catch (Exception $exception) {
205 DB::query('ROLLBACK');
206
207 Log::error('Failed updating a donation form', compact('donationForm'));
208
209 throw new $exception('Failed updating a donation form');
210 }
211
212 DB::query('COMMIT');
213
214 Hooks::doAction('givewp_donation_form_updated', $donationForm);
215 }
216
217 /**
218 * @since 3.0.0
219 *
220 * @throws Exception
221 */
222 public function delete(DonationForm $donationForm): bool
223 {
224 DB::query('START TRANSACTION');
225
226 Hooks::doAction('givewp_donation_form_deleting', $donationForm);
227
228 try {
229 DB::table('posts')
230 ->where('id', $donationForm->id)
231 ->delete();
232
233 DB::table('give_formmeta')
234 ->where('form_id', $donationForm->id)
235 ->delete();
236 } catch (Exception $exception) {
237 DB::query('ROLLBACK');
238
239 Log::error('Failed deleting a donation form', compact('donationForm'));
240
241 throw new $exception('Failed deleting a donation form');
242 }
243
244 DB::query('COMMIT');
245
246 Hooks::doAction('givewp_donation_form_deleted', $donationForm);
247
248 return true;
249 }
250
251 /**
252 * @since 3.0.0
253 *
254 * @param DonationForm $donationForm
255 *
256 * @return void
257 */
258 private function validateProperties(DonationForm $donationForm)
259 {
260 foreach ($this->requiredProperties as $key) {
261 if (!isset($donationForm->$key)) {
262 throw new InvalidArgumentException("'$key' is required.");
263 }
264 }
265 }
266
267 /**
268 * @since 3.12.1 Prevent returning forms without the "formBuilderSettings" and "formBuilderFields" meta keys
269 * @since 3.0.0
270 *
271 * @return ModelQueryBuilder<DonationForm>
272 */
273 public function prepareQuery(): ModelQueryBuilder
274 {
275 $builder = new ModelQueryBuilder(DonationForm::class);
276
277 return $builder->from('posts', 'forms')
278 ->select(
279 ['ID', 'id'],
280 ['post_date', 'createdAt'],
281 ['post_modified', 'updatedAt'],
282 ['post_status', 'status'],
283 ['post_title', 'title'],
284 ['post_content', 'page_content'] // @todo Repurpose as form page.
285 )
286 ->attachMeta(
287 'give_formmeta',
288 'ID',
289 'form_id',
290 ...DonationFormMetaKeys::getColumnsForAttachMetaQuery()
291 )
292 ->whereIsNotNull("give_formmeta_attach_meta_settings.meta_value")
293 ->whereIsNotNull("give_formmeta_attach_meta_fields.meta_value")
294 ->where('post_type', 'give_forms');
295 }
296
297 /**
298 * @return PaymentGateway[]
299 */
300 public function getEnabledPaymentGateways($formId): array
301 {
302 $gateways = [];
303
304 $enabledGateways = give_get_option('gateways_v3', []);
305
306 if (!empty($enabledGateways)) {
307 foreach ($enabledGateways as $gatewayId => $enabled) {
308 if (!$enabled || !$this->paymentGatewayRegister->hasPaymentGateway($gatewayId)) {
309 continue;
310 }
311
312 $gateway = $this->paymentGatewayRegister->getPaymentGateway($gatewayId);
313
314 if (!in_array(3, $gateway->supportsFormVersions(), true)) {
315 continue;
316 }
317
318 $gateways[$gatewayId] = $gateway;
319 }
320
321 $defaultGateway = give_get_default_gateway($formId, 3);
322
323 if (array_key_exists($defaultGateway, $gateways)) {
324 $gateways = array_merge([$defaultGateway => $gateways[$defaultGateway]], $gateways);
325 }
326 }
327
328 return apply_filters('givewp_donation_form_enabled_gateways', $gateways, $formId);
329 }
330
331 /**
332 * @since 3.0.0
333 */
334 public function getDefaultEnabledGatewayId(int $formId): string
335 {
336 $gateways = $this->getEnabledPaymentGateways($formId);
337
338 return !empty($gateways) ? current($gateways)::id() : '';
339 }
340
341 /**
342 * @since 3.0.0
343 */
344 public function getFormDataGateways(int $formId): array
345 {
346 $formDataGateways = [];
347
348 foreach ($this->getEnabledPaymentGateways($formId) as $gateway) {
349 $gatewayId = $gateway::id();
350 $settings = $this->getGatewayFormSettings($formId, $gateway);
351 $label = give_get_gateway_checkout_label($gatewayId, 3) ?? $gateway->getPaymentMethodLabel();
352
353 /*
354 * TODO: Make gateway arrayable
355 */
356 $formDataGateways[] = [
357 'id' => $gatewayId,
358 'label' => $label,
359 'supportsSubscriptions' => $gateway->supportsSubscriptions(),
360 'settings' => $settings,
361 ];
362 }
363
364 return $formDataGateways;
365 }
366
367 /**
368 *
369 * @since 3.0.0
370 */
371 public function getTotalNumberOfDonors(int $formId): int
372 {
373 return DB::table('give_donationmeta')
374 ->where('meta_key', DonationMetaKeys::DONOR_ID)
375 ->whereIn('donation_id', function ($builder) use ($formId) {
376 $builder
377 ->select('donation_id')
378 ->from('give_donationmeta')
379 ->where('meta_key', DonationMetaKeys::FORM_ID)
380 ->where('meta_value', $formId);
381 })->count('DISTINCT meta_value');
382 }
383
384 /**
385 *
386 * @since 3.0.0
387 */
388 public function getTotalNumberOfDonorsFromSubscriptions(int $formId): int
389 {
390 return DB::table('give_subscriptions')
391 ->where('product_id', $formId)
392 ->count('DISTINCT customer_id');
393 }
394
395 /**
396 * @since 3.0.0
397 */
398 public function getTotalNumberOfDonations(int $formId): int
399 {
400 return (new DonationQuery)
401 ->form($formId)
402 ->count();
403 }
404
405 /**
406 * @since 3.0.0
407 */
408 public function getTotalNumberOfSubscriptions(int $formId): int
409 {
410 return DB::table('give_subscriptions')
411 ->where('product_id', $formId)
412 ->count();
413 }
414
415 /**
416 * @since 3.12.0 Update query to use intended amounts (without recovered fees).
417 * @since 3.0.0
418 */
419 public function getTotalRevenue(int $formId): int
420 {
421 return (int) (new DonationQuery)
422 ->form($formId)
423 ->sumIntendedAmount();
424 }
425
426 /**
427 * @since 3.0.0
428 * @return int|float
429 */
430 public function getTotalInitialAmountFromSubscriptions(int $formId)
431 {
432 return DB::table('give_subscriptions')
433 ->where('product_id', $formId)
434 ->sum('initial_amount');
435 }
436
437 /**
438 * @since 3.0.0
439 * @throws NameCollisionException
440 */
441 public function getFormSchemaFromBlocks(int $formId, BlockCollection $blocks): DonationFormNode
442 {
443 try {
444 [$form, $blockNodeRelationships] = (new ConvertDonationFormBlocksToFieldsApi())($blocks, $formId);
445 $formNodes = $form->all();
446
447 /** @var Section $firstSection */
448 $firstSection = $form->count() ? $formNodes[0] : null;
449
450 if ($firstSection) {
451 $firstSection->append(
452 Hidden::make('formId')
453 ->defaultValue($formId)
454 ->rules(
455 'required', 'integer',
456 function ($value, Closure $fail, string $key, array $values) use ($formId) {
457 if ($value !== $formId) {
458 $fail('Invalid donation form ID');
459 }
460 }
461 )
462 );
463 }
464 } catch (NameCollisionException $exception) {
465 throw $exception;
466 } catch (Exception $exception) {
467 Log::error('Failed converting donation form blocks to fields', compact('formId', 'blocks'));
468
469 $form = new DonationFormNode('donation-form');
470 $blockNodeRelationships = [];
471 }
472
473 Hooks::doAction('givewp_donation_form_schema', $form, $formId, $blockNodeRelationships);
474
475 return $form;
476 }
477
478 /**
479 * @since 3.0.3 Use isV3Form() method instead of 'post_content' to check if it's a legacy form
480 * @since 3.0.0
481 */
482 public function isLegacyForm(int $formId): bool
483 {
484 return ! Utils::isV3Form($formId);
485 }
486
487 /**
488 * @since 4.7.0
489 */
490 public function getColorSettings(DonationForm $donationForm): array
491 {
492 $primaryColor = $donationForm->settings->primaryColor;
493 $secondaryColor = $donationForm->settings->secondaryColor;
494
495 if ($donationForm->settings->inheritCampaignColors) {
496 /** @var Campaign $campaign */
497 $campaign = give()->campaigns->getByFormId($donationForm->id);
498
499 if ($campaign) {
500 $primaryColor = $campaign->primaryColor;
501 $secondaryColor = $campaign->secondaryColor;
502 }
503 }
504
505 return [
506 'primaryColor' => $primaryColor,
507 'secondaryColor' => $secondaryColor,
508 ];
509 }
510
511 /**
512 * Get gateway form settings and handle any exceptions.
513 *
514 * @since 3.0.0
515 */
516 private function getGatewayFormSettings(int $formId, PaymentGateway $gateway): array
517 {
518 if (!method_exists($gateway, 'formSettings')) {
519 return [];
520 }
521
522 try {
523 return $gateway->formSettings($formId);
524 } catch (\Exception $exception) {
525 $gatewayName = $gateway->getName();
526 Log::error("Failed getting gateway ($gatewayName) form settings", [
527 'formId' => $formId,
528 'gateway' => $gatewayName,
529 'error' => $exception->getMessage(),
530 ]);
531
532 return [];
533 }
534 }
535 }
536