Нет описания

SegmentsRepository.php 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. <?php
  2. namespace MailPoet\Segments;
  3. if (!defined('ABSPATH')) exit;
  4. use DateTime;
  5. use MailPoet\Doctrine\Repository;
  6. use MailPoet\Entities\DynamicSegmentFilterData;
  7. use MailPoet\Entities\DynamicSegmentFilterEntity;
  8. use MailPoet\Entities\SegmentEntity;
  9. use MailPoet\Entities\SubscriberSegmentEntity;
  10. use MailPoet\Form\FormsRepository;
  11. use MailPoet\Newsletter\Segment\NewsletterSegmentRepository;
  12. use MailPoet\NotFoundException;
  13. use MailPoet\WP\Functions as WPFunctions;
  14. use MailPoetVendor\Carbon\Carbon;
  15. use MailPoetVendor\Doctrine\DBAL\Connection;
  16. use MailPoetVendor\Doctrine\ORM\EntityManager;
  17. /**
  18. * @extends Repository<SegmentEntity>
  19. */
  20. class SegmentsRepository extends Repository {
  21. /** @var NewsletterSegmentRepository */
  22. private $newsletterSegmentRepository;
  23. /** @var FormsRepository */
  24. private $formsRepository;
  25. public function __construct(
  26. EntityManager $entityManager,
  27. NewsletterSegmentRepository $newsletterSegmentRepository,
  28. FormsRepository $formsRepository
  29. ) {
  30. parent::__construct($entityManager);
  31. $this->newsletterSegmentRepository = $newsletterSegmentRepository;
  32. $this->formsRepository = $formsRepository;
  33. }
  34. protected function getEntityClassName() {
  35. return SegmentEntity::class;
  36. }
  37. public function getWPUsersSegment() {
  38. return $this->findOneBy(['type' => SegmentEntity::TYPE_WP_USERS]);
  39. }
  40. public function getWooCommerceSegment(): SegmentEntity {
  41. $segment = $this->findOneBy(['type' => SegmentEntity::TYPE_WC_USERS]);
  42. if (!$segment) {
  43. // create the WooCommerce customers segment
  44. $segment = new SegmentEntity(
  45. WPFunctions::get()->__('WooCommerce Customers', 'mailpoet'),
  46. SegmentEntity::TYPE_WC_USERS,
  47. WPFunctions::get()->__('This list contains all of your WooCommerce customers.', 'mailpoet')
  48. );
  49. $this->entityManager->persist($segment);
  50. $this->entityManager->flush();
  51. }
  52. return $segment;
  53. }
  54. public function getCountsPerType(): array {
  55. $results = $this->doctrineRepository->createQueryBuilder('s')
  56. ->select('s.type, COUNT(s) as cnt')
  57. ->where('s.deletedAt IS NULL')
  58. ->groupBy('s.type')
  59. ->getQuery()
  60. ->getResult();
  61. $countMap = [];
  62. foreach ($results as $result) {
  63. $countMap[$result['type']] = (int)$result['cnt'];
  64. }
  65. return $countMap;
  66. }
  67. public function isNameUnique(string $name, ?int $id): bool {
  68. $qb = $this->doctrineRepository->createQueryBuilder('s')
  69. ->select('s')
  70. ->where('s.name = :name')
  71. ->setParameter('name', $name);
  72. if ($id !== null) {
  73. $qb->andWhere('s.id != :id')
  74. ->setParameter('id', $id);
  75. }
  76. $results = $qb->getQuery()
  77. ->getResult();
  78. return count($results) === 0;
  79. }
  80. /**
  81. * @param DynamicSegmentFilterData[] $filtersData
  82. */
  83. public function createOrUpdate(
  84. string $name,
  85. string $description = '',
  86. string $type = SegmentEntity::TYPE_DEFAULT,
  87. array $filtersData = [],
  88. ?int $id = null
  89. ): SegmentEntity {
  90. if ($id) {
  91. $segment = $this->findOneById($id);
  92. if (!$segment instanceof SegmentEntity) {
  93. throw new NotFoundException("Segment with ID [{$id}] was not found.");
  94. }
  95. $segment->setName($name);
  96. $segment->setDescription($description);
  97. } else {
  98. $segment = new SegmentEntity($name, $type, $description);
  99. $this->persist($segment);
  100. }
  101. // We want to remove redundant filters before update
  102. while ($segment->getDynamicFilters()->count() > count($filtersData)) {
  103. $filterEntity = $segment->getDynamicFilters()->last();
  104. if ($filterEntity) {
  105. $segment->getDynamicFilters()->removeElement($filterEntity);
  106. $this->entityManager->remove($filterEntity);
  107. }
  108. }
  109. foreach ($filtersData as $key => $filterData) {
  110. if ($filterData instanceof DynamicSegmentFilterData) {
  111. $filterEntity = $segment->getDynamicFilters()->get($key);
  112. if (!$filterEntity instanceof DynamicSegmentFilterEntity) {
  113. $filterEntity = new DynamicSegmentFilterEntity($segment, $filterData);
  114. $segment->getDynamicFilters()->add($filterEntity);
  115. $this->entityManager->persist($filterEntity);
  116. } else {
  117. $filterEntity->setFilterData($filterData);
  118. }
  119. }
  120. }
  121. $this->flush();
  122. return $segment;
  123. }
  124. public function bulkDelete(array $ids, $type = SegmentEntity::TYPE_DEFAULT) {
  125. if (empty($ids)) {
  126. return 0;
  127. }
  128. return $this->entityManager->transactional(function (EntityManager $entityManager) use ($ids, $type) {
  129. $subscriberSegmentTable = $entityManager->getClassMetadata(SubscriberSegmentEntity::class)->getTableName();
  130. $segmentTable = $entityManager->getClassMetadata(SegmentEntity::class)->getTableName();
  131. $segmentFiltersTable = $entityManager->getClassMetadata(DynamicSegmentFilterEntity::class)->getTableName();
  132. $entityManager->getConnection()->executeUpdate("
  133. DELETE ss FROM $subscriberSegmentTable ss
  134. JOIN $segmentTable s ON ss.`segment_id` = s.`id`
  135. WHERE ss.`segment_id` IN (:ids)
  136. AND s.`type` = :type
  137. ", [
  138. 'ids' => $ids,
  139. 'type' => $type,
  140. ], ['ids' => Connection::PARAM_INT_ARRAY]);
  141. $entityManager->getConnection()->executeUpdate("
  142. DELETE df FROM $segmentFiltersTable df
  143. WHERE df.`segment_id` IN (:ids)
  144. ", [
  145. 'ids' => $ids,
  146. ], ['ids' => Connection::PARAM_INT_ARRAY]);
  147. return $entityManager->getConnection()->executeUpdate("
  148. DELETE s FROM $segmentTable s
  149. WHERE s.`id` IN (:ids)
  150. AND s.`type` = :type
  151. ", [
  152. 'ids' => $ids,
  153. 'type' => $type,
  154. ], ['ids' => Connection::PARAM_INT_ARRAY]);
  155. });
  156. }
  157. public function bulkTrash(array $ids, string $type = SegmentEntity::TYPE_DEFAULT): int {
  158. $activelyUsedInNewsletters = $this->newsletterSegmentRepository->getSubjectsOfActivelyUsedEmailsForSegments($ids);
  159. $activelyUsedInForms = $this->formsRepository->getNamesOfFormsForSegments();
  160. $activelyUsed = array_unique(array_merge(array_keys($activelyUsedInNewsletters), array_keys($activelyUsedInForms)));
  161. $ids = array_diff($ids, $activelyUsed);
  162. return $this->updateDeletedAt($ids, new Carbon(), $type);
  163. }
  164. public function doTrash(array $ids, string $type = SegmentEntity::TYPE_DEFAULT): int {
  165. return $this->updateDeletedAt($ids, new Carbon(), $type);
  166. }
  167. public function bulkRestore(array $ids, string $type = SegmentEntity::TYPE_DEFAULT): int {
  168. return $this->updateDeletedAt($ids, null, $type);
  169. }
  170. private function updateDeletedAt(array $ids, ?DateTime $deletedAt, string $type): int {
  171. if (empty($ids)) {
  172. return 0;
  173. }
  174. $rows = $this->entityManager->createQueryBuilder()->update(SegmentEntity::class, 's')
  175. ->set('s.deletedAt', ':deletedAt')
  176. ->where('s.id IN (:ids)')
  177. ->andWhere('s.type IN (:type)')
  178. ->setParameter('deletedAt', $deletedAt)
  179. ->setParameter('ids', $ids)
  180. ->setParameter('type', $type)
  181. ->getQuery()->execute();
  182. return $rows;
  183. }
  184. public function findByUpdatedScoreNotInLastDay(int $limit): array {
  185. $dateTime = (new Carbon())->subDay();
  186. return $this->entityManager->createQueryBuilder()
  187. ->select('s')
  188. ->from(SegmentEntity::class, 's')
  189. ->where('s.averageEngagementScoreUpdatedAt IS NULL')
  190. ->orWhere('s.averageEngagementScoreUpdatedAt < :dateTime')
  191. ->setParameter('dateTime', $dateTime)
  192. ->getQuery()
  193. ->setMaxResults($limit)
  194. ->getResult();
  195. }
  196. }