feat(contact): Contact model for public-ticket dedupe (Pattern B)#27
Draft
feat(contact): Contact model for public-ticket dedupe (Pattern B)#27
Conversation
…ern B) WordPress port of the Pattern B design shipped in escalated-nestjs PR #17 and companion PRs for Laravel / Rails / Django / Adonis / .NET. Changes: - class-activator.php — adds nullable contact_id column to escalated_tickets (via dbDelta; idempotent on re-activation) and a new escalated_contacts table with unique email index and nullable user_id. - includes/Models/Contact.php — class with: static normalize_email (trim + lowercase, pure) static decide_action (branch selection, pure) static find_by_email / find static find_or_create_by_email (dedupe, dual-mode name fill) static link_to_user static promote_to_user (link + back-stamp requester_id on existing tickets) - tests/Test_Contact_Model.php — 7 WP_UnitTestCase cases (normalize_email ×3, decide_action ×4). Inline guest_name / guest_email / guest_token columns preserved on tickets for backwards compatibility; follow-up pass migrates the AJAX handler and inbound controller to write via Contact. Local verification: WP test library not installed in author's env; CI on this PR will run the tests. The pure-function tests (normalize_email, decide_action) have no WP dependencies and match the equivalents passing in NestJS (message-id), Adonis (contact_model), .NET (ContactModelTests) PRs. See escalated-dev/escalated docs/superpowers/plans/2026-04-24-public-tickets-rollout-status.md
Follow-up on the Contact model added earlier on this branch. TicketService::create_guest now resolves/creates a Contact by email and stamps contact_id on the new ticket row. The widget REST controller and frontend AJAX handler both delegate here, so one change covers both submission paths. Inline guest_name / guest_email / guest_token columns remain populated for backwards compat. Tests: 1 new integration case in Test_Contact_Model — test_create_guest_dedupes_contacts_by_email (two submissions with casing variants yield one Contact row, both tickets carry the same contact_id) Local verification: WP test library not installed in author's env; CI on this PR will run the tests. Pure-function coverage (normalize_email + decide_action) mirrors equivalents passing in NestJS/Adonis/Go/.NET/Symfony.
Member
Author
Wire-up commit pushed
Test added: Inline |
Covers the wire-up added in the previous commit: two widget submissions with casing variants on the same email yield one Contact row, and both tickets carry the same contact_id.
…erator_spaces) Fixes CI Pint check on PR #27.
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
WordPress port of Pattern B. Reference implementations:
Rollout strategy: https://github.com/escalated-dev/escalated/blob/feat/public-ticket-system/docs/superpowers/plans/2026-04-24-public-tickets-rollout-status.md
Changes
Test plan
Backwards compatibility
Inline guest_name / guest_email / guest_token columns preserved. Follow-up PRs migrate the AJAX / inbound-email write paths.