From a416cecddf860ca8cf487e3aa0e480f8b9ed8f8f Mon Sep 17 00:00:00 2001 From: Jan Jakes Date: Tue, 3 Sep 2024 15:41:32 +0200 Subject: [PATCH] Optimize populating newsletter option fields --- mailpoet/lib/Config/Populator.php | 41 ++++++++++++----- .../integration/Config/PopulatorTest.php | 44 +++++++++++++++++++ 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/mailpoet/lib/Config/Populator.php b/mailpoet/lib/Config/Populator.php index 79d823fadc..1dffdf5711 100644 --- a/mailpoet/lib/Config/Populator.php +++ b/mailpoet/lib/Config/Populator.php @@ -78,9 +78,7 @@ class Populator { $this->wpSegment = $wpSegment; $this->referralDetector = $referralDetector; $this->prefix = Env::$dbPrefix; - $this->models = [ - 'newsletter_option_fields', - ]; + $this->models = []; $this->templates = [ 'WelcomeBlank1Column', 'WelcomeBlank12Column', @@ -169,6 +167,7 @@ class Populator { $localizer->forceLoadWebsiteLocaleText(); array_map([$this, 'populate'], $this->models); + $this->populateNewsletterOptionFields(); $this->populateNewsletterTemplates(); $this->createDefaultSegment(); @@ -382,7 +381,7 @@ class Populator { return $defaultSegment; } - protected function newsletterOptionFields() { + private function populateNewsletterOptionFields() { $optionFields = [ [ 'name' => 'isScheduled', @@ -510,13 +509,33 @@ class Populator { ], ]; - return [ - 'rows' => $optionFields, - 'identification_columns' => [ - 'name', - 'newsletter_type', - ], - ]; + // 1. Load all existing option fields from the database. + $tableName = $this->entityManager->getClassMetadata(NewsletterOptionFieldEntity::class)->getTableName(); + $connection = $this->entityManager->getConnection(); + $existingOptionFields = $connection->createQueryBuilder() + ->select('of.name, of.newsletter_type') + ->from($tableName, 'of') + ->executeQuery() + ->fetchAllAssociative(); + + // 2. Insert new option fields using a single query (good for first installs). + $inserts = array_udiff( + $optionFields, + $existingOptionFields, + fn($a, $b) => [$a['name'], $a['newsletter_type']] <=> [$b['name'], $b['newsletter_type']] + ); + if ($inserts) { + $placeholders = implode(',', array_fill(0, count($inserts), '(?, ?)')); + $connection->executeStatement( + "INSERT INTO $tableName (name, newsletter_type) VALUES $placeholders", + array_merge( + ...array_map( + fn($of) => [$of['name'], $of['newsletter_type']], + $inserts + ) + ) + ); + } } private function populateNewsletterTemplates(): void { diff --git a/mailpoet/tests/integration/Config/PopulatorTest.php b/mailpoet/tests/integration/Config/PopulatorTest.php index f57cbe5f0c..4c4236e1df 100644 --- a/mailpoet/tests/integration/Config/PopulatorTest.php +++ b/mailpoet/tests/integration/Config/PopulatorTest.php @@ -3,13 +3,48 @@ namespace MailPoet\Test\Config; use MailPoet\Config\Populator; +use MailPoet\Entities\NewsletterOptionFieldEntity; use MailPoet\Entities\NewsletterTemplateEntity; use MailPoetTest; use MailPoetVendor\Doctrine\ORM\Query; class PopulatorTest extends MailPoetTest { + private const OPTION_FIELD_COUNT = 31; private const TEMPLATE_COUNT = 76; + public function testItInsertsOptionFields(): void { + $populator = $this->diContainer->get(Populator::class); + + // no option fields + $this->entityManager->createQueryBuilder() + ->delete(NewsletterOptionFieldEntity::class, 'f') + ->getQuery() + ->execute(); + $optionFields = $this->getAllOptionFields(); + $this->assertSame(0, count($optionFields)); + + // insert all option fields + $populator->up(); + $optionFields = $this->getAllOptionFields(); + $this->assertSame(self::OPTION_FIELD_COUNT, count($optionFields)); + + // delete only some fields + $this->entityManager->createQueryBuilder() + ->delete(NewsletterOptionFieldEntity::class, 'f') + ->where('f.id IN (:ids)') + ->setParameter('ids', array_map(fn($field) => $field->getId(), array_slice($optionFields, 0, 10))) + ->getQuery() + ->execute(); + + $optionFields = $this->getAllOptionFields(); + $this->assertSame(self::OPTION_FIELD_COUNT - 10, count($optionFields)); + + // insert new option fields + $populator->up(); + $optionFields = $this->getAllOptionFields(); + $this->assertSame(self::OPTION_FIELD_COUNT, count($optionFields)); + } + public function testItInsertsTemplates(): void { $populator = $this->diContainer->get(Populator::class); @@ -86,6 +121,15 @@ class PopulatorTest extends MailPoetTest { $this->assertSame(self::TEMPLATE_COUNT, count($templates)); } + private function getAllOptionFields(): array { + return (array)$this->entityManager->createQueryBuilder() + ->select('f') + ->from(NewsletterOptionFieldEntity::class, 'f') + ->getQuery() + ->setHint(Query::HINT_REFRESH, true) + ->getResult(); + } + private function getAllTemplates(): array { return (array)$this->entityManager->createQueryBuilder() ->select('t')