createTable('form'); $form->addColumn('id', 'string', ['length' => 64]); $form->addColumn('website_id', 'string', ['length' => 64]); $form->addColumn('locale', 'string', ['length' => 12]); $form->addColumn('slug', 'string', ['length' => 80]); $form->addColumn('name', 'string', ['length' => 120]); $form->addColumn('fields', 'json'); // Recipient, subject, consent text and the message shown after a successful send. $form->addColumn('settings', 'json'); $form->addColumn('version', 'integer', ['default' => 0]); $form->setPrimaryKey(['id']); $form->addUniqueIndex(['website_id', 'locale', 'slug'], 'uniq_form_slug'); $submission = $schema->createTable('form_submission'); $submission->addColumn('id', 'string', ['length' => 64]); $submission->addColumn('form_id', 'string', ['length' => 64]); $submission->addColumn('data', 'json'); // Keyed hash of the sender address, never the address itself: enough to rate limit, // not enough to reconstruct who sent from where. $submission->addColumn('sender_hash', 'string', ['length' => 64]); $submission->addColumn('created_at', 'datetime_immutable'); // When the sender ticked the consent box. Null means the form asked for no consent. $submission->addColumn('consent_at', 'datetime_immutable', ['notnull' => false]); $submission->addColumn('delivered', 'boolean', ['default' => false]); $submission->setPrimaryKey(['id']); $submission->addIndex(['form_id', 'created_at'], 'idx_submission_form'); $submission->addIndex(['sender_hash', 'created_at'], 'idx_submission_sender'); } public function down(Schema $schema): void { $this->throwIrreversibleMigrationException('Contains form definitions and personal data from submissions. Restore a verified database backup with matching code.'); } }