DonationFormDataRepository.php
273 lines
| 1 | <?php |
| 2 | |
| 3 | namespace Give\DonationForms\Repositories; |
| 4 | |
| 5 | |
| 6 | use Give\Campaigns\CampaignsDataQuery; |
| 7 | use Give\Campaigns\ValueObjects\CampaignGoalType; |
| 8 | use Give\DonationForms\DonationFormDataQuery; |
| 9 | use Give\DonationForms\V2\Models\DonationForm; |
| 10 | use Give\DonationForms\ValueObjects\GoalSource; |
| 11 | use Give\DonationForms\ValueObjects\GoalType; |
| 12 | use Give\Framework\Database\DB; |
| 13 | |
| 14 | /** |
| 15 | * @since 4.3.0 |
| 16 | * |
| 17 | * Used to optimize the donation forms list table performance and to avoid n+1 problems. |
| 18 | */ |
| 19 | class DonationFormDataRepository |
| 20 | { |
| 21 | private array $formAmounts = []; |
| 22 | private array $campaignAmounts = []; |
| 23 | private array $formSubscriptionAmounts = []; |
| 24 | private array $campaignSubscriptionAmounts = []; |
| 25 | private array $formDonationsCount = []; |
| 26 | private array $campaignDonationsCount = []; |
| 27 | private array $formSubscriptionDonationsCount = []; |
| 28 | private array $campaignSubscriptionDonationsCount = []; |
| 29 | private array $formDonorsCount = []; |
| 30 | private array $campaignDonorsCount = []; |
| 31 | private array $formSubscriptionDonorsCount = []; |
| 32 | private array $campaignSubscriptionDonorsCount = []; |
| 33 | |
| 34 | /** |
| 35 | * @param DonationForm[] $forms |
| 36 | * |
| 37 | * @return DonationFormDataRepository |
| 38 | */ |
| 39 | public static function forms(array $forms): DonationFormDataRepository |
| 40 | { |
| 41 | $self = new self(); |
| 42 | |
| 43 | $formsUsingFormGoal = []; |
| 44 | $formsUsingCampaignGoal = []; |
| 45 | |
| 46 | foreach ($forms as $form) { |
| 47 | if ($form->goalSettings->goalSource === GoalSource::CAMPAIGN) { |
| 48 | $formsUsingCampaignGoal[] = $form->id; |
| 49 | } else { |
| 50 | $formsUsingFormGoal[] = $form->id; |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | if ( ! empty($formsUsingFormGoal)) { |
| 55 | $self->setFormsGoals($formsUsingFormGoal); |
| 56 | } |
| 57 | |
| 58 | if ( ! empty($formsUsingCampaignGoal)) { |
| 59 | $self->setFormsCampaignGoals($formsUsingCampaignGoal); |
| 60 | } |
| 61 | |
| 62 | return $self; |
| 63 | } |
| 64 | |
| 65 | |
| 66 | /** |
| 67 | * @since 4.3.0 |
| 68 | */ |
| 69 | public function setFormsGoals(array $ids): void |
| 70 | { |
| 71 | $donations = DonationFormDataQuery::donations($ids); |
| 72 | |
| 73 | $this->formAmounts = $donations->collectIntendedAmounts(); |
| 74 | $this->formDonationsCount = $donations->collectDonations(); |
| 75 | $this->formDonorsCount = $donations->collectDonors(); |
| 76 | |
| 77 | // Set subscriptions data |
| 78 | if (defined('GIVE_RECURRING_VERSION')) { |
| 79 | $subscriptions = DonationFormDataQuery::subscriptions($ids); |
| 80 | |
| 81 | $this->formSubscriptionAmounts = $subscriptions->collectInitialAmounts(); |
| 82 | $this->formSubscriptionDonationsCount = $subscriptions->collectDonations(); |
| 83 | $this->formSubscriptionDonorsCount = $subscriptions->collectDonors(); |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | /** |
| 88 | * @since 4.3.0 |
| 89 | */ |
| 90 | public function setFormsCampaignGoals(array $ids): void |
| 91 | { |
| 92 | $campaigns = DB::table('give_campaign_forms') |
| 93 | ->distinct() |
| 94 | ->select(['campaign_id', 'id']) |
| 95 | ->whereIn('form_id', $ids) |
| 96 | ->getAll(); |
| 97 | |
| 98 | $ids = array_map(function ($campaign) { |
| 99 | return (int)$campaign->id; |
| 100 | }, $campaigns); |
| 101 | |
| 102 | $donations = CampaignsDataQuery::donations($ids); |
| 103 | |
| 104 | $this->campaignAmounts = $donations->collectIntendedAmounts(); |
| 105 | $this->campaignDonationsCount = $donations->collectDonations(); |
| 106 | $this->campaignDonorsCount = $donations->collectDonors(); |
| 107 | |
| 108 | // Set subscriptions data |
| 109 | if (defined('GIVE_RECURRING_VERSION')) { |
| 110 | $subscriptions = CampaignsDataQuery::subscriptions($ids); |
| 111 | |
| 112 | $this->campaignSubscriptionAmounts = $subscriptions->collectInitialAmounts(); |
| 113 | $this->campaignSubscriptionDonationsCount = $subscriptions->collectDonations(); |
| 114 | $this->campaignSubscriptionDonorsCount = $subscriptions->collectDonors(); |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | /** |
| 119 | * @since 4.3.0 |
| 120 | * |
| 121 | * Get revenue for form |
| 122 | * |
| 123 | * @param DonationForm $form |
| 124 | * |
| 125 | * @return int|float |
| 126 | */ |
| 127 | public function getRevenue(DonationForm $form) |
| 128 | { |
| 129 | $data = $form->goalSettings->goalType->isAmountFromSubscriptions() |
| 130 | ? $this->{$form->goalSettings->goalSource . 'SubscriptionAmounts'} |
| 131 | : $this->{$form->goalSettings->goalSource . 'Amounts'}; |
| 132 | |
| 133 | $source = $this->getSourceData($form); |
| 134 | |
| 135 | foreach ($data as $row) { |
| 136 | if (isset($row[$source['column']]) && $row[$source['column']] == $source['id']) { |
| 137 | return $row['sum']; |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | return 0; |
| 142 | } |
| 143 | |
| 144 | /** |
| 145 | * @since 4.3.0 |
| 146 | * |
| 147 | * Get donations count for form |
| 148 | * |
| 149 | * @param DonationForm $form |
| 150 | * |
| 151 | * @return int |
| 152 | */ |
| 153 | public function getDonationsCount(DonationForm $form): int |
| 154 | { |
| 155 | $data = $form->goalSettings->goalType->isSubscriptions() |
| 156 | ? $this->{$form->goalSettings->goalSource . 'SubscriptionDonationsCount'} |
| 157 | : $this->{$form->goalSettings->goalSource . 'DonationsCount'}; |
| 158 | |
| 159 | $source = $this->getSourceData($form); |
| 160 | |
| 161 | foreach ($data as $row) { |
| 162 | if (isset($row[$source['column']]) && $row[$source['column']] == $source['id']) { |
| 163 | return (int)$row['count']; |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | return 0; |
| 168 | } |
| 169 | |
| 170 | /** |
| 171 | * @since 4.3.0 |
| 172 | * |
| 173 | * Get donors count for form |
| 174 | * |
| 175 | * @param DonationForm $form |
| 176 | * |
| 177 | * @return int |
| 178 | */ |
| 179 | public function getDonorsCount(DonationForm $form): int |
| 180 | { |
| 181 | $data = $form->goalSettings->goalType->isSubscriptions() |
| 182 | ? $this->{$form->goalSettings->goalSource . 'SubscriptionDonorsCount'} |
| 183 | : $this->{$form->goalSettings->goalSource . 'DonorsCount'}; |
| 184 | |
| 185 | $source = $this->getSourceData($form); |
| 186 | |
| 187 | foreach ($data as $row) { |
| 188 | if (isset($row[$source['column']]) && $row[$source['column']] == $source['id']) { |
| 189 | return (int)$row['count']; |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | return 0; |
| 194 | } |
| 195 | |
| 196 | |
| 197 | /** |
| 198 | * @since 4.3.0 |
| 199 | * |
| 200 | * Get goal data for form |
| 201 | * |
| 202 | * @param DonationForm $form |
| 203 | * |
| 204 | * @return array{actual: int, goal: int, actualFormatted: string, goalFormatted:string, percentage:float} |
| 205 | */ |
| 206 | public function getGoalData(DonationForm $form): array |
| 207 | { |
| 208 | $actual = $this->getActualGoal($form); |
| 209 | $percentage = $form->goalSettings->goalAmount |
| 210 | ? $actual / $form->goalSettings->goalAmount |
| 211 | : 0; |
| 212 | |
| 213 | $typeIsMoney = $form->goalSettings->goalType->isOneOf( |
| 214 | GoalType::AMOUNT(), |
| 215 | GoalType::AMOUNT_FROM_SUBSCRIPTIONS(), |
| 216 | ); |
| 217 | |
| 218 | return [ |
| 219 | 'actual' => $actual, |
| 220 | 'goal' => $form->goalSettings->goalAmount, |
| 221 | 'actualFormatted' => $typeIsMoney |
| 222 | ? give_currency_filter(give_format_amount($actual)) |
| 223 | : $actual, |
| 224 | 'goalFormatted' => $typeIsMoney |
| 225 | ? give_currency_filter(give_format_amount($form->goalSettings->goalAmount)) |
| 226 | : $form->goalSettings->goalAmount, |
| 227 | 'percentage' => round($percentage * 100, 2), |
| 228 | 'typeIsMoney' => $typeIsMoney, |
| 229 | ]; |
| 230 | } |
| 231 | |
| 232 | /** |
| 233 | * @since 4.3.0 |
| 234 | * |
| 235 | * @param DonationForm $form |
| 236 | * |
| 237 | * @return int|float |
| 238 | */ |
| 239 | private function getActualGoal(DonationForm $form) |
| 240 | { |
| 241 | switch ($form->goalSettings->goalType->getValue()) { |
| 242 | case GoalType::DONATIONS(): |
| 243 | case GoalType::SUBSCRIPTIONS(): |
| 244 | return $this->getDonationsCount($form); |
| 245 | case GoalType::DONORS(): |
| 246 | case GoalType::DONORS_FROM_SUBSCRIPTIONS(): |
| 247 | return $this->getDonorsCount($form); |
| 248 | default: |
| 249 | return $this->getRevenue($form); |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | * @param DonationForm $form |
| 255 | * |
| 256 | * @return array{id: int, column: string} |
| 257 | */ |
| 258 | private function getSourceData(DonationForm $form): array |
| 259 | { |
| 260 | if ($form->goalSettings->goalSource === GoalSource::CAMPAIGN) { |
| 261 | return [ |
| 262 | 'id' => $form->campaignId, |
| 263 | 'column' => 'campaign_id', |
| 264 | ]; |
| 265 | } |
| 266 | |
| 267 | return [ |
| 268 | 'id' => $form->id, |
| 269 | 'column' => 'form_id', |
| 270 | ]; |
| 271 | } |
| 272 | } |
| 273 |