feat: MailJet list handling with DOI, webhook receiver and API endpoint

addresses #869cut134
This commit is contained in:
Björn Fromme
2026-04-29 09:51:57 +02:00
parent df7885ca1c
commit e4ef2f7adc
46 changed files with 3389 additions and 322 deletions
+27
View File
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20260422120000 extends AbstractMigration
{
public function getDescription(): string
{
return 'Store requested Mailjet list IDs on newsletter opt-in confirmations';
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE newsletter_opt_in_confirmation ADD mailjet_list_ids JSON DEFAULT NULL COMMENT '(DC2Type:json)'");
$this->addSql('UPDATE newsletter_opt_in_confirmation SET mailjet_list_ids = JSON_ARRAY(10321569) WHERE mailjet_list_ids IS NULL');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE newsletter_opt_in_confirmation DROP mailjet_list_ids');
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20260423120000 extends AbstractMigration
{
public function getDescription(): string
{
return 'Store optional first and last names on newsletter opt-in confirmations';
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE newsletter_opt_in_confirmation ADD first_name VARCHAR(255) DEFAULT NULL, ADD last_name VARCHAR(255) DEFAULT NULL");
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE newsletter_opt_in_confirmation DROP first_name, DROP last_name');
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20260424110732 extends AbstractMigration
{
public function getDescription(): string
{
return 'Track newsletter revocations on opt-in confirmations';
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE newsletter_opt_in_confirmation ADD revoked_at DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)'");
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE newsletter_opt_in_confirmation DROP revoked_at');
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20260429120000 extends AbstractMigration
{
public function getDescription(): string
{
return 'Create per-list newsletter consent table and backfill confirmed opt-ins';
}
public function up(Schema $schema): void
{
$this->addSql("CREATE TABLE newsletter_consent (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(255) NOT NULL, mailjet_list_id INT NOT NULL, confirmed_at DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)', revoked_at DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)', first_name VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', updated_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', UNIQUE INDEX UNIQ_NEWSLETTER_CONSENT_EMAIL_LIST (email, mailjet_list_id), INDEX IDX_NEWSLETTER_CONSENT_EMAIL (email), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB");
$this->addSql("INSERT IGNORE INTO newsletter_consent (email, mailjet_list_id, confirmed_at, revoked_at, first_name, last_name, created_at, updated_at) SELECT email, 10321569, confirmed_at, revoked_at, first_name, last_name, created_at, COALESCE(confirmed_at, created_at) FROM newsletter_opt_in_confirmation WHERE confirmed_at IS NOT NULL AND (mailjet_list_ids IS NULL OR JSON_LENGTH(mailjet_list_ids) = 0)");
$this->addSql("INSERT IGNORE INTO newsletter_consent (email, mailjet_list_id, confirmed_at, revoked_at, first_name, last_name, created_at, updated_at) SELECT c.email, CAST(j.list_id AS UNSIGNED), c.confirmed_at, c.revoked_at, c.first_name, c.last_name, c.created_at, COALESCE(c.confirmed_at, c.created_at) FROM newsletter_opt_in_confirmation c JOIN JSON_TABLE(c.mailjet_list_ids, '$[*]' COLUMNS (list_id VARCHAR(32) PATH '$')) j WHERE c.confirmed_at IS NOT NULL");
$this->addSql('DELETE FROM newsletter_opt_in_confirmation WHERE confirmed_at IS NOT NULL');
}
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE newsletter_consent');
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20260429130000 extends AbstractMigration
{
public function getDescription(): string
{
return 'Rename newsletter opt-in confirmation table to newsletter opt-in request';
}
public function up(Schema $schema): void
{
$this->addSql('RENAME TABLE newsletter_opt_in_confirmation TO newsletter_opt_in_request');
}
public function down(Schema $schema): void
{
$this->addSql('RENAME TABLE newsletter_opt_in_request TO newsletter_opt_in_confirmation');
}
}