PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.33.1
MailPoet – Newsletters, Email Marketing, and Automation v5.33.1
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 / Entities / SubscriberEntity.php
mailpoet / lib / Entities Last commit date
CustomFieldEntity.php 3 months ago DynamicSegmentFilterData.php 3 months ago DynamicSegmentFilterEntity.php 3 years ago FeatureFlagEntity.php 3 years ago FormEntity.php 1 year ago LogEntity.php 3 years ago NewsletterEntity.php 4 months ago NewsletterLinkEntity.php 3 years ago NewsletterOptionEntity.php 3 years ago NewsletterOptionFieldEntity.php 3 months ago NewsletterPostEntity.php 3 years ago NewsletterSegmentEntity.php 3 years ago NewsletterTemplateEntity.php 3 years ago ScheduledTaskEntity.php 2 months ago ScheduledTaskSubscriberEntity.php 1 year ago SegmentEntity.php 3 months ago SendingQueueEntity.php 4 months ago SettingEntity.php 3 years ago StatisticsBounceEntity.php 3 years ago StatisticsClickEntity.php 3 years ago StatisticsFormEntity.php 3 years ago StatisticsNewsletterEntity.php 1 year ago StatisticsOpenEntity.php 3 years ago StatisticsUnsubscribeEntity.php 3 months ago StatisticsWooCommercePurchaseEntity.php 2 years ago StatsNotificationEntity.php 3 years ago SubscriberCustomFieldEntity.php 3 years ago SubscriberEntity.php 1 month ago SubscriberIPEntity.php 3 years ago SubscriberSegmentEntity.php 3 years ago SubscriberTagEntity.php 4 years ago TagEntity.php 4 years ago UserAgentEntity.php 3 years ago UserFlagEntity.php 3 years ago WpPostEntity.php 2 years ago index.php 3 years ago
SubscriberEntity.php
775 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\Entities;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use DateTimeInterface;
9 use MailPoet\Doctrine\EntityTraits\AutoincrementedIdTrait;
10 use MailPoet\Doctrine\EntityTraits\CreatedAtTrait;
11 use MailPoet\Doctrine\EntityTraits\DeletedAtTrait;
12 use MailPoet\Doctrine\EntityTraits\UpdatedAtTrait;
13 use MailPoet\Doctrine\EntityTraits\ValidationGroupsTrait;
14 use MailPoetVendor\Doctrine\Common\Collections\ArrayCollection;
15 use MailPoetVendor\Doctrine\Common\Collections\Collection;
16 use MailPoetVendor\Doctrine\Common\Collections\Criteria;
17 use MailPoetVendor\Doctrine\ORM\Mapping as ORM;
18 use MailPoetVendor\Symfony\Component\Validator\Constraints as Assert;
19
20 /**
21 * @ORM\Entity()
22 * @ORM\Table(name="subscribers")
23 * @ORM\HasLifecycleCallbacks
24 * @ORM\EntityListeners({"\MailPoet\Doctrine\EventListeners\SubscriberListener"})
25 */
26 class SubscriberEntity {
27 // hook names
28 public const HOOK_SUBSCRIBER_CREATED = 'mailpoet_subscriber_created';
29 public const HOOK_SUBSCRIBER_DELETED = 'mailpoet_subscriber_deleted';
30 public const HOOK_SUBSCRIBER_UPDATED = 'mailpoet_subscriber_updated';
31 public const HOOK_SUBSCRIBER_STATUS_CHANGED = 'mailpoet_subscriber_status_changed';
32 public const HOOK_MULTIPLE_SUBSCRIBERS_CREATED = 'mailpoet_multiple_subscribers_created';
33 public const HOOK_MULTIPLE_SUBSCRIBERS_DELETED = 'mailpoet_multiple_subscribers_deleted';
34 public const HOOK_MULTIPLE_SUBSCRIBERS_UPDATED = 'mailpoet_multiple_subscribers_updated';
35 public const HOOK_SUBSCRIBERS_COUNT_CHANGED = 'mailpoet_subscribers_count_changed';
36
37 // statuses
38 const STATUS_BOUNCED = 'bounced';
39 const STATUS_INACTIVE = 'inactive';
40 const STATUS_SUBSCRIBED = 'subscribed';
41 const STATUS_UNCONFIRMED = 'unconfirmed';
42 const STATUS_UNSUBSCRIBED = 'unsubscribed';
43
44 public const OBSOLETE_LINK_TOKEN_LENGTH = 6;
45 public const LINK_TOKEN_LENGTH = 32;
46 public const TIME_ZONE_FIELD_NAME = 'mailpoet_subscriber_timezone';
47 public const TIME_ZONE_SOURCE_FORM = 'form';
48 public const TIME_ZONE_SOURCE_SITE_FALLBACK = 'site_fallback';
49 public const TIME_ZONE_CONFIDENCE_BROWSER = 90;
50
51 /** @var array<string,bool>|null */
52 private static $validTimeZones = null;
53
54 use AutoincrementedIdTrait;
55 use CreatedAtTrait;
56 use UpdatedAtTrait;
57 use DeletedAtTrait;
58 use ValidationGroupsTrait;
59
60 /**
61 * @ORM\Column(type="bigint", nullable=true)
62 * @var string|null
63 */
64 private $wpUserId;
65
66 /**
67 * @ORM\Column(type="boolean")
68 * @var bool
69 */
70 private $isWoocommerceUser = false;
71
72 /**
73 * @ORM\Column(type="string")
74 * @var string
75 */
76 private $firstName = '';
77
78 /**
79 * @ORM\Column(type="string")
80 * @var string
81 */
82 private $lastName = '';
83
84 /**
85 * @ORM\Column(type="string")
86 * @Assert\Email(groups={"Saving"})
87 * @Assert\NotBlank()
88 * @var string
89 */
90 private $email;
91
92 /**
93 * @ORM\Column(type="string")
94 * @var string
95 */
96 private $status = self::STATUS_UNCONFIRMED;
97
98 /**
99 * @ORM\Column(type="string", nullable=true)
100 * @var string|null
101 */
102 private $subscribedIp;
103
104 /**
105 * @ORM\Column(type="string", nullable=true)
106 * @var string|null
107 */
108 private $confirmedIp;
109
110 /**
111 * @ORM\Column(type="string", nullable=true)
112 * @var string|null
113 */
114 private $timeZone;
115
116 /**
117 * @ORM\Column(type="string", nullable=true)
118 * @var string|null
119 */
120 private $timeZoneSource;
121
122 /**
123 * @ORM\Column(type="integer", nullable=true)
124 * @var int|null
125 */
126 private $timeZoneConfidence;
127
128 /**
129 * @ORM\Column(type="datetimetz", nullable=true)
130 * @var DateTimeInterface|null
131 */
132 private $timeZoneUpdatedAt;
133
134 /**
135 * @ORM\Column(type="datetimetz", nullable=true)
136 * @var DateTimeInterface|null
137 */
138 private $confirmedAt;
139
140 /**
141 * @ORM\Column(type="datetimetz", nullable=true)
142 * @var DateTimeInterface|null
143 */
144 private $lastSubscribedAt;
145
146 /**
147 * @ORM\Column(type="datetimetz", nullable=true)
148 * @var DateTimeInterface|null
149 */
150 private $lastConfirmationEmailSentAt;
151
152 /**
153 * @ORM\Column(type="text", nullable=true)
154 * @var string|null
155 */
156 private $unconfirmedData;
157
158 /**
159 * @ORM\Column(type="string")
160 * @var string
161 */
162 private $source = 'unknown';
163
164 /**
165 * @ORM\Column(type="integer")
166 * @var int
167 */
168 private $countConfirmations = 0;
169
170 /**
171 * Denormalized number of subscribed memberships in non-deleted segments.
172 * Maintained by SegmentsCountRecalculator; used to quickly find subscribers
173 * without a list (segments_count = 0).
174 * @ORM\Column(type="integer", options={"unsigned":true})
175 * @var int
176 */
177 private $segmentsCount = 0;
178
179 /**
180 * @ORM\Column(type="string", nullable=true)
181 * @var string|null
182 */
183 private $unsubscribeToken;
184
185 /**
186 * @ORM\Column(type="string", nullable=true)
187 * @var string|null
188 */
189 private $linkToken;
190
191 /**
192 * @ORM\Column(type="float", nullable=true)
193 * @var float|null
194 */
195 private $engagementScore;
196
197 /**
198 * @ORM\Column(type="datetimetz", nullable=true)
199 * @var DateTimeInterface|null
200 */
201 private $engagementScoreUpdatedAt;
202
203 /**
204 * @ORM\Column(type="datetimetz", nullable=true)
205 * @var DateTimeInterface|null
206 */
207 private $lastEngagementAt;
208
209 /**
210 * @ORM\Column(type="datetimetz", nullable=true)
211 * @var DateTimeInterface|null
212 */
213 private $lastSendingAt;
214
215 /**
216 * @ORM\Column(type="datetimetz", nullable=true)
217 * @var DateTimeInterface|null
218 */
219 private $lastOpenAt;
220
221 /**
222 * @ORM\Column(type="datetimetz", nullable=true)
223 * @var DateTimeInterface|null
224 */
225 private $lastClickAt;
226
227 /**
228 * @ORM\Column(type="datetimetz", nullable=true)
229 * @var DateTimeInterface|null
230 */
231 private $lastPurchaseAt;
232
233 /**
234 * @ORM\Column(type="datetimetz", nullable=true)
235 * @var DateTimeInterface|null
236 */
237 private $lastPageViewAt;
238
239 /**
240 * @ORM\Column(type="datetimetz", nullable=true)
241 * @var DateTimeInterface|null
242 */
243 private $woocommerceSyncedAt;
244
245 /**
246 * @ORM\Column(type="integer")
247 * @var int
248 */
249 private $emailCount = 0;
250
251 /**
252 * @ORM\OneToMany(targetEntity="MailPoet\Entities\SubscriberSegmentEntity", mappedBy="subscriber", orphanRemoval=true)
253 * @var Collection<int, SubscriberSegmentEntity>
254 */
255 private $subscriberSegments;
256
257 /**
258 * @ORM\OneToMany(targetEntity="MailPoet\Entities\SubscriberCustomFieldEntity", mappedBy="subscriber", orphanRemoval=true)
259 * @var Collection<int, SubscriberCustomFieldEntity>
260 */
261 private $subscriberCustomFields;
262
263 /**
264 * @ORM\OneToMany(targetEntity="MailPoet\Entities\SubscriberTagEntity", mappedBy="subscriber", orphanRemoval=true)
265 * @var Collection<int, SubscriberTagEntity>
266 */
267 private $subscriberTags;
268
269 /**
270 * @ORM\OneToMany(targetEntity="MailPoet\Entities\ScheduledTaskSubscriberEntity", mappedBy="subscriber", orphanRemoval=true)
271 * @var Collection<int, ScheduledTaskSubscriberEntity>
272 */
273 private $scheduledTaskSubscribers;
274
275 public function __construct() {
276 $this->subscriberSegments = new ArrayCollection();
277 $this->subscriberCustomFields = new ArrayCollection();
278 $this->subscriberTags = new ArrayCollection();
279 $this->scheduledTaskSubscribers = new ArrayCollection();
280 }
281
282 /**
283 * @return int|null
284 */
285 public function getWpUserId() {
286 return $this->wpUserId ? (int)$this->wpUserId : null;
287 }
288
289 /**
290 * @param int|null $wpUserId
291 */
292 public function setWpUserId($wpUserId) {
293 $this->wpUserId = $wpUserId ? (string)$wpUserId : null;
294 }
295
296 public function isWPUser(): bool {
297 return $this->getWpUserId() > 0;
298 }
299
300 /**
301 * @return bool
302 */
303 public function getIsWoocommerceUser() {
304 return $this->isWoocommerceUser;
305 }
306
307 /**
308 * @param bool $isWoocommerceUser
309 */
310 public function setIsWoocommerceUser($isWoocommerceUser) {
311 $this->isWoocommerceUser = $isWoocommerceUser;
312 }
313
314 /**
315 * @return string
316 */
317 public function getFirstName() {
318 return $this->firstName;
319 }
320
321 /**
322 * @param string $firstName
323 */
324 public function setFirstName($firstName) {
325 $this->firstName = $firstName;
326 }
327
328 /**
329 * @return string
330 */
331 public function getLastName() {
332 return $this->lastName;
333 }
334
335 /**
336 * @param string $lastName
337 */
338 public function setLastName($lastName) {
339 $this->lastName = $lastName;
340 }
341
342 /**
343 * @return string
344 */
345 public function getEmail() {
346 return $this->email;
347 }
348
349 /**
350 * @param string $email
351 */
352 public function setEmail($email) {
353 $this->email = $email;
354 }
355
356 /**
357 * @return string
358 */
359 public function getStatus() {
360 return $this->status;
361 }
362
363 /**
364 * @param string $status
365 */
366 public function setStatus($status) {
367 if (
368 !in_array($status, [
369 self::STATUS_BOUNCED,
370 self::STATUS_INACTIVE,
371 self::STATUS_SUBSCRIBED,
372 self::STATUS_UNCONFIRMED,
373 self::STATUS_UNSUBSCRIBED,
374 ])
375 ) {
376 throw new \InvalidArgumentException("Invalid status '{$status}' given to subscriber!");
377 }
378 $this->status = $status;
379 }
380
381 /**
382 * @return string|null
383 */
384 public function getSubscribedIp() {
385 return $this->subscribedIp;
386 }
387
388 /**
389 * @param string $subscribedIp
390 */
391 public function setSubscribedIp($subscribedIp) {
392 $this->subscribedIp = $subscribedIp;
393 }
394
395 /**
396 * @return string|null
397 */
398 public function getConfirmedIp() {
399 return $this->confirmedIp;
400 }
401
402 /**
403 * @param string|null $confirmedIp
404 */
405 public function setConfirmedIp($confirmedIp) {
406 $this->confirmedIp = $confirmedIp;
407 }
408
409 public function getTimeZone(): ?string {
410 return $this->timeZone;
411 }
412
413 public function setTimeZone(?string $timeZone): void {
414 $this->timeZone = $timeZone;
415 }
416
417 public function getTimeZoneSource(): ?string {
418 return $this->timeZoneSource;
419 }
420
421 public function setTimeZoneSource(?string $timeZoneSource): void {
422 $this->timeZoneSource = $timeZoneSource;
423 }
424
425 public function getTimeZoneConfidence(): ?int {
426 return $this->timeZoneConfidence;
427 }
428
429 public function setTimeZoneConfidence(?int $timeZoneConfidence): void {
430 $this->timeZoneConfidence = $timeZoneConfidence;
431 }
432
433 public function getTimeZoneUpdatedAt(): ?DateTimeInterface {
434 return $this->timeZoneUpdatedAt;
435 }
436
437 public function setTimeZoneUpdatedAt(?DateTimeInterface $timeZoneUpdatedAt): void {
438 $this->timeZoneUpdatedAt = $timeZoneUpdatedAt;
439 }
440
441 /**
442 * @param mixed $timeZone
443 */
444 public static function sanitizeTimeZone($timeZone): ?string {
445 if (!is_string($timeZone)) {
446 return null;
447 }
448 $timeZone = trim($timeZone);
449 if ($timeZone === '' || strlen($timeZone) > 64) {
450 return null;
451 }
452 return self::isValidTimeZone($timeZone) ? $timeZone : null;
453 }
454
455 /**
456 * @param mixed $timeZone
457 */
458 public static function isValidTimeZone($timeZone): bool {
459 if (!is_string($timeZone) || $timeZone === '') {
460 return false;
461 }
462 if (self::$validTimeZones === null) {
463 self::$validTimeZones = array_fill_keys(\DateTimeZone::listIdentifiers(), true);
464 }
465 return isset(self::$validTimeZones[$timeZone]);
466 }
467
468 /**
469 * @return DateTimeInterface|null
470 */
471 public function getConfirmedAt() {
472 return $this->confirmedAt;
473 }
474
475 /**
476 * @param DateTimeInterface|null $confirmedAt
477 */
478 public function setConfirmedAt($confirmedAt) {
479 $this->confirmedAt = $confirmedAt;
480 }
481
482 /**
483 * @return DateTimeInterface|null
484 */
485 public function getLastSubscribedAt() {
486 return $this->lastSubscribedAt;
487 }
488
489 /**
490 * @param DateTimeInterface|null $lastSubscribedAt
491 */
492 public function setLastSubscribedAt($lastSubscribedAt) {
493 $this->lastSubscribedAt = $lastSubscribedAt;
494 }
495
496 /**
497 * @return DateTimeInterface|null
498 */
499 public function getLastConfirmationEmailSentAt() {
500 return $this->lastConfirmationEmailSentAt;
501 }
502
503 /**
504 * @param DateTimeInterface|null $lastConfirmationEmailSentAt
505 */
506 public function setLastConfirmationEmailSentAt($lastConfirmationEmailSentAt) {
507 $this->lastConfirmationEmailSentAt = $lastConfirmationEmailSentAt;
508 }
509
510 /**
511 * @return string|null
512 */
513 public function getUnconfirmedData() {
514 return $this->unconfirmedData;
515 }
516
517 /**
518 * @param string|null $unconfirmedData
519 */
520 public function setUnconfirmedData($unconfirmedData) {
521 $this->unconfirmedData = $unconfirmedData;
522 }
523
524 /**
525 * @return string
526 */
527 public function getSource() {
528 return $this->source;
529 }
530
531 /**
532 * @param string $source
533 */
534 public function setSource($source) {
535 if (
536 !in_array($source, [
537 'api',
538 'form',
539 'unknown',
540 'imported',
541 'administrator',
542 'wordpress_user',
543 'wordpress_user_deleted',
544 'woocommerce_user',
545 'woocommerce_checkout',
546 ])
547 ) {
548 throw new \InvalidArgumentException("Invalid source '{$source}' given to subscriber!");
549 }
550 $this->source = $source;
551 }
552
553 /**
554 * @return int
555 */
556 public function getConfirmationsCount() {
557 return $this->countConfirmations;
558 }
559
560 /**
561 * @param int $countConfirmations
562 */
563 public function setConfirmationsCount($countConfirmations) {
564 $this->countConfirmations = $countConfirmations;
565 }
566
567 public function getSegmentsCount(): int {
568 return $this->segmentsCount;
569 }
570
571 public function setSegmentsCount(int $segmentsCount): void {
572 $this->segmentsCount = $segmentsCount;
573 }
574
575 /**
576 * @return string|null
577 */
578 public function getUnsubscribeToken() {
579 return $this->unsubscribeToken;
580 }
581
582 /**
583 * @param string|null $unsubscribeToken
584 */
585 public function setUnsubscribeToken($unsubscribeToken) {
586 $this->unsubscribeToken = $unsubscribeToken;
587 }
588
589 /**
590 * @return string|null
591 */
592 public function getLinkToken() {
593 return $this->linkToken;
594 }
595
596 /**
597 * @param string|null $linkToken
598 */
599 public function setLinkToken($linkToken) {
600 $this->linkToken = $linkToken;
601 }
602
603 /**
604 * @return Collection<int, SubscriberSegmentEntity>
605 */
606 public function getSubscriberSegments(?string $status = null) {
607 if (!is_null($status)) {
608 $criteria = Criteria::create()
609 ->where(Criteria::expr()->eq('status', $status));
610 $subscriberSegments = $this->subscriberSegments->matching($criteria);
611 } else {
612 $subscriberSegments = $this->subscriberSegments;
613 }
614
615 return $subscriberSegments;
616 }
617
618 /** * @return Collection<int, SegmentEntity> */
619 public function getSegments() {
620 return $this->subscriberSegments->map(function (?SubscriberSegmentEntity $subscriberSegment = null) {
621 if (!$subscriberSegment) return null;
622 return $subscriberSegment->getSegment();
623 })->filter(function (?SegmentEntity $segment = null) {
624 return $segment !== null;
625 });
626 }
627
628 /**
629 * @return Collection<int, SubscriberCustomFieldEntity>
630 */
631 public function getSubscriberCustomFields() {
632 return $this->subscriberCustomFields;
633 }
634
635 public function getSubscriberCustomField(CustomFieldEntity $customField): ?SubscriberCustomFieldEntity {
636 $criteria = Criteria::create()
637 ->where(Criteria::expr()->eq('customField', $customField))
638 ->setMaxResults(1);
639 return $this->getSubscriberCustomFields()->matching($criteria)->first() ?: null;
640 }
641
642 /**
643 * @return Collection<int, SubscriberTagEntity>
644 */
645 public function getSubscriberTags() {
646 return $this->subscriberTags;
647 }
648
649 public function getSubscriberTag(TagEntity $tag): ?SubscriberTagEntity {
650 $criteria = Criteria::create()
651 ->where(Criteria::expr()->eq('tag', $tag))
652 ->setMaxResults(1);
653 return $this->getSubscriberTags()->matching($criteria)->first() ?: null;
654 }
655
656 /**
657 * @return float|null
658 */
659 public function getEngagementScore(): ?float {
660 return $this->engagementScore;
661 }
662
663 /**
664 * @param float|null $engagementScore
665 */
666 public function setEngagementScore(?float $engagementScore): void {
667 $this->engagementScore = $engagementScore;
668 }
669
670 /**
671 * @return DateTimeInterface|null
672 */
673 public function getEngagementScoreUpdatedAt(): ?DateTimeInterface {
674 return $this->engagementScoreUpdatedAt;
675 }
676
677 /**
678 * @param DateTimeInterface|null $engagementScoreUpdatedAt
679 */
680 public function setEngagementScoreUpdatedAt(?DateTimeInterface $engagementScoreUpdatedAt): void {
681 $this->engagementScoreUpdatedAt = $engagementScoreUpdatedAt;
682 }
683
684 public function getLastEngagementAt(): ?DateTimeInterface {
685 return $this->lastEngagementAt;
686 }
687
688 /**
689 * Sets the raw engagement timestamp without touching status. Prefer markEngaged() when
690 * recording a real engagement event (open, click, purchase, page view) so inactive
691 * subscribers are reactivated immediately instead of waiting for the maintenance cron.
692 */
693 public function setLastEngagementAt(DateTimeInterface $lastEngagementAt): void {
694 $this->lastEngagementAt = $lastEngagementAt;
695 }
696
697 /**
698 * Records engagement and immediately reactivates the subscriber if they were inactive,
699 * so they don't wait for the InactiveSubscribersMaintenance cron to be reactivated.
700 */
701 public function markEngaged(DateTimeInterface $engagedAt): void {
702 $this->setLastEngagementAt($engagedAt);
703 if ($this->getStatus() === self::STATUS_INACTIVE) {
704 $this->setStatus(self::STATUS_SUBSCRIBED);
705 }
706 }
707
708 public function getLastSendingAt(): ?DateTimeInterface {
709 return $this->lastSendingAt;
710 }
711
712 public function setLastSendingAt(?DateTimeInterface $dateTime): void {
713 $this->lastSendingAt = $dateTime;
714 }
715
716 public function getLastOpenAt(): ?DateTimeInterface {
717 return $this->lastOpenAt;
718 }
719
720 public function setLastOpenAt(?DateTimeInterface $dateTime): void {
721 $this->lastOpenAt = $dateTime;
722 }
723
724 public function getLastClickAt(): ?DateTimeInterface {
725 return $this->lastClickAt;
726 }
727
728 public function setLastClickAt(?DateTimeInterface $dateTime): void {
729 $this->lastClickAt = $dateTime;
730 }
731
732 public function getLastPurchaseAt(): ?DateTimeInterface {
733 return $this->lastPurchaseAt;
734 }
735
736 public function setLastPurchaseAt(?DateTimeInterface $dateTime): void {
737 $this->lastPurchaseAt = $dateTime;
738 }
739
740 public function getLastPageViewAt(): ?DateTimeInterface {
741 return $this->lastPageViewAt;
742 }
743
744 public function setLastPageViewAt(?DateTimeInterface $dateTime): void {
745 $this->lastPageViewAt = $dateTime;
746 }
747
748 public function setWoocommerceSyncedAt(?DateTimeInterface $woocommerceSyncedAt): void {
749 $this->woocommerceSyncedAt = $woocommerceSyncedAt;
750 }
751
752 public function getWoocommerceSyncedAt(): ?DateTimeInterface {
753 return $this->woocommerceSyncedAt;
754 }
755
756 public function getEmailCount(): int {
757 return $this->emailCount;
758 }
759
760 public function setEmailCount(int $emailCount): void {
761 $this->emailCount = $emailCount;
762 }
763
764 /** @ORM\PreFlush */
765 public function cleanupSubscriberSegments(): void {
766 // Delete old orphan SubscriberSegments to avoid errors on update
767 $this->subscriberSegments->map(function (?SubscriberSegmentEntity $subscriberSegment = null) {
768 if (!$subscriberSegment) return null;
769 if ($subscriberSegment->getSegment() === null) {
770 $this->subscriberSegments->removeElement($subscriberSegment);
771 }
772 });
773 }
774 }
775