From f2655c40aaac616f79d93b6253f65c5c0c9d45a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lys=C3=BD?= Date: Mon, 22 Feb 2021 12:57:05 +0100 Subject: [PATCH] Use doctrine for exporting subscribers [MAILPOET-3376] --- lib/API/JSON/v1/ImportExport.php | 12 +- .../ImportExport/Export/Export.php | 138 +++++++++++++----- 2 files changed, 110 insertions(+), 40 deletions(-) diff --git a/lib/API/JSON/v1/ImportExport.php b/lib/API/JSON/v1/ImportExport.php index 95f2537f77..a53f4127b1 100644 --- a/lib/API/JSON/v1/ImportExport.php +++ b/lib/API/JSON/v1/ImportExport.php @@ -9,7 +9,9 @@ use MailPoet\CustomFields\CustomFieldsRepository; use MailPoet\Models\ScheduledTask; use MailPoet\Models\Segment; use MailPoet\Newsletter\Options\NewsletterOptionsRepository; +use MailPoet\Segments\SegmentsRepository; use MailPoet\Segments\WP; +use MailPoet\Subscribers\ImportExport\Export\Export; use MailPoet\Subscribers\ImportExport\Import\Import; use MailPoet\Subscribers\ImportExport\Import\MailChimp; use MailPoet\Subscribers\ImportExport\ImportExportRepository; @@ -30,6 +32,9 @@ class ImportExport extends APIEndpoint { /** @var NewsletterOptionsRepository */ private $newsletterOptionsRepository; + /** @var SegmentsRepository */ + private $segmentsRepository; + /** @var SubscribersRepository */ private $subscriberRepository; @@ -42,12 +47,14 @@ class ImportExport extends APIEndpoint { CustomFieldsRepository $customFieldsRepository, ImportExportRepository $importExportRepository, NewsletterOptionsRepository $newsletterOptionsRepository, + SegmentsRepository $segmentsRepository, SubscribersRepository $subscribersRepository ) { $this->wpSegment = $wpSegment; $this->customFieldsRepository = $customFieldsRepository; $this->importExportRepository = $importExportRepository; $this->newsletterOptionsRepository = $newsletterOptionsRepository; + $this->segmentsRepository = $segmentsRepository; $this->subscriberRepository = $subscribersRepository; } @@ -109,7 +116,10 @@ class ImportExport extends APIEndpoint { public function processExport($data) { try { - $export = new \MailPoet\Subscribers\ImportExport\Export\Export( + $export = new Export( + $this->customFieldsRepository, + $this->importExportRepository, + $this->segmentsRepository, json_decode($data, true) ); $process = $export->process(); diff --git a/lib/Subscribers/ImportExport/Export/Export.php b/lib/Subscribers/ImportExport/Export/Export.php index 2598b5f4c0..0a207d36f0 100644 --- a/lib/Subscribers/ImportExport/Export/Export.php +++ b/lib/Subscribers/ImportExport/Export/Export.php @@ -3,8 +3,11 @@ namespace MailPoet\Subscribers\ImportExport\Export; use MailPoet\Config\Env; -use MailPoet\Models\CustomField; +use MailPoet\CustomFields\CustomFieldsRepository; +use MailPoet\Entities\SegmentEntity; +use MailPoet\Segments\SegmentsRepository; use MailPoet\Subscribers\ImportExport\ImportExportFactory; +use MailPoet\Subscribers\ImportExport\ImportExportRepository; use MailPoet\Util\Security; use MailPoet\WP\Functions as WPFunctions; use MailPoetVendor\XLSXWriter; @@ -19,24 +22,41 @@ class Export { public $exportPath; public $exportFile; public $exportFileURL; - public $defaultSubscribersGetter; - public $dynamicSubscribersGetter; - public function __construct($data) { + /** @var int */ + private $subscribersOffset; + + /** @var array */ + private $segments; + + /** @var int */ + private $segmentIndex; + + /** @var CustomFieldsRepository */ + private $customFieldsRepository; + + /** @var ImportExportRepository */ + private $importExportRepository; + + /** @var SegmentsRepository */ + private $segmentsRepository; + + public function __construct( + CustomFieldsRepository $customFieldsRepository, + ImportExportRepository $importExportRepository, + SegmentsRepository $segmentsRepository, + array $data + ) { + $this->customFieldsRepository = $customFieldsRepository; + $this->importExportRepository = $importExportRepository; + $this->segmentsRepository = $segmentsRepository; if (strpos((string)@ini_get('disable_functions'), 'set_time_limit') === false) { set_time_limit(0); } - $this->defaultSubscribersGetter = new DefaultSubscribersGetter( - $data['segments'], - self::SUBSCRIBER_BATCH_SIZE - ); - - $this->dynamicSubscribersGetter = new DynamicSubscribersGetter( - $data['segments'], - self::SUBSCRIBER_BATCH_SIZE - ); - + $this->subscribersOffset = 0; + $this->segmentIndex = 0; + $this->segments = $this->getSegments($data['segments']); $this->exportFormatOption = $data['export_format_option']; $this->subscriberFields = $data['subscriber_fields']; $this->subscriberCustomFields = $this->getSubscriberCustomFields(); @@ -57,9 +77,9 @@ class Export { return Env::$tempPath; } - public function process() { + public function process(): array { $processedSubscribers = 0; - $this->defaultSubscribersGetter->reset(); + $this->resetCounters(); try { if (is_writable($this->exportPath) === false) { throw new \Exception(__('The export file could not be saved on the server.', 'mailpoet')); @@ -83,7 +103,7 @@ class Export { ]; } - public function generateCSV() { + public function generateCSV(): int { $processedSubscribers = 0; $formattedSubscriberFields = $this->formattedSubscriberFields; $cSVFile = fopen($this->exportFile, 'w'); @@ -108,29 +128,26 @@ class Export { ) . PHP_EOL ); - $subscribers = $this->getSubscribers(); - while ($subscribers !== false) { + while (($subscribers = $this->getSubscribers()) !== null) { $processedSubscribers += count($subscribers); foreach ($subscribers as $subscriber) { $row = $this->formatSubscriberData($subscriber); $row[] = ucwords($subscriber['segment_name']); fwrite($cSVFile, implode(',', array_map($formatCSV, $row)) . "\n"); } - $subscribers = $this->getSubscribers(); } - fclose($cSVFile); + return $processedSubscribers; } - public function generateXLSX() { + public function generateXLSX(): int { $processedSubscribers = 0; $xLSXWriter = new XLSXWriter(); $xLSXWriter->setAuthor('MailPoet (www.mailpoet.com)'); $lastSegment = false; $processedSegments = []; - $subscribers = $this->getSubscribers(); - while ($subscribers !== false) { + while (($subscribers = $this->getSubscribers()) !== null) { $processedSubscribers += count($subscribers); foreach ($subscribers as $i => $subscriber) { $currentSegment = ucwords($subscriber['segment_name']); @@ -167,7 +184,6 @@ class Export { $this->formatSubscriberData($subscriber) ); } - $subscribers = $this->getSubscribers(); } $xLSXWriter->writeToFile($this->exportFile); return $processedSubscribers; @@ -177,15 +193,28 @@ class Export { return $xLSXWriter->writeSheetRow(ucwords($segment), $data); } - public function getSubscribers() { - $subscribers = $this->defaultSubscribersGetter->get(); - if ($subscribers === false) { - $subscribers = $this->dynamicSubscribersGetter->get(); + public function getSubscribers(): ?array { + $segment = array_key_exists($this->segmentIndex, $this->segments) ? $this->segments[$this->segmentIndex] : false; + if ($segment === false) { + return null; } + + $subscribers = $this->importExportRepository->getSubscribersBatchBySegment( + $segment, + self::SUBSCRIBER_BATCH_SIZE, + $this->subscribersOffset + ); + $this->subscribersOffset += count($subscribers); + + if (count($subscribers) < self::SUBSCRIBER_BATCH_SIZE) { + $this->segmentIndex++; + $this->subscribersOffset = 0; + } + return $subscribers; } - public function getExportFileURL($file) { + public function getExportFileURL($file): string { return sprintf( '%s/%s', Env::$tempUrl, @@ -193,7 +222,7 @@ class Export { ); } - public function getExportFile($format) { + public function getExportFile($format): string { return sprintf( $this->exportPath . '/' . self::getFilePrefix() . '%s.%s', Security::generateRandomString(15), @@ -201,15 +230,46 @@ class Export { ); } - public function getSubscriberCustomFields() { - return array_column( - CustomField::findArray(), - 'name', - 'id' - ); + /** + * @return array + */ + public function getSubscriberCustomFields(): array { + $result = []; + foreach ($this->customFieldsRepository->findAll() as $customField) { + $result[(int)$customField->getId()] = $customField->getName(); + } + return $result; } - public function formatSubscriberFields($subscriberFields, $subscriberCustomFields) { + /** + * @param array $segmentIds + * @return array + */ + private function getSegments(array $segmentIds): array { + $segments = $this->segmentsRepository->findBy(['id' => $segmentIds]); + $result = []; + foreach ($segmentIds as $segmentId) { + $segmentId = (int)$segmentId; + $segment = current(array_filter($segments, function (SegmentEntity $segment) use ($segmentId): bool { + return $segment->getId() === $segmentId; + })) ?: null; + + if (!$segment && $segmentId !== 0) { + continue; + } + + $result[] = $segment; + } + + return $result; + } + + private function resetCounters(): void { + $this->segmentIndex = 0; + $this->subscribersOffset = 0; + } + + public function formatSubscriberFields($subscriberFields, $subscriberCustomFields): array { $exportFactory = new ImportExportFactory('export'); $translatedFields = $exportFactory->getSubscriberFields(); return array_map(function($field) use ( @@ -223,7 +283,7 @@ class Export { }, $subscriberFields); } - public function formatSubscriberData($subscriber) { + public function formatSubscriberData($subscriber): array { return array_map(function($field) use ($subscriber) { return $subscriber[$field]; }, $this->subscriberFields);