PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.37.0
MailPoet – Newsletters, Email Marketing, and Automation v5.37.0
5.37.0 5.36.1 5.36.0 5.35.1 5.35.0 5.34.3 5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / lib / API / JSON / ResponseBuilders / SubscribersResponseBuilder.php
mailpoet / lib / API / JSON / ResponseBuilders Last commit date
CustomFieldsResponseBuilder.php 1 year ago DynamicSegmentsResponseBuilder.php 3 months ago FormsResponseBuilder.php 1 year ago NewsletterTemplatesResponseBuilder.php 1 year ago NewslettersResponseBuilder.php 1 month ago ScheduledTaskSubscriberResponseBuilder.php 3 years ago SegmentsResponseBuilder.php 3 months ago SendingQueuesResponseBuilder.php 3 months ago SubscribersResponseBuilder.php 2 days ago index.php 3 years ago
SubscribersResponseBuilder.php
244 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 'tracking_consent' => $subscriber->getTrackingConsent(),
88 ];
89 }
90
91 public function build(SubscriberEntity $subscriberEntity): array {
92 $data = [
93 'id' => (string)$subscriberEntity->getId(),
94 'wp_user_id' => $subscriberEntity->getWpUserId(),
95 'is_woocommerce_user' => $subscriberEntity->getIsWoocommerceUser(),
96 'subscriptions' => $this->buildSubscriptions($subscriberEntity),
97 'unsubscribes' => $this->buildUnsubscribes($subscriberEntity),
98 'status' => $subscriberEntity->getStatus(),
99 'last_name' => $subscriberEntity->getLastName(),
100 'first_name' => $subscriberEntity->getFirstName(),
101 'email' => $subscriberEntity->getEmail(),
102 'created_at' => ($createdAt = $subscriberEntity->getCreatedAt()) ? $createdAt->format(self::DATE_FORMAT) : null,
103 'updated_at' => ($updatedAt = $subscriberEntity->getUpdatedAt()) ? $updatedAt->format(self::DATE_FORMAT) : null,
104 'deleted_at' => ($deletedAt = $subscriberEntity->getDeletedAt()) ? $deletedAt->format(self::DATE_FORMAT) : null,
105 'subscribed_ip' => $subscriberEntity->getSubscribedIp(),
106 'confirmed_ip' => $subscriberEntity->getConfirmedIp(),
107 'confirmed_at' => ($confirmedAt = $subscriberEntity->getConfirmedAt()) ? $confirmedAt->format(self::DATE_FORMAT) : null,
108 'last_subscribed_at' => ($lastSubscribedAt = $subscriberEntity->getLastSubscribedAt()) ? $lastSubscribedAt->format(self::DATE_FORMAT) : null,
109 'unconfirmed_data' => $subscriberEntity->getUnconfirmedData(),
110 'source' => $subscriberEntity->getSource(),
111 'count_confirmations' => $subscriberEntity->getConfirmationsCount(),
112 'unsubscribe_token' => $subscriberEntity->getUnsubscribeToken(),
113 'link_token' => $subscriberEntity->getLinkToken(),
114 'tags' => $this->buildTags($subscriberEntity),
115 'timezone' => $subscriberEntity->getTimeZone(),
116 'tracking_consent' => $subscriberEntity->getTrackingConsent(),
117 'tracking_consent_updated_at' => ($trackingConsentUpdatedAt = $subscriberEntity->getTrackingConsentUpdatedAt())
118 ? $trackingConsentUpdatedAt->format(self::DATE_FORMAT) : null,
119 'tracking_consent_method' => $subscriberEntity->getTrackingConsentMethod(),
120 'tracking_consent_copy' => $subscriberEntity->getTrackingConsentCopy(),
121 ];
122
123 return $this->buildCustomFields($subscriberEntity, $data);
124 }
125
126 private function buildSubscriptions(SubscriberEntity $subscriberEntity): array {
127 $result = [];
128 foreach ($subscriberEntity->getSubscriberSegments() as $subscriberSegment) {
129 $segment = $subscriberSegment->getSegment();
130 if ($segment instanceof SegmentEntity) {
131 $result[] = [
132 'id' => $subscriberSegment->getId(),
133 'subscriber_id' => (string)$subscriberEntity->getId(),
134 'created_at' => ($createdAt = $subscriberSegment->getCreatedAt()) ? $createdAt->format(self::DATE_FORMAT) : null,
135 'segment_id' => (string)$segment->getId(),
136 'status' => $subscriberSegment->getStatus(),
137 'updated_at' => ($updatedAt = $subscriberSegment->getUpdatedAt()) ? $updatedAt->format(self::DATE_FORMAT) : null,
138 ];
139 }
140 }
141 return $result;
142 }
143
144 private function buildUnsubscribes(SubscriberEntity $subscriberEntity): array {
145 $unsubscribes = $this->statisticsUnsubscribesRepository->findBy([
146 'subscriber' => $subscriberEntity,
147 ], [
148 'createdAt' => 'desc',
149 ]);
150 $result = [];
151 $reasonLabels = $this->unsubscribeReasonTracker->getReasonLabels();
152 foreach ($unsubscribes as $unsubscribe) {
153 $mapped = [
154 'source' => $unsubscribe->getSource(),
155 'meta' => $unsubscribe->getMeta(),
156 'createdAt' => $unsubscribe->getCreatedAt(),
157 'reason' => $unsubscribe->getReason(),
158 'reasonLabel' => $this->getUnsubscribeReasonLabel($unsubscribe->getReason(), $reasonLabels),
159 'reasonText' => $unsubscribe->getReasonText(),
160 'reasonSubmittedAt' => $unsubscribe->getReasonSubmittedAt(),
161 ];
162 $newsletter = $unsubscribe->getNewsletter();
163 if ($newsletter instanceof NewsletterEntity) {
164 $mapped['newsletterId'] = $newsletter->getId();
165 $mapped['newsletterSubject'] = $newsletter->getSubject();
166 }
167 $result[] = $mapped;
168 }
169 return $result;
170 }
171
172 /**
173 * @param array<string, string> $reasonLabels
174 */
175 private function getUnsubscribeReasonLabel(?string $reason, array $reasonLabels): ?string {
176 if ($reason === null) {
177 return null;
178 }
179 if ($reason === '' || $reason === StatisticsUnsubscribeEntity::REASON_UNSPECIFIED) {
180 return __('No reason provided', 'mailpoet');
181 }
182 return $reasonLabels[$reason] ?? $reason;
183 }
184
185 private function buildCustomFields(SubscriberEntity $subscriberEntity, array $data): array {
186 $customFields = $this->customFieldsRepository->findAllActive();
187
188 foreach ($customFields as $customField) {
189 $subscriberCustomField = $this->subscriberCustomFieldRepository->findOneBy(
190 ['subscriber' => $subscriberEntity, 'customField' => $customField]
191 );
192 if ($subscriberCustomField instanceof SubscriberCustomFieldEntity) {
193 $data['cf_' . $customField->getId()] = $subscriberCustomField->getValue();
194 }
195 }
196 return $data;
197 }
198
199 private function buildTags(SubscriberEntity $subscriber): array {
200 $result = [];
201 foreach ($subscriber->getSubscriberTags() as $subscriberTag) {
202 $tag = $subscriberTag->getTag();
203 if (!$tag) {
204 continue;
205 }
206 $result[] = [
207 'id' => $subscriberTag->getId(),
208 'subscriber_id' => (string)$subscriber->getId(),
209 'tag_id' => (string)$tag->getId(),
210 'created_at' => ($createdAt = $subscriberTag->getCreatedAt()) ? $createdAt->format(self::DATE_FORMAT) : null,
211 'updated_at' => ($updatedAt = $subscriberTag->getUpdatedAt()) ? $updatedAt->format(self::DATE_FORMAT) : null,
212 'name' => $tag->getName(),
213 ];
214 }
215 return $result;
216 }
217
218 /**
219 * @param SubscriberEntity[] $subscribers
220 */
221 private function prefetchRelations(array $subscribers): void {
222 // Prefetch subscriptions
223 $this->entityManager->createQueryBuilder()
224 ->select('PARTIAL s.{id}, ssg, sg')
225 ->from(SubscriberEntity::class, 's')
226 ->leftJoin('s.subscriberSegments', 'ssg')
227 ->leftJoin('ssg.segment', 'sg')
228 ->where('s.id IN (:subscribers)')
229 ->setParameter('subscribers', $subscribers)
230 ->getQuery()
231 ->getResult();
232 // Prefetch tags
233 $this->entityManager->createQueryBuilder()
234 ->select('PARTIAL s.{id}, st, t')
235 ->from(SubscriberEntity::class, 's')
236 ->leftJoin('s.subscriberTags', 'st')
237 ->leftJoin('st.tag', 't')
238 ->where('s.id IN (:subscribers)')
239 ->setParameter('subscribers', $subscribers)
240 ->getQuery()
241 ->getResult();
242 }
243 }
244