Use doctrine for exporting subscribers

[MAILPOET-3376]
This commit is contained in:
Jan Lysý
2021-02-22 12:57:05 +01:00
committed by Veljko V
parent b2e0bac2d8
commit f2655c40aa
2 changed files with 110 additions and 40 deletions

View File

@ -9,7 +9,9 @@ use MailPoet\CustomFields\CustomFieldsRepository;
use MailPoet\Models\ScheduledTask; use MailPoet\Models\ScheduledTask;
use MailPoet\Models\Segment; use MailPoet\Models\Segment;
use MailPoet\Newsletter\Options\NewsletterOptionsRepository; use MailPoet\Newsletter\Options\NewsletterOptionsRepository;
use MailPoet\Segments\SegmentsRepository;
use MailPoet\Segments\WP; use MailPoet\Segments\WP;
use MailPoet\Subscribers\ImportExport\Export\Export;
use MailPoet\Subscribers\ImportExport\Import\Import; use MailPoet\Subscribers\ImportExport\Import\Import;
use MailPoet\Subscribers\ImportExport\Import\MailChimp; use MailPoet\Subscribers\ImportExport\Import\MailChimp;
use MailPoet\Subscribers\ImportExport\ImportExportRepository; use MailPoet\Subscribers\ImportExport\ImportExportRepository;
@ -30,6 +32,9 @@ class ImportExport extends APIEndpoint {
/** @var NewsletterOptionsRepository */ /** @var NewsletterOptionsRepository */
private $newsletterOptionsRepository; private $newsletterOptionsRepository;
/** @var SegmentsRepository */
private $segmentsRepository;
/** @var SubscribersRepository */ /** @var SubscribersRepository */
private $subscriberRepository; private $subscriberRepository;
@ -42,12 +47,14 @@ class ImportExport extends APIEndpoint {
CustomFieldsRepository $customFieldsRepository, CustomFieldsRepository $customFieldsRepository,
ImportExportRepository $importExportRepository, ImportExportRepository $importExportRepository,
NewsletterOptionsRepository $newsletterOptionsRepository, NewsletterOptionsRepository $newsletterOptionsRepository,
SegmentsRepository $segmentsRepository,
SubscribersRepository $subscribersRepository SubscribersRepository $subscribersRepository
) { ) {
$this->wpSegment = $wpSegment; $this->wpSegment = $wpSegment;
$this->customFieldsRepository = $customFieldsRepository; $this->customFieldsRepository = $customFieldsRepository;
$this->importExportRepository = $importExportRepository; $this->importExportRepository = $importExportRepository;
$this->newsletterOptionsRepository = $newsletterOptionsRepository; $this->newsletterOptionsRepository = $newsletterOptionsRepository;
$this->segmentsRepository = $segmentsRepository;
$this->subscriberRepository = $subscribersRepository; $this->subscriberRepository = $subscribersRepository;
} }
@ -109,7 +116,10 @@ class ImportExport extends APIEndpoint {
public function processExport($data) { public function processExport($data) {
try { try {
$export = new \MailPoet\Subscribers\ImportExport\Export\Export( $export = new Export(
$this->customFieldsRepository,
$this->importExportRepository,
$this->segmentsRepository,
json_decode($data, true) json_decode($data, true)
); );
$process = $export->process(); $process = $export->process();

View File

@ -3,8 +3,11 @@
namespace MailPoet\Subscribers\ImportExport\Export; namespace MailPoet\Subscribers\ImportExport\Export;
use MailPoet\Config\Env; 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\ImportExportFactory;
use MailPoet\Subscribers\ImportExport\ImportExportRepository;
use MailPoet\Util\Security; use MailPoet\Util\Security;
use MailPoet\WP\Functions as WPFunctions; use MailPoet\WP\Functions as WPFunctions;
use MailPoetVendor\XLSXWriter; use MailPoetVendor\XLSXWriter;
@ -19,24 +22,41 @@ class Export {
public $exportPath; public $exportPath;
public $exportFile; public $exportFile;
public $exportFileURL; public $exportFileURL;
public $defaultSubscribersGetter;
public $dynamicSubscribersGetter;
public function __construct($data) { /** @var int */
private $subscribersOffset;
/** @var array<SegmentEntity|null> */
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) { if (strpos((string)@ini_get('disable_functions'), 'set_time_limit') === false) {
set_time_limit(0); set_time_limit(0);
} }
$this->defaultSubscribersGetter = new DefaultSubscribersGetter( $this->subscribersOffset = 0;
$data['segments'], $this->segmentIndex = 0;
self::SUBSCRIBER_BATCH_SIZE $this->segments = $this->getSegments($data['segments']);
);
$this->dynamicSubscribersGetter = new DynamicSubscribersGetter(
$data['segments'],
self::SUBSCRIBER_BATCH_SIZE
);
$this->exportFormatOption = $data['export_format_option']; $this->exportFormatOption = $data['export_format_option'];
$this->subscriberFields = $data['subscriber_fields']; $this->subscriberFields = $data['subscriber_fields'];
$this->subscriberCustomFields = $this->getSubscriberCustomFields(); $this->subscriberCustomFields = $this->getSubscriberCustomFields();
@ -57,9 +77,9 @@ class Export {
return Env::$tempPath; return Env::$tempPath;
} }
public function process() { public function process(): array {
$processedSubscribers = 0; $processedSubscribers = 0;
$this->defaultSubscribersGetter->reset(); $this->resetCounters();
try { try {
if (is_writable($this->exportPath) === false) { if (is_writable($this->exportPath) === false) {
throw new \Exception(__('The export file could not be saved on the server.', 'mailpoet')); 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; $processedSubscribers = 0;
$formattedSubscriberFields = $this->formattedSubscriberFields; $formattedSubscriberFields = $this->formattedSubscriberFields;
$cSVFile = fopen($this->exportFile, 'w'); $cSVFile = fopen($this->exportFile, 'w');
@ -108,29 +128,26 @@ class Export {
) . PHP_EOL ) . PHP_EOL
); );
$subscribers = $this->getSubscribers(); while (($subscribers = $this->getSubscribers()) !== null) {
while ($subscribers !== false) {
$processedSubscribers += count($subscribers); $processedSubscribers += count($subscribers);
foreach ($subscribers as $subscriber) { foreach ($subscribers as $subscriber) {
$row = $this->formatSubscriberData($subscriber); $row = $this->formatSubscriberData($subscriber);
$row[] = ucwords($subscriber['segment_name']); $row[] = ucwords($subscriber['segment_name']);
fwrite($cSVFile, implode(',', array_map($formatCSV, $row)) . "\n"); fwrite($cSVFile, implode(',', array_map($formatCSV, $row)) . "\n");
} }
$subscribers = $this->getSubscribers();
} }
fclose($cSVFile);
return $processedSubscribers; return $processedSubscribers;
} }
public function generateXLSX() { public function generateXLSX(): int {
$processedSubscribers = 0; $processedSubscribers = 0;
$xLSXWriter = new XLSXWriter(); $xLSXWriter = new XLSXWriter();
$xLSXWriter->setAuthor('MailPoet (www.mailpoet.com)'); $xLSXWriter->setAuthor('MailPoet (www.mailpoet.com)');
$lastSegment = false; $lastSegment = false;
$processedSegments = []; $processedSegments = [];
$subscribers = $this->getSubscribers(); while (($subscribers = $this->getSubscribers()) !== null) {
while ($subscribers !== false) {
$processedSubscribers += count($subscribers); $processedSubscribers += count($subscribers);
foreach ($subscribers as $i => $subscriber) { foreach ($subscribers as $i => $subscriber) {
$currentSegment = ucwords($subscriber['segment_name']); $currentSegment = ucwords($subscriber['segment_name']);
@ -167,7 +184,6 @@ class Export {
$this->formatSubscriberData($subscriber) $this->formatSubscriberData($subscriber)
); );
} }
$subscribers = $this->getSubscribers();
} }
$xLSXWriter->writeToFile($this->exportFile); $xLSXWriter->writeToFile($this->exportFile);
return $processedSubscribers; return $processedSubscribers;
@ -177,15 +193,28 @@ class Export {
return $xLSXWriter->writeSheetRow(ucwords($segment), $data); return $xLSXWriter->writeSheetRow(ucwords($segment), $data);
} }
public function getSubscribers() { public function getSubscribers(): ?array {
$subscribers = $this->defaultSubscribersGetter->get(); $segment = array_key_exists($this->segmentIndex, $this->segments) ? $this->segments[$this->segmentIndex] : false;
if ($subscribers === false) { if ($segment === false) {
$subscribers = $this->dynamicSubscribersGetter->get(); 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; return $subscribers;
} }
public function getExportFileURL($file) { public function getExportFileURL($file): string {
return sprintf( return sprintf(
'%s/%s', '%s/%s',
Env::$tempUrl, Env::$tempUrl,
@ -193,7 +222,7 @@ class Export {
); );
} }
public function getExportFile($format) { public function getExportFile($format): string {
return sprintf( return sprintf(
$this->exportPath . '/' . self::getFilePrefix() . '%s.%s', $this->exportPath . '/' . self::getFilePrefix() . '%s.%s',
Security::generateRandomString(15), Security::generateRandomString(15),
@ -201,15 +230,46 @@ class Export {
); );
} }
public function getSubscriberCustomFields() { /**
return array_column( * @return array<int, string>
CustomField::findArray(), */
'name', public function getSubscriberCustomFields(): array {
'id' $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<SegmentEntity|null>
*/
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'); $exportFactory = new ImportExportFactory('export');
$translatedFields = $exportFactory->getSubscriberFields(); $translatedFields = $exportFactory->getSubscriberFields();
return array_map(function($field) use ( return array_map(function($field) use (
@ -223,7 +283,7 @@ class Export {
}, $subscriberFields); }, $subscriberFields);
} }
public function formatSubscriberData($subscriber) { public function formatSubscriberData($subscriber): array {
return array_map(function($field) use ($subscriber) { return array_map(function($field) use ($subscriber) {
return $subscriber[$field]; return $subscriber[$field];
}, $this->subscriberFields); }, $this->subscriberFields);