PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.28.1
MailPoet – Newsletters, Email Marketing, and Automation v5.28.1
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 / Segments / SubscribersFinder.php
mailpoet / lib / Segments Last commit date
DynamicSegments 3 days ago RestApi 1 week ago SegmentDependencyValidator.php 3 years ago SegmentListingRepository.php 2 weeks ago SegmentSaveController.php 2 weeks ago SegmentSubscribersRepository.php 1 month ago SegmentsFinder.php 3 years ago SegmentsRepository.php 2 weeks ago SegmentsSimpleListRepository.php 1 month ago SubscribersFinder.php 1 month ago WP.php 3 weeks ago WooCommerce.php 1 month ago index.php 3 years ago
SubscribersFinder.php
221 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\Segments;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Entities\ScheduledTaskEntity;
9 use MailPoet\Entities\ScheduledTaskSubscriberEntity;
10 use MailPoet\Entities\SegmentEntity;
11 use MailPoet\Entities\SubscriberEntity;
12 use MailPoet\Entities\SubscriberSegmentEntity;
13 use MailPoet\InvalidStateException;
14 use MailPoet\Newsletter\Sending\ScheduledTaskSubscribersRepository;
15 use MailPoetVendor\Doctrine\DBAL\ArrayParameterType;
16 use MailPoetVendor\Doctrine\DBAL\ParameterType;
17 use MailPoetVendor\Doctrine\ORM\EntityManager;
18
19 class SubscribersFinder {
20
21 /** @var SegmentSubscribersRepository */
22 private $segmentSubscriberRepository;
23
24 /** @var SegmentsRepository */
25 private $segmentsRepository;
26
27 /** @var EntityManager */
28 private $entityManager;
29
30 /** @var ScheduledTaskSubscribersRepository */
31 private $scheduledTaskSubscribersRepository;
32
33 public function __construct(
34 SegmentSubscribersRepository $segmentSubscriberRepository,
35 SegmentsRepository $segmentsRepository,
36 EntityManager $entityManager,
37 ScheduledTaskSubscribersRepository $scheduledTaskSubscribersRepository
38 ) {
39 $this->segmentSubscriberRepository = $segmentSubscriberRepository;
40 $this->segmentsRepository = $segmentsRepository;
41 $this->entityManager = $entityManager;
42 $this->scheduledTaskSubscribersRepository = $scheduledTaskSubscribersRepository;
43 }
44
45 /**
46 * @return array
47 * @throws InvalidStateException
48 */
49 public function findSubscribersInSegments($subscribersToProcessIds, $newsletterSegmentsIds, ?int $filterSegmentId = null) {
50 $result = [];
51 foreach ($newsletterSegmentsIds as $segmentId) {
52 $segment = $this->segmentsRepository->findOneById($segmentId);
53 if (!$segment instanceof SegmentEntity) {
54 continue; // skip deleted segments
55 }
56 $result = array_merge($result, $this->findSubscribersInSegment($segment, $subscribersToProcessIds));
57 }
58
59 if (is_int($filterSegmentId)) {
60 $filterSegment = $this->segmentsRepository->verifyDynamicSegmentExists($filterSegmentId);
61 $idsInFilterSegment = $this->findSubscribersInSegment($filterSegment, $subscribersToProcessIds);
62 $result = array_intersect($result, $idsInFilterSegment);
63 }
64
65 return $this->unique($result);
66 }
67
68 /**
69 * @param int[] $newsletterSegmentsIds
70 * @return int[]
71 * @throws InvalidStateException
72 */
73 public function getSubscriberIdsFromSegments(array $newsletterSegmentsIds, ?int $filterSegmentId = null): array {
74 $result = [];
75 foreach ($newsletterSegmentsIds as $segmentId) {
76 $segment = $this->segmentsRepository->findOneById($segmentId);
77 if (!$segment instanceof SegmentEntity) {
78 continue;
79 }
80 try {
81 $result = array_merge($result, $this->segmentSubscriberRepository->getSubscriberIdsInSegment((int)$segment->getId()));
82 } catch (InvalidStateException $exception) {
83 continue;
84 }
85 }
86
87 if (is_int($filterSegmentId)) {
88 $filterSegment = $this->segmentsRepository->verifyDynamicSegmentExists($filterSegmentId);
89 $filterSubscriberIds = $this->segmentSubscriberRepository->getSubscriberIdsInSegment((int)$filterSegment->getId());
90 $result = array_intersect($result, $filterSubscriberIds);
91 }
92
93 return array_values($this->unique($result));
94 }
95
96 private function findSubscribersInSegment(SegmentEntity $segment, $subscribersToProcessIds): array {
97 try {
98 return $this->segmentSubscriberRepository->findSubscribersIdsInSegment((int)$segment->getId(), $subscribersToProcessIds);
99 } catch (InvalidStateException $e) {
100 return [];
101 }
102 }
103
104 /**
105 * @param ScheduledTaskEntity $task
106 * @param array<int> $segmentIds
107 *
108 * @return void
109 */
110 public function addSubscribersToTaskFromSegments(ScheduledTaskEntity $task, array $segmentIds, ?int $filterSegmentId = null): void {
111 // Prepare subscribers on the DB side for performance reasons
112 if (is_int($filterSegmentId)) {
113 try {
114 $this->segmentsRepository->verifyDynamicSegmentExists($filterSegmentId);
115 } catch (InvalidStateException $exception) {
116 return;
117 }
118 }
119 $staticSegmentIds = [];
120 $dynamicSegmentIds = [];
121 foreach ($segmentIds as $segment) {
122 $segment = $this->segmentsRepository->findOneById($segment);
123 if ($segment instanceof SegmentEntity) {
124 if ($segment->isStatic()) {
125 $staticSegmentIds[] = (int)$segment->getId();
126 } elseif ($segment->getType() === SegmentEntity::TYPE_DYNAMIC) {
127 $dynamicSegmentIds[] = (int)$segment->getId();
128 }
129 }
130 }
131 $count = 0;
132 if (!empty($staticSegmentIds)) {
133 $count += $this->addSubscribersToTaskFromStaticSegments($task, $staticSegmentIds, $filterSegmentId);
134 }
135 if (!empty($dynamicSegmentIds)) {
136 $count += $this->addSubscribersToTaskFromDynamicSegments($task, $dynamicSegmentIds, $filterSegmentId);
137 }
138 if ($count > 0) {
139 $this->entityManager->refresh($task);
140 }
141 }
142
143 /**
144 * @param ScheduledTaskEntity $task
145 * @param array<int> $segmentIds
146 *
147 * @return int
148 */
149 private function addSubscribersToTaskFromStaticSegments(ScheduledTaskEntity $task, array $segmentIds, ?int $filterSegmentId = null) {
150 $scheduledTaskSubscriberTable = $this->entityManager->getClassMetadata(ScheduledTaskSubscriberEntity::class)->getTableName();
151 $subscriberSegmentTable = $this->entityManager->getClassMetadata(SubscriberSegmentEntity::class)->getTableName();
152 $subscriberTable = $this->entityManager->getClassMetadata(SubscriberEntity::class)->getTableName();
153
154 $connection = $this->entityManager->getConnection();
155 $selectQueryBuilder = $connection->createQueryBuilder();
156 $selectQueryBuilder
157 ->select('DISTINCT :task_id as task_id', 'subscribers.id as subscriber_id', ':processed as processed')
158 ->from($subscriberSegmentTable, 'relation')
159 ->join('relation', $subscriberTable, 'subscribers', 'subscribers.id = relation.subscriber_id')
160 ->where('subscribers.deleted_at IS NULL')
161 ->andWhere('subscribers.status = :subscribers_status')
162 ->andWhere('relation.status = :relation_status')
163 ->andWhere($selectQueryBuilder->expr()->in('relation.segment_id', ':segment_ids'))
164 ->setParameter('task_id', $task->getId(), ParameterType::INTEGER)
165 ->setParameter('processed', ScheduledTaskSubscriberEntity::STATUS_UNPROCESSED, ParameterType::INTEGER)
166 ->setParameter('subscribers_status', SubscriberEntity::STATUS_SUBSCRIBED, ParameterType::STRING)
167 ->setParameter('relation_status', SubscriberEntity::STATUS_SUBSCRIBED, ParameterType::STRING)
168 ->setParameter('segment_ids', $segmentIds, ArrayParameterType::INTEGER);
169
170 if ($filterSegmentId) {
171 $filterSegmentSubscriberIds = $this->segmentSubscriberRepository->findSubscribersIdsInSegment($filterSegmentId);
172 $selectQueryBuilder
173 ->andWhere($selectQueryBuilder->expr()->in('subscribers.id', ':filterSegmentSubscriberIds'))
174 ->setParameter('filterSegmentSubscriberIds', $filterSegmentSubscriberIds, ArrayParameterType::INTEGER);
175 }
176
177 // queryBuilder doesn't support INSERT IGNORE directly
178 $sql = "INSERT IGNORE INTO $scheduledTaskSubscriberTable (task_id, subscriber_id, processed) " . $selectQueryBuilder->getSQL();
179 $result = $connection->executeQuery($sql, $selectQueryBuilder->getParameters(), $selectQueryBuilder->getParameterTypes());
180
181 return (int)$result->rowCount();
182 }
183
184 /**
185 * @param ScheduledTaskEntity $task
186 * @param array<int> $segmentIds
187 *
188 * @return int
189 */
190 private function addSubscribersToTaskFromDynamicSegments(ScheduledTaskEntity $task, array $segmentIds, ?int $filterSegmentId = null) {
191 $count = 0;
192 foreach ($segmentIds as $segmentId) {
193 $count += $this->addSubscribersToTaskFromDynamicSegment($task, (int)$segmentId, $filterSegmentId);
194 }
195 return $count;
196 }
197
198 private function addSubscribersToTaskFromDynamicSegment(ScheduledTaskEntity $task, int $segmentId, ?int $filterSegmentId) {
199 $count = 0;
200 $subscriberIds = $this->segmentSubscriberRepository->getSubscriberIdsInSegment($segmentId);
201
202 if ($filterSegmentId) {
203 $filterSegmentSubscriberIds = $this->segmentSubscriberRepository->getSubscriberIdsInSegment($filterSegmentId);
204 $subscriberIds = array_intersect($subscriberIds, $filterSegmentSubscriberIds);
205 }
206
207 if ($subscriberIds) {
208 $count += $this->scheduledTaskSubscribersRepository->addSubscribersByIds($task, $subscriberIds);
209 }
210 return $count;
211 }
212
213 private function unique(array $subscriberIds) {
214 $result = [];
215 foreach ($subscriberIds as $id) {
216 $result[$id] = $id;
217 }
218 return $result;
219 }
220 }
221