CampaignPageRepository.php
7 months ago
CampaignRepository.php
1 year ago
CampaignsDataRepository.php
9 months ago
CampaignRepository.php
403 lines
| 1 | <?php |
| 2 | |
| 3 | namespace Give\Campaigns\Repositories; |
| 4 | |
| 5 | use Exception; |
| 6 | use Give\Campaigns\Models\Campaign; |
| 7 | use Give\Campaigns\ValueObjects\CampaignType; |
| 8 | use Give\Donations\ValueObjects\DonationMetaKeys; |
| 9 | use Give\Framework\Database\DB; |
| 10 | use Give\Framework\Exceptions\Primitives\InvalidArgumentException; |
| 11 | use Give\Framework\Models\ModelQueryBuilder; |
| 12 | use Give\Framework\Support\Facades\DateTime\Temporal; |
| 13 | use Give\Helpers\Hooks; |
| 14 | use Give\Log\Log; |
| 15 | |
| 16 | /** |
| 17 | * @since 4.0.0 |
| 18 | */ |
| 19 | class CampaignRepository |
| 20 | { |
| 21 | /** |
| 22 | * @since 4.0.0 |
| 23 | * |
| 24 | * @var string[] |
| 25 | */ |
| 26 | private $requiredProperties = [ |
| 27 | 'title', |
| 28 | 'status', |
| 29 | ]; |
| 30 | |
| 31 | /** |
| 32 | * @since 4.0.0 |
| 33 | * |
| 34 | * Get Campaign by ID |
| 35 | * |
| 36 | * @since 4.0.0 |
| 37 | */ |
| 38 | public function getById(int $id) |
| 39 | { |
| 40 | return $this->queryById($id)->get(); |
| 41 | } |
| 42 | |
| 43 | /** |
| 44 | * @since 4.0.0 |
| 45 | * |
| 46 | * Query Campaign by ID |
| 47 | * |
| 48 | * @since 4.0.0 |
| 49 | */ |
| 50 | public function queryById(int $id): ModelQueryBuilder |
| 51 | { |
| 52 | return $this->prepareQuery() |
| 53 | ->where('id', $id); |
| 54 | } |
| 55 | |
| 56 | /** |
| 57 | * @since 4.0.0 |
| 58 | * |
| 59 | * Get Campaign by Form ID using a lookup table |
| 60 | */ |
| 61 | public function getByFormId(int $formId) |
| 62 | { |
| 63 | return $this->queryByFormId($formId)->get(); |
| 64 | } |
| 65 | |
| 66 | /** |
| 67 | * @since 4.0.0 |
| 68 | * |
| 69 | * @param int $formId |
| 70 | * |
| 71 | * @return ModelQueryBuilder<Campaign> |
| 72 | */ |
| 73 | public function queryByFormId(int $formId): ModelQueryBuilder |
| 74 | { |
| 75 | return $this->prepareQuery() |
| 76 | ->leftJoin('give_campaign_forms', 'campaigns.id', 'forms.campaign_id', 'forms') |
| 77 | ->where('forms.form_id', $formId); |
| 78 | } |
| 79 | |
| 80 | /** |
| 81 | * @since 4.0.0 |
| 82 | * |
| 83 | * @throws Exception|InvalidArgumentException |
| 84 | */ |
| 85 | public function insert(Campaign $campaign): void |
| 86 | { |
| 87 | $this->validateProperties($campaign); |
| 88 | |
| 89 | Hooks::doAction('givewp_campaign_creating', $campaign); |
| 90 | |
| 91 | $currentDate = Temporal::getCurrentDateTime(); |
| 92 | |
| 93 | $dateCreated = Temporal::withoutMicroseconds($campaign->createdAt ?: $currentDate); |
| 94 | $dateCreatedFormatted = Temporal::getFormattedDateTime($dateCreated); |
| 95 | |
| 96 | $startDate = Temporal::withoutMicroseconds($campaign->startDate ?: $currentDate); |
| 97 | $startDateFormatted = Temporal::getFormattedDateTime($startDate); |
| 98 | |
| 99 | $endDateFormatted = $campaign->endDate ? Temporal::getFormattedDateTime($campaign->endDate) : $campaign->endDate; |
| 100 | |
| 101 | DB::query('START TRANSACTION'); |
| 102 | |
| 103 | try { |
| 104 | DB::table('give_campaigns') |
| 105 | ->insert([ |
| 106 | 'campaign_type' => $campaign->type->getValue(), |
| 107 | 'campaign_title' => wp_strip_all_tags($campaign->title, true), |
| 108 | 'short_desc' => wp_strip_all_tags($campaign->shortDescription), |
| 109 | 'long_desc' => wp_strip_all_tags($campaign->longDescription), |
| 110 | 'campaign_logo' => $campaign->logo, |
| 111 | 'campaign_image' => $campaign->image, |
| 112 | 'primary_color' => $campaign->primaryColor, |
| 113 | 'secondary_color' => $campaign->secondaryColor, |
| 114 | 'campaign_goal' => $campaign->goal, |
| 115 | 'goal_type' => $campaign->goalType->getValue(), |
| 116 | 'status' => $campaign->status->getValue(), |
| 117 | 'start_date' => $startDateFormatted, |
| 118 | 'end_date' => $endDateFormatted, |
| 119 | 'date_created' => $dateCreatedFormatted, |
| 120 | ]); |
| 121 | |
| 122 | $campaignId = DB::last_insert_id(); |
| 123 | |
| 124 | } catch (Exception $exception) { |
| 125 | DB::query('ROLLBACK'); |
| 126 | |
| 127 | Log::error('Failed creating a campaign', compact('campaign')); |
| 128 | |
| 129 | throw new $exception('Failed creating a campaign'); |
| 130 | } |
| 131 | |
| 132 | DB::query('COMMIT'); |
| 133 | |
| 134 | $campaign->id = $campaignId; |
| 135 | $campaign->createdAt = $dateCreated; |
| 136 | $campaign->startDate = $startDate; |
| 137 | |
| 138 | Hooks::doAction('givewp_campaign_created', $campaign); |
| 139 | } |
| 140 | |
| 141 | /** |
| 142 | * @since 4.0.0 |
| 143 | * |
| 144 | * @throws Exception|InvalidArgumentException |
| 145 | */ |
| 146 | public function update(Campaign $campaign): void |
| 147 | { |
| 148 | $this->validateProperties($campaign); |
| 149 | |
| 150 | $startDateFormatted = Temporal::getFormattedDateTime($campaign->startDate); |
| 151 | $endDateFormatted = $campaign->endDate ? Temporal::getFormattedDateTime($campaign->endDate) : $campaign->endDate; |
| 152 | |
| 153 | Hooks::doAction('givewp_campaign_updating', $campaign); |
| 154 | |
| 155 | DB::query('START TRANSACTION'); |
| 156 | |
| 157 | try { |
| 158 | DB::table('give_campaigns') |
| 159 | ->where('id', $campaign->id) |
| 160 | ->update([ |
| 161 | 'campaign_type' => $campaign->type->getValue(), |
| 162 | 'campaign_page_id' => $campaign->pageId, |
| 163 | 'campaign_title' => wp_strip_all_tags($campaign->title, true), |
| 164 | 'short_desc' => wp_strip_all_tags($campaign->shortDescription), |
| 165 | 'long_desc' => wp_strip_all_tags($campaign->longDescription), |
| 166 | 'campaign_logo' => $campaign->logo, |
| 167 | 'campaign_image' => $campaign->image, |
| 168 | 'primary_color' => $campaign->primaryColor, |
| 169 | 'secondary_color' => $campaign->secondaryColor, |
| 170 | 'campaign_goal' => $campaign->goal, |
| 171 | 'goal_type' => $campaign->goalType->getValue(), |
| 172 | 'status' => $campaign->status->getValue(), |
| 173 | 'start_date' => $startDateFormatted, |
| 174 | 'end_date' => $endDateFormatted, |
| 175 | ]); |
| 176 | } catch (Exception $exception) { |
| 177 | DB::query('ROLLBACK'); |
| 178 | |
| 179 | Log::error('Failed updating a campaign', compact('campaign')); |
| 180 | |
| 181 | throw new $exception('Failed updating a campaign'); |
| 182 | } |
| 183 | |
| 184 | DB::query('COMMIT'); |
| 185 | |
| 186 | Hooks::doAction('givewp_campaign_updated', $campaign); |
| 187 | } |
| 188 | |
| 189 | /** |
| 190 | * @since 4.0.0 |
| 191 | * |
| 192 | * @throws Exception |
| 193 | */ |
| 194 | public function addCampaignForm(Campaign $campaign, int $donationFormId, bool $isDefault = false) |
| 195 | { |
| 196 | Hooks::doAction('givewp_campaign_form_relationship_creating', $campaign, $donationFormId, $isDefault); |
| 197 | |
| 198 | DB::query('START TRANSACTION'); |
| 199 | |
| 200 | try { |
| 201 | if ($isDefault) { |
| 202 | DB::table('give_campaigns') |
| 203 | ->where('id', $campaign->id) |
| 204 | ->update([ |
| 205 | 'form_id' => $donationFormId, |
| 206 | ]); |
| 207 | |
| 208 | $campaign->defaultFormId = $donationFormId; |
| 209 | } |
| 210 | |
| 211 | DB::table('give_campaign_forms') |
| 212 | ->insert([ |
| 213 | 'form_id' => $donationFormId, |
| 214 | 'campaign_id' => $campaign->id, |
| 215 | ]); |
| 216 | } catch (Exception $exception) { |
| 217 | DB::query('ROLLBACK'); |
| 218 | |
| 219 | Log::error('Failed creating a campaign form relationship', compact('campaign')); |
| 220 | |
| 221 | throw new $exception('Failed creating a campaign form relationship'); |
| 222 | } |
| 223 | |
| 224 | DB::query('COMMIT'); |
| 225 | |
| 226 | Hooks::doAction('givewp_campaign_form_relationship_created', $campaign, $donationFormId, $isDefault); |
| 227 | } |
| 228 | |
| 229 | /** |
| 230 | * @since 4.0.0 |
| 231 | * |
| 232 | * @throws Exception |
| 233 | */ |
| 234 | public function updateDefaultCampaignForm(Campaign $campaign, int $donationFormId) |
| 235 | { |
| 236 | Hooks::doAction('givewp_campaign_default_form_updating', $campaign, $donationFormId); |
| 237 | |
| 238 | DB::query('START TRANSACTION'); |
| 239 | |
| 240 | try { |
| 241 | DB::table('give_campaigns') |
| 242 | ->where('id', $campaign->id) |
| 243 | ->update([ |
| 244 | 'form_id' => $donationFormId |
| 245 | ]); |
| 246 | |
| 247 | $campaign->defaultFormId = $donationFormId; |
| 248 | } catch (Exception $exception) { |
| 249 | DB::query('ROLLBACK'); |
| 250 | |
| 251 | Log::error('Failed updating the campaign default form', compact('campaign')); |
| 252 | |
| 253 | throw new $exception('Failed updating the campaign default form'); |
| 254 | } |
| 255 | |
| 256 | DB::query('COMMIT'); |
| 257 | |
| 258 | Hooks::doAction('givewp_campaign_default_form_updated', $campaign, $donationFormId); |
| 259 | } |
| 260 | |
| 261 | /** |
| 262 | * @since 4.0.0 |
| 263 | * |
| 264 | * @throws Exception |
| 265 | */ |
| 266 | public function delete(Campaign $campaign): bool |
| 267 | { |
| 268 | DB::query('START TRANSACTION'); |
| 269 | |
| 270 | Hooks::doAction('givewp_campaign_deleting', $campaign); |
| 271 | |
| 272 | try { |
| 273 | DB::table('give_campaigns') |
| 274 | ->where('id', $campaign->id) |
| 275 | ->delete(); |
| 276 | |
| 277 | } catch (Exception $exception) { |
| 278 | DB::query('ROLLBACK'); |
| 279 | |
| 280 | Log::error('Failed deleting a campaign', compact('campaign')); |
| 281 | |
| 282 | throw new $exception('Failed deleting a campaign'); |
| 283 | } |
| 284 | |
| 285 | DB::query('COMMIT'); |
| 286 | |
| 287 | Hooks::doAction('givewp_campaign_deleted', $campaign); |
| 288 | |
| 289 | return true; |
| 290 | } |
| 291 | |
| 292 | /** |
| 293 | * @since 4.0.0 |
| 294 | * |
| 295 | * @throws Exception |
| 296 | */ |
| 297 | public function mergeCampaigns(Campaign $destinationCampaign, Campaign ...$campaignsToMerge): bool |
| 298 | { |
| 299 | // Make sure the destination campaign ID will not be included into $campaignsToMergeIds |
| 300 | $campaignsToMergeIds = array_column($campaignsToMerge, 'id'); |
| 301 | if ($key = array_search($destinationCampaign->id, $campaignsToMergeIds, true)) { |
| 302 | unset($campaignsToMergeIds[$key]); |
| 303 | } |
| 304 | |
| 305 | Hooks::doAction('givewp_campaigns_merging', $destinationCampaign, $campaignsToMergeIds); |
| 306 | |
| 307 | DB::query('START TRANSACTION'); |
| 308 | |
| 309 | try { |
| 310 | // Convert $campaignsToMergeIds to string to use it in the queries |
| 311 | $campaignsToMergeIdsString = implode(', ', $campaignsToMergeIds); |
| 312 | |
| 313 | // Migrate revenue entries from campaigns to merge to the destination campaign |
| 314 | DB::query( |
| 315 | DB::prepare("UPDATE " . DB::prefix('give_revenue') . " SET campaign_id = %d WHERE campaign_id IN ($campaignsToMergeIdsString)", |
| 316 | [ |
| 317 | $destinationCampaign->id, |
| 318 | ]) |
| 319 | ); |
| 320 | |
| 321 | // Migrate forms from campaigns to merge to the destination campaign |
| 322 | DB::query( |
| 323 | DB::prepare("UPDATE " . DB::prefix('give_campaign_forms') . " SET campaign_id = %d WHERE campaign_id IN ($campaignsToMergeIdsString)", |
| 324 | [ |
| 325 | $destinationCampaign->id, |
| 326 | ]) |
| 327 | ); |
| 328 | |
| 329 | // Update donations campaign id meta value |
| 330 | DB::query( |
| 331 | DB::prepare("UPDATE " . DB::prefix('give_donationmeta') . " SET meta_value = %d WHERE meta_key = %s AND meta_value IN ($campaignsToMergeIdsString)", |
| 332 | [ |
| 333 | $destinationCampaign->id, |
| 334 | DonationMetaKeys::CAMPAIGN_ID |
| 335 | ]) |
| 336 | ); |
| 337 | |
| 338 | // Delete campaigns to merge now that we already migrated the necessary data to the destination campaign |
| 339 | DB::query("DELETE FROM " . DB::prefix('give_campaigns') . " WHERE id IN ($campaignsToMergeIdsString)"); |
| 340 | } catch (Exception $exception) { |
| 341 | DB::query('ROLLBACK'); |
| 342 | |
| 343 | Log::error('Failed merging campaigns into destination campaign', [ |
| 344 | 'campaignsToMergeIds' => $campaignsToMergeIds, |
| 345 | 'destinationCampaign' => compact('destinationCampaign'), |
| 346 | ]); |
| 347 | |
| 348 | throw new $exception('Failed merging campaigns into destination campaign'); |
| 349 | } |
| 350 | |
| 351 | DB::query('COMMIT'); |
| 352 | |
| 353 | Hooks::doAction('givewp_campaigns_merged', $destinationCampaign, $campaignsToMergeIds); |
| 354 | |
| 355 | return true; |
| 356 | } |
| 357 | |
| 358 | /** |
| 359 | * @since 4.0.0 |
| 360 | */ |
| 361 | private function validateProperties(Campaign $campaign): void |
| 362 | { |
| 363 | foreach ($this->requiredProperties as $key) { |
| 364 | if ( ! isset($campaign->$key)) { |
| 365 | throw new InvalidArgumentException("'$key' is required."); |
| 366 | } |
| 367 | } |
| 368 | } |
| 369 | |
| 370 | /** |
| 371 | * @since 4.0.0 |
| 372 | * |
| 373 | * @return ModelQueryBuilder<Campaign> |
| 374 | */ |
| 375 | public function prepareQuery(): ModelQueryBuilder |
| 376 | { |
| 377 | $builder = new ModelQueryBuilder(Campaign::class); |
| 378 | |
| 379 | return $builder->from('give_campaigns', 'campaigns') |
| 380 | ->select( |
| 381 | ['campaigns.id', 'id'], |
| 382 | ['campaigns.form_id', 'defaultFormId'], // Prefix the `form_id` column to avoid conflicts with the `give_campaign_forms` table. |
| 383 | ['campaign_type', 'type'], |
| 384 | ['campaign_page_id', 'pageId'], |
| 385 | ['campaign_title', 'title'], |
| 386 | ['short_desc', 'shortDescription'], |
| 387 | ['long_desc', 'longDescription'], |
| 388 | ['campaign_logo', 'logo'], |
| 389 | ['campaign_image', 'image'], |
| 390 | ['primary_color', 'primaryColor'], |
| 391 | ['secondary_color', 'secondaryColor'], |
| 392 | ['campaign_goal', 'goal'], |
| 393 | ['goal_type', 'goalType'], |
| 394 | 'status', |
| 395 | ['start_date', 'startDate'], |
| 396 | ['end_date', 'endDate'], |
| 397 | ['date_created', 'createdAt'] |
| 398 | ) |
| 399 | // Select only core campaign |
| 400 | ->where('campaigns.campaign_type', CampaignType::CORE); |
| 401 | } |
| 402 | } |
| 403 |