CustomFieldsResponseBuilder.php
11 months ago
DynamicSegmentsResponseBuilder.php
2 months ago
FormsResponseBuilder.php
11 months ago
NewsletterTemplatesResponseBuilder.php
11 months ago
NewslettersResponseBuilder.php
3 weeks ago
ScheduledTaskSubscriberResponseBuilder.php
3 years ago
SegmentsResponseBuilder.php
2 months ago
SendingQueuesResponseBuilder.php
2 months ago
SubscribersResponseBuilder.php
2 weeks ago
index.php
3 years ago
SubscribersResponseBuilder.php
238 lines
| 1 | <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing |
| 2 | |
| 3 | namespace MailPoet\API\JSON\ResponseBuilders; |
| 4 | |
| 5 | if (!defined('ABSPATH')) exit; |
| 6 | |
| 7 | |
| 8 | use MailPoet\CustomFields\CustomFieldsRepository; |
| 9 | use MailPoet\Entities\NewsletterEntity; |
| 10 | use MailPoet\Entities\SegmentEntity; |
| 11 | use MailPoet\Entities\StatisticsUnsubscribeEntity; |
| 12 | use MailPoet\Entities\SubscriberCustomFieldEntity; |
| 13 | use MailPoet\Entities\SubscriberEntity; |
| 14 | use MailPoet\Statistics\StatisticsUnsubscribesRepository; |
| 15 | use MailPoet\Statistics\UnsubscribeReasonTracker; |
| 16 | use MailPoet\Subscribers\Statistics\SubscriberStatisticsRepository; |
| 17 | use MailPoet\Subscribers\SubscriberCustomFieldRepository; |
| 18 | use MailPoetVendor\Doctrine\ORM\EntityManager; |
| 19 | |
| 20 | class SubscribersResponseBuilder { |
| 21 | const DATE_FORMAT = 'Y-m-d H:i:s'; |
| 22 | |
| 23 | /** @var StatisticsUnsubscribesRepository */ |
| 24 | private $statisticsUnsubscribesRepository; |
| 25 | |
| 26 | /** @var CustomFieldsRepository */ |
| 27 | private $customFieldsRepository; |
| 28 | |
| 29 | /** @var SubscriberCustomFieldRepository */ |
| 30 | private $subscriberCustomFieldRepository; |
| 31 | |
| 32 | /** @var EntityManager */ |
| 33 | private $entityManager; |
| 34 | |
| 35 | /** @var UnsubscribeReasonTracker */ |
| 36 | private $unsubscribeReasonTracker; |
| 37 | |
| 38 | /** @var SubscriberStatisticsRepository */ |
| 39 | private $subscriberStatisticsRepository; |
| 40 | |
| 41 | public function __construct( |
| 42 | EntityManager $entityManager, |
| 43 | CustomFieldsRepository $customFieldsRepository, |
| 44 | SubscriberCustomFieldRepository $subscriberCustomFieldRepository, |
| 45 | StatisticsUnsubscribesRepository $statisticsUnsubscribesRepository, |
| 46 | UnsubscribeReasonTracker $unsubscribeReasonTracker, |
| 47 | SubscriberStatisticsRepository $subscriberStatisticsRepository |
| 48 | ) { |
| 49 | $this->statisticsUnsubscribesRepository = $statisticsUnsubscribesRepository; |
| 50 | $this->customFieldsRepository = $customFieldsRepository; |
| 51 | $this->subscriberCustomFieldRepository = $subscriberCustomFieldRepository; |
| 52 | $this->entityManager = $entityManager; |
| 53 | $this->unsubscribeReasonTracker = $unsubscribeReasonTracker; |
| 54 | $this->subscriberStatisticsRepository = $subscriberStatisticsRepository; |
| 55 | } |
| 56 | |
| 57 | public function buildForListing(array $subscribers): array { |
| 58 | $this->prefetchRelations($subscribers); |
| 59 | $engagementScoreTypes = $this->subscriberStatisticsRepository->getEngagementScoreTypes($subscribers); |
| 60 | $data = []; |
| 61 | foreach ($subscribers as $subscriber) { |
| 62 | $data[] = $this->buildListingItem( |
| 63 | $subscriber, |
| 64 | $engagementScoreTypes[(int)$subscriber->getId()] ?? SubscriberStatisticsRepository::ENGAGEMENT_SCORE_UNKNOWN |
| 65 | ); |
| 66 | } |
| 67 | return $data; |
| 68 | } |
| 69 | |
| 70 | private function buildListingItem(SubscriberEntity $subscriber, string $engagementScoreType): array { |
| 71 | return [ |
| 72 | 'id' => (string)$subscriber->getId(), // (string) for BC |
| 73 | 'email' => $subscriber->getEmail(), |
| 74 | 'first_name' => $subscriber->getFirstName(), |
| 75 | 'last_name' => $subscriber->getLastName(), |
| 76 | 'subscriptions' => $this->buildSubscriptions($subscriber), |
| 77 | 'status' => $subscriber->getStatus(), |
| 78 | 'count_confirmations' => $subscriber->getConfirmationsCount(), |
| 79 | 'wp_user_id' => $subscriber->getWpUserId(), |
| 80 | 'is_woocommerce_user' => $subscriber->getIsWoocommerceUser(), |
| 81 | 'created_at' => ($createdAt = $subscriber->getCreatedAt()) ? $createdAt->format(self::DATE_FORMAT) : null, |
| 82 | 'deleted_at' => ($deletedAt = $subscriber->getDeletedAt()) ? $deletedAt->format(self::DATE_FORMAT) : null, |
| 83 | 'last_subscribed_at' => ($lastSubscribedAt = $subscriber->getLastSubscribedAt()) ? $lastSubscribedAt->format(self::DATE_FORMAT) : null, |
| 84 | 'engagement_score' => $subscriber->getEngagementScore(), |
| 85 | 'engagement_score_type' => $engagementScoreType, |
| 86 | 'tags' => $this->buildTags($subscriber), |
| 87 | ]; |
| 88 | } |
| 89 | |
| 90 | public function build(SubscriberEntity $subscriberEntity): array { |
| 91 | $data = [ |
| 92 | 'id' => (string)$subscriberEntity->getId(), |
| 93 | 'wp_user_id' => $subscriberEntity->getWpUserId(), |
| 94 | 'is_woocommerce_user' => $subscriberEntity->getIsWoocommerceUser(), |
| 95 | 'subscriptions' => $this->buildSubscriptions($subscriberEntity), |
| 96 | 'unsubscribes' => $this->buildUnsubscribes($subscriberEntity), |
| 97 | 'status' => $subscriberEntity->getStatus(), |
| 98 | 'last_name' => $subscriberEntity->getLastName(), |
| 99 | 'first_name' => $subscriberEntity->getFirstName(), |
| 100 | 'email' => $subscriberEntity->getEmail(), |
| 101 | 'created_at' => ($createdAt = $subscriberEntity->getCreatedAt()) ? $createdAt->format(self::DATE_FORMAT) : null, |
| 102 | 'updated_at' => ($updatedAt = $subscriberEntity->getUpdatedAt()) ? $updatedAt->format(self::DATE_FORMAT) : null, |
| 103 | 'deleted_at' => ($deletedAt = $subscriberEntity->getDeletedAt()) ? $deletedAt->format(self::DATE_FORMAT) : null, |
| 104 | 'subscribed_ip' => $subscriberEntity->getSubscribedIp(), |
| 105 | 'confirmed_ip' => $subscriberEntity->getConfirmedIp(), |
| 106 | 'confirmed_at' => ($confirmedAt = $subscriberEntity->getConfirmedAt()) ? $confirmedAt->format(self::DATE_FORMAT) : null, |
| 107 | 'last_subscribed_at' => ($lastSubscribedAt = $subscriberEntity->getLastSubscribedAt()) ? $lastSubscribedAt->format(self::DATE_FORMAT) : null, |
| 108 | 'unconfirmed_data' => $subscriberEntity->getUnconfirmedData(), |
| 109 | 'source' => $subscriberEntity->getSource(), |
| 110 | 'count_confirmations' => $subscriberEntity->getConfirmationsCount(), |
| 111 | 'unsubscribe_token' => $subscriberEntity->getUnsubscribeToken(), |
| 112 | 'link_token' => $subscriberEntity->getLinkToken(), |
| 113 | 'tags' => $this->buildTags($subscriberEntity), |
| 114 | 'timezone' => $subscriberEntity->getTimeZone(), |
| 115 | ]; |
| 116 | |
| 117 | return $this->buildCustomFields($subscriberEntity, $data); |
| 118 | } |
| 119 | |
| 120 | private function buildSubscriptions(SubscriberEntity $subscriberEntity): array { |
| 121 | $result = []; |
| 122 | foreach ($subscriberEntity->getSubscriberSegments() as $subscriberSegment) { |
| 123 | $segment = $subscriberSegment->getSegment(); |
| 124 | if ($segment instanceof SegmentEntity) { |
| 125 | $result[] = [ |
| 126 | 'id' => $subscriberSegment->getId(), |
| 127 | 'subscriber_id' => (string)$subscriberEntity->getId(), |
| 128 | 'created_at' => ($createdAt = $subscriberSegment->getCreatedAt()) ? $createdAt->format(self::DATE_FORMAT) : null, |
| 129 | 'segment_id' => (string)$segment->getId(), |
| 130 | 'status' => $subscriberSegment->getStatus(), |
| 131 | 'updated_at' => ($updatedAt = $subscriberSegment->getUpdatedAt()) ? $updatedAt->format(self::DATE_FORMAT) : null, |
| 132 | ]; |
| 133 | } |
| 134 | } |
| 135 | return $result; |
| 136 | } |
| 137 | |
| 138 | private function buildUnsubscribes(SubscriberEntity $subscriberEntity): array { |
| 139 | $unsubscribes = $this->statisticsUnsubscribesRepository->findBy([ |
| 140 | 'subscriber' => $subscriberEntity, |
| 141 | ], [ |
| 142 | 'createdAt' => 'desc', |
| 143 | ]); |
| 144 | $result = []; |
| 145 | $reasonLabels = $this->unsubscribeReasonTracker->getReasonLabels(); |
| 146 | foreach ($unsubscribes as $unsubscribe) { |
| 147 | $mapped = [ |
| 148 | 'source' => $unsubscribe->getSource(), |
| 149 | 'meta' => $unsubscribe->getMeta(), |
| 150 | 'createdAt' => $unsubscribe->getCreatedAt(), |
| 151 | 'reason' => $unsubscribe->getReason(), |
| 152 | 'reasonLabel' => $this->getUnsubscribeReasonLabel($unsubscribe->getReason(), $reasonLabels), |
| 153 | 'reasonText' => $unsubscribe->getReasonText(), |
| 154 | 'reasonSubmittedAt' => $unsubscribe->getReasonSubmittedAt(), |
| 155 | ]; |
| 156 | $newsletter = $unsubscribe->getNewsletter(); |
| 157 | if ($newsletter instanceof NewsletterEntity) { |
| 158 | $mapped['newsletterId'] = $newsletter->getId(); |
| 159 | $mapped['newsletterSubject'] = $newsletter->getSubject(); |
| 160 | } |
| 161 | $result[] = $mapped; |
| 162 | } |
| 163 | return $result; |
| 164 | } |
| 165 | |
| 166 | /** |
| 167 | * @param array<string, string> $reasonLabels |
| 168 | */ |
| 169 | private function getUnsubscribeReasonLabel(?string $reason, array $reasonLabels): ?string { |
| 170 | if ($reason === null) { |
| 171 | return null; |
| 172 | } |
| 173 | if ($reason === '' || $reason === StatisticsUnsubscribeEntity::REASON_UNSPECIFIED) { |
| 174 | return __('No reason provided', 'mailpoet'); |
| 175 | } |
| 176 | return $reasonLabels[$reason] ?? $reason; |
| 177 | } |
| 178 | |
| 179 | private function buildCustomFields(SubscriberEntity $subscriberEntity, array $data): array { |
| 180 | $customFields = $this->customFieldsRepository->findAllActive(); |
| 181 | |
| 182 | foreach ($customFields as $customField) { |
| 183 | $subscriberCustomField = $this->subscriberCustomFieldRepository->findOneBy( |
| 184 | ['subscriber' => $subscriberEntity, 'customField' => $customField] |
| 185 | ); |
| 186 | if ($subscriberCustomField instanceof SubscriberCustomFieldEntity) { |
| 187 | $data['cf_' . $customField->getId()] = $subscriberCustomField->getValue(); |
| 188 | } |
| 189 | } |
| 190 | return $data; |
| 191 | } |
| 192 | |
| 193 | private function buildTags(SubscriberEntity $subscriber): array { |
| 194 | $result = []; |
| 195 | foreach ($subscriber->getSubscriberTags() as $subscriberTag) { |
| 196 | $tag = $subscriberTag->getTag(); |
| 197 | if (!$tag) { |
| 198 | continue; |
| 199 | } |
| 200 | $result[] = [ |
| 201 | 'id' => $subscriberTag->getId(), |
| 202 | 'subscriber_id' => (string)$subscriber->getId(), |
| 203 | 'tag_id' => (string)$tag->getId(), |
| 204 | 'created_at' => ($createdAt = $subscriberTag->getCreatedAt()) ? $createdAt->format(self::DATE_FORMAT) : null, |
| 205 | 'updated_at' => ($updatedAt = $subscriberTag->getUpdatedAt()) ? $updatedAt->format(self::DATE_FORMAT) : null, |
| 206 | 'name' => $tag->getName(), |
| 207 | ]; |
| 208 | } |
| 209 | return $result; |
| 210 | } |
| 211 | |
| 212 | /** |
| 213 | * @param SubscriberEntity[] $subscribers |
| 214 | */ |
| 215 | private function prefetchRelations(array $subscribers): void { |
| 216 | // Prefetch subscriptions |
| 217 | $this->entityManager->createQueryBuilder() |
| 218 | ->select('PARTIAL s.{id}, ssg, sg') |
| 219 | ->from(SubscriberEntity::class, 's') |
| 220 | ->leftJoin('s.subscriberSegments', 'ssg') |
| 221 | ->leftJoin('ssg.segment', 'sg') |
| 222 | ->where('s.id IN (:subscribers)') |
| 223 | ->setParameter('subscribers', $subscribers) |
| 224 | ->getQuery() |
| 225 | ->getResult(); |
| 226 | // Prefetch tags |
| 227 | $this->entityManager->createQueryBuilder() |
| 228 | ->select('PARTIAL s.{id}, st, t') |
| 229 | ->from(SubscriberEntity::class, 's') |
| 230 | ->leftJoin('s.subscriberTags', 'st') |
| 231 | ->leftJoin('st.tag', 't') |
| 232 | ->where('s.id IN (:subscribers)') |
| 233 | ->setParameter('subscribers', $subscribers) |
| 234 | ->getQuery() |
| 235 | ->getResult(); |
| 236 | } |
| 237 | } |
| 238 |