File: /home/xedaptot/ai.naniguide.com/database/seeders/Demo/DemoWiper.php
<?php
namespace Database\Seeders\Demo;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
/**
* DemoWiper — completely wipes business data so DemoSeeder can rebuild from scratch.
*
* IMPORTANT: This is destructive. It removes ALL customers, lists, subscribers,
* campaigns, etc.
*
* Preservation rule: only tables LISTED in `$tables` get truncated.
* Everything else in the schema is preserved by omission. Notable preserved
* tables include (but are NOT limited to):
* - languages (system reference, LanguageSeeder is idempotent)
* - settings (system config — Stage19 selectively re-seeds)
* - migrations (laravel state)
* - plugins (plugin registry — survives reseed)
* - ai_conversations / ai_messages / ai_requests / ai_tool_calls /
* ai_feedback / ai_raw_blobs (AI observability audit tables)
*
* We DO wipe countries and admin_groups because DemoSeeder calls
* DatabaseInit which re-seeds them (and they are NOT idempotent).
*
* It also removes admin users + the wiped admins' user rows so re-seeding
* doesn't trip on unique-email constraints.
*
* Wipe order = reverse of foreign-key dependency. We disable FK checks once
* for speed and re-enable at the end.
*/
class DemoWiper
{
/** Tables to truncate, in safe order (children → parents). */
protected static array $tables = [
// tracking & log children
'ip_locations', 'open_logs', 'click_logs', 'bounce_logs', 'feedback_logs',
'unsubscribe_logs', 'reply_logs', 'tracking_logs',
// campaign + automation graph
'campaigns_sending_servers',
'campaigns_lists_segments', 'campaign_links', 'campaign_headers',
'campaign_webhooks', 'ab_test_assignments', 'ab_test_variants', 'ab_tests',
'auto_triggers', 'trigger_sessions',
'automation_emails', 'automation_elements', 'automation2s',
'emails',
'campaigns',
// funnels
'funnel_conversions', 'funnel_page_views', 'funnel_product_links',
'funnel_products', 'funnel_steps', 'funnel_domains', 'funnels',
// forms
'forms',
// subscribers + list children
'segments', 'fields', 'field_options',
'subscribers',
'mail_list_templates', 'mail_lists',
// billing (transactions table dropped — Transaction model removed)
'order_items',
'orders',
'invoices',
'payment_intents',
'billing_addresses',
// 4-concept subscription credit snapshot (FK → subscriptions; TRUNCATE
// bypasses cascade, so we must wipe explicitly BEFORE subscriptions).
'subscription_credit_state',
'subscriptions',
// sending infrastructure (per-customer copies)
// sending_identities FK-cascades from sending_servers + customers,
// but TRUNCATE bypasses FK cascade — wipe explicitly before parents.
'sending_identities',
'tracking_domains', 'verified_senders', 'signatures',
'email_verification_servers', 'sending_servers',
// misc per-customer
'assets',
'customer_email_templates', 'page_templates', 'form_templates',
// templates — wipe ALL to force TemplateSeeder re-run + prevent
// accumulation of orphan copies across reseeds (campaigns/emails
// create template copies; without wipe they pile up: 12k+ rows)
// ads module — leaf tables first, then parents (FK order)
'ad_metrics', 'ad_conversions', 'ad_products', 'ad_leads',
'ad_publish_logs', 'ad_activity_logs', 'ad_ab_variants',
'ad_ab_tests', 'ad_automation_rules', 'ad_lead_form_configs',
'ad_product_catalogs', 'ad_pixels', 'ad_audiences',
'ad_campaign_platforms', 'ad_campaigns', 'ad_accounts',
'ad_platforms',
'ad_creatives', 'ad_templates',
'templates_categories',
'system_email_templates', 'system_templates', 'templates',
'websites', 'wp_posts',
'activity_logs', 'notifications',
// admin-level catalogs
'blacklists', 'senders', 'bounce_handlers', 'feedback_loop_handlers',
'payment_methods', 'payment_gateways',
'warmup_strategies',
'plans_sending_servers',
'plan_remote_mappings',
// 4-concept plan feature tables (FK → plans with cascade; TRUNCATE
// doesn't cascade, so wipe explicitly BEFORE plans).
'plan_credits', 'plan_quotas', 'plan_entitlements', 'plan_rate_limits',
// top-level
'plans',
'admins', 'customers',
'users',
// system reference data — DatabaseInit will re-seed these
'currencies', 'countries', 'admin_groups',
];
public static function wipe(): void
{
echo "[demo:wipe] starting full wipe...\n";
DB::statement('SET FOREIGN_KEY_CHECKS=0');
try {
foreach (self::$tables as $table) {
if (Schema::hasTable($table)) {
DB::table($table)->truncate();
echo "[demo:wipe] truncated {$table}\n";
}
}
// Drop dynamic custom-field columns from `subscribers` (they accumulate
// across reseeds and would confuse field tag uniqueness).
self::dropDynamicSubscriberColumns();
} finally {
DB::statement('SET FOREIGN_KEY_CHECKS=1');
}
// Clean up template thumbnail files from storage — they accumulate
// across reseeds (one per Template::copy()) and are NOT on the repo.
// TemplateSeeder will regenerate canonical thumbs from theme source SVGs.
self::cleanTemplateThumbStorage();
echo "[demo:wipe] done\n";
}
/**
* Remove template + ad template thumbnail files from storage.
* Without cleanup these accumulate across reseeds.
*/
protected static function cleanTemplateThumbStorage(): void
{
$dirs = [
storage_path('app/public/templates/thumb'),
storage_path('app/public/ad-templates/thumb'),
];
foreach ($dirs as $thumbDir) {
if (!is_dir($thumbDir)) continue;
$files = glob($thumbDir . '/*');
$count = 0;
foreach ($files as $file) {
if (is_file($file)) {
unlink($file);
$count++;
}
}
if ($count > 0) {
$label = basename(dirname($thumbDir));
echo "[demo:wipe] removed {$count} {$label} thumbnail files\n";
}
}
}
/**
* The Subscriber model dynamically appends columns named field_NNN.
* Drop them so a fresh seed starts with a clean slate.
*/
protected static function dropDynamicSubscriberColumns(): void
{
if (!Schema::hasTable('subscribers')) return;
$cols = Schema::getColumnListing('subscribers');
$dynamic = array_filter($cols, fn($c) => preg_match('/^field_\d+$/', $c));
if (empty($dynamic)) return;
Schema::table('subscribers', function ($table) use ($dynamic) {
foreach ($dynamic as $col) {
$table->dropColumn($col);
}
});
echo "[demo:wipe] dropped " . count($dynamic) . " dynamic field_* columns\n";
}
}