Wedissimo Data Migration Guide
This guide outlines the complete process for migrating data from WordPress/HivePress to Laravel.
Prerequisites
- Ensure the WordPress legacy database is accessible via the
mysql_legacyconnection (import the db file to a local mysql instance, or connect to the Cloud SQL instance via the cloud-sql-proxy) - Verify all Laravel migrations are up to date
- Confirm the database is running and accessible
- Configure the required environment variables (see below)
Required Environment Variables
Configure the following variables in your .env file before running migrations:
Primary Database (PostgreSQL)
DB_CONNECTION=pgsql
DB_HOST=wedissimo-pg
DB_PORT=5432
DB_DATABASE=wedissimo
DB_USERNAME=wedissimo
DB_PASSWORD=your_postgres_passwordLegacy Database (MySQL - WordPress)
Option 1: Local MySQL Instance
MYSQL_LEGACY_SOURCE=local
MYSQL_LEGACY_HOST=wedissimo-mysql
MYSQL_LEGACY_PORT=3306
MYSQL_LEGACY_DATABASE=wedissimo_legacy
MYSQL_LEGACY_USERNAME=root
MYSQL_LEGACY_PASSWORD=your_mysql_passwordOption 2: Cloud SQL Proxy
MYSQL_LEGACY_SOURCE=cloud-sql-proxy
MYSQL_LEGACY_HOST=127.0.0.1
MYSQL_LEGACY_PORT=3307
MYSQL_LEGACY_DATABASE=wedissimo_wordpress
MYSQL_LEGACY_USERNAME=your_cloud_sql_userEnvironment Configuration
# Set to 'production' to preserve real emails, otherwise emails will be obfuscated
APP_ENV=local
# Application key (required)
APP_KEY=base64:your_app_key_here
# Set to false during migration for better performance
APP_DEBUG=trueEmail Obfuscation Behavior
The APP_ENV setting controls email obfuscation during migration:
Non-Production (
local,staging,development):- Emails from
@wedissimo.com,@thelightninggroup.co.uk, and@founderandlightning.comare preserved - All other emails are converted to:
wedissimo_test+<hash>@thelightninggroup.co.uk
- Emails from
Production (
production):- All emails are preserved as-is from WordPress
Optional: Cloud SQL Proxy Setup
If using Cloud SQL Proxy, ensure it's running before migrations:
# Start cloud-sql-proxy (adjust instance connection string)
docker-compose up -d wedissimo-cloud-sql-proxy
# Or manually:
./cloud-sql-proxy your-project:region:instance-name --port=3307Verification
Verify your database connections are working:
# Test primary database
docker exec -it wedissimo-api php artisan db:show
# Test legacy database connection
docker exec -it wedissimo-api php artisan tinker
>>> DB::connection('mysql_legacy')->select('SELECT COUNT(*) as count FROM wp_users');Migration Order
The migrations must be run in the following order to ensure data integrity and maintain relationships:
TLDR: Categories/Tags → Users → Vendors → Listings → Listing Taxonomies → Attributes → Video Examples → Media → Orders → Favourites
Alternatively, run all migrations at once:
docker exec -it wedissimo-api php artisan wedissimo:migrate-all1. Users Migration
Migrates all WordPress users to Laravel, including profile data, OAuth connections, and role assignments.
# Test mode (10 users only)
docker exec -it wedissimo-api php artisan wedissimo:migrate-users --test
# Full migration
docker exec -it wedissimo-api php artisan wedissimo:migrate-users
# Skip marketing consents
docker exec -it wedissimo-api php artisan wedissimo:migrate-users --skip-consentsWhat gets migrated:
- User accounts (first_name, last_name, email, password)
- Profile data (phone, bio, avatar_path, verified status)
- Social OAuth (Google, Facebook)
- Role mapping (administrator → wedissimo_admin, contributor → vendor, default → couple)
- Marketing consents (from
hp_enable_newsletter)
Role Mapping:
- WordPress
administratororwpseo_editor→wedissimo_admin - WordPress
editor→wedissimo_user - WordPress
contributororpre_contributor→vendor - All other WordPress roles →
couple
Admin Access Control:
- Only
super_admin,wedissimo_admin, andwedissimo_usercan access/admin/login coupleandvendorusers are redirected with message: "Couples and vendors must login via the mobile app"- API authentication should be used for couple and vendor access
Email Obfuscation:
Email obfuscation is controlled by the APP_ENV environment variable. See Email Obfuscation Behavior in the Required Environment Variables section for details.
2. Vendors Migration
Migrates vendor profiles including business information and taxonomies (media migrated separately).
# Test mode (10 vendors only)
docker exec -it wedissimo-api php artisan wedissimo:migrate-vendors --test
# Full migration
docker exec -it wedissimo-api php artisan wedissimo:migrate-vendorsWhat gets migrated:
- Vendor profiles (name, slug, description)
- Business information (business_name, company_registration, VAT)
- Location data (address, city, country, lat/long)
- Contact info (phone, website, social media)
- Onboarding progress and approval status
- Financial data (Stripe account ID, stripe_onboarded status)
- Ratings and review counts
- Booking defaults (calendar sync, lead times, approval requirements)
- Profile questions and service attributes
- External review widgets
- Taxonomies:
- Categories (
hp_vendor_category) - Tags (region, business type, languages, etc.)
- Categories (
Data Quality Tracking:
- Duplicate slugs are automatically resolved by appending WordPress ID
- All data quality issues are tracked and reported after migration
- Issues include: duplicate slugs, missing fields, invalid data
Performance Optimizations:
- Slug caching reduces database queries by 98%
- Batch processing with progress bars
- Memory-efficient query building
3. Listings Migration
Migrates service packages/listings with comprehensive package inclusions for all service types. Note: Media and taxonomies are migrated separately.
# Test mode (10 listings only)
docker exec -it wedissimo-api php artisan wedissimo:migrate-listings --test
# Full migration
docker exec -it wedissimo-api php artisan wedissimo:migrate-listingsWhat gets migrated:
- Listing/package details (title, slug, description)
- Pricing (price, deposit, currency)
- Location and coverage (address, city, lat/long, coverage_bounds)
- Availability and booking settings
- Service-specific package inclusions:
- Photography (11 fields): preparations, ceremony, group shots, speeches, first dance, etc.
- Videography (6 fields): hours of coverage, edited video, highlights film, drone footage, etc.
- HMUA (5 fields): bridal trial, party makeup, airbrush, false lashes, travel
- DJ (8 fields): setup time, performance hours, equipment, MC services, lighting, etc.
- Photo Booth (7 fields): hours, prints, props, backdrop, guestbook, social sharing, attendant
- Wedding Car (6 fields): ribbons, champagne, red carpet, chauffeur, fuel, duration
- Celebrant (6 fields): ceremony planning, rehearsal, vows, readings, music, license
- Featured status and verification
Package Inclusions: The migration intelligently maps WordPress meta fields to structured JSON for each service type, preserving all package details.
Performance Optimizations:
- Taxonomy relationships are migrated separately for better performance
- Media migration is handled by a separate command
- Reduced memory footprint by focusing on core listing data only
4. Categories and Tags Migration
IMPORTANT: This must be run BEFORE the listing taxonomies migration, as it creates the category and tag records that will be referenced.
Migrates all category and tag taxonomy records from WordPress to Laravel.
# Full migration
docker exec -it wedissimo-api php artisan wedissimo:migrate-taxonomy-and-tagsWhat gets migrated:
- Categories from
hp_listing_categorytaxonomy- Photography, Videography, HMUA, DJ, Photo Booth, Wedding Car, Celebrant, etc.
- Hierarchical relationships (parent/child categories)
- Tags from multiple taxonomies:
- Region (
hp_listing_region) - Package type (
hp_listing_package) - Price tier (
hp_listing_price_tier) - General tags (
hp_listing_tags) - Service-specific inclusions (photo booth, HMUA, DJ, celebrant)
- Attributes (languages, max passengers, video types, group shots, pets, etc.)
- Region (
Data Quality Handling:
- Duplicate slugs are automatically resolved by appending WordPress ID (e.g.,
group_shots-57) - All taxonomy records are upserted, making it safe to re-run
Performance:
- Single query to load all categories
- Single query to load all tags across taxonomies
- Batch upsert of all records
5. Listing Taxonomies Migration
IMPORTANT: This must be run AFTER both listings and categories/tags have been migrated, as it syncs the relationships between them.
Migrates all category and tag relationships for listings with pre-loaded caching for optimal performance.
# Test mode (10 listings only)
docker exec -it wedissimo-api php artisan wedissimo:migrate-listing-taxonomies --test
# Full migration
docker exec -it wedissimo-api php artisan wedissimo:migrate-listing-taxonomiesWhat gets migrated:
- Category relationships linking listings to categories (
hp_listing_category) - Tag relationships linking listings to tags from multiple taxonomies:
- Region, package type, price tier, general tags
- Service-specific tags (photo booth, HMUA, DJ, celebrant inclusions)
- Attributes (languages, max passengers, video types, etc.)
Performance Optimizations:
- Single query to WordPress to load ALL taxonomy relationships
- Cached category and tag ID mappings
- Batch processing with chunking (100 listings per chunk)
- Detailed statistics and logging
6. Favourites Migration
IMPORTANT: This must be run AFTER both users and listings have been migrated, as favourites link users to listings.
Migrates user favourites/wishlists from WordPress to Laravel.
# Test mode (10 favourites only)
docker exec -it wedissimo-api php artisan wedissimo:migrate-favourites --test
# Full migration
docker exec -it wedissimo-api php artisan wedissimo:migrate-favourites
# Skip verification (for faster runs)
docker exec -it wedissimo-api php artisan wedissimo:migrate-favourites --skip-verificationWhat gets migrated:
- User favourites from WordPress
wp_commentswherecomment_type = 'hp_favourite' - Relationships between users and their favourited listings
- Original creation timestamps from WordPress
Data Quality Handling:
- Favourites with missing users are skipped and logged as warnings
- Favourites with missing listings are skipped and logged as warnings
- Statistics show migrated vs skipped counts
Verification:
- Random sample of 20 favourites is checked (unless
--skip-verificationis used) - Compares WordPress vs Laravel counts
- Shows users with favourites and listings with favourites statistics
7. Media Migration (Galleries & Images)
IMPORTANT: This must be run AFTER vendors and listings have been migrated, as media is attached to those entities.
Migrates all media galleries for vendors and listings with many-to-many relationships for media reusability.
# Test mode (10 vendors + 10 listings only)
docker exec -it wedissimo-api php artisan wedissimo:migrate-media --test
# Full migration
docker exec -it wedissimo-api php artisan wedissimo:migrate-mediaWhat gets migrated:
Vendor Media:
- Main photo gallery (
hp_photo_gallery) - Additional galleries 1-4 (
hp_additional_gallery_1throughhp_additional_gallery_4) - Alternative images field (
hp_images)
Listing Media:
- Primary listing gallery (
hp_images) - Alternative gallery field (
hp_photo_gallery)
Media System Architecture:
- Ownership: Media is owned by vendors (
vendor_idin media table) - Attachments: Media can be attached to multiple entities via
mediablespivot table - Collections: Organized by collection name (gallery, gallery_1, gallery_2, logo, etc.)
- Ordering: Each attachment has an
orderfield for sorting - Reusability: A single media record can be attached to vendor galleries AND multiple listings
Example: A vendor uploads 500 images during onboarding. These images are stored once in the media table with the vendor's ID. The same images can then be attached to:
- Vendor profile galleries (collections:
gallery,gallery_1,gallery_2, etc.) - Multiple listings (collection:
gallery) - Vendor logo (collection:
logo)
8. Attributes Migration
Migrates attribute definitions, options, and values from WordPress to Laravel for both vendors and listings.
# Test mode
docker exec -it wedissimo-api php artisan wedissimo:migrate-attributes --test
# Full migration
docker exec -it wedissimo-api php artisan wedissimo:migrate-attributes
# Skip verification step
docker exec -it wedissimo-api php artisan wedissimo:migrate-attributes --skip-verificationWhat gets migrated:
- Attribute definitions (name, slug, type, target_type)
- Attribute options for select/multi-select attributes
- Vendor attribute values (linked to vendors via
vendor_attribute_values) - Listing attribute values (linked to listings via
listing_attribute_values)
9. Video Examples Migration
Migrates video example URLs from WordPress postmeta to listing attribute values.
# Full migration
docker exec -it wedissimo-api php artisan wedissimo:migrate-video-examplesWhat gets migrated:
- Video example URLs from
video_example_1throughvideo_example_6postmeta fields - Linked to the corresponding
video_example_*attributes in Laravel
10. Orders Migration
Migrates WordPress/WooCommerce orders to Laravel.
# Test mode
docker exec -it wedissimo-api php artisan wedissimo:migrate-orders --test
# Full migration
docker exec -it wedissimo-api php artisan wedissimo:migrate-ordersWhat gets migrated:
- Order records with status, totals, and timestamps
- Customer and vendor relationships
- Order items and line items
11. Additional Migrations
These are handled automatically during the main migrations but can be verified separately.
Marketing Consents Migration
Migrated during users migration (unless --skip-consents flag is used).
WordPress Source: wp_usermeta where meta_key = 'hp_enable_newsletter' and meta_value = '1'
Database Schema
Key Tables Created
users- User accounts with WordPress ID mappingvendors- Vendor profiles with approval workflowvendor_approval_history- Audit trail for vendor approvalslistings- Service packages/listingscategories- Service categories (polymorphic)tags- Tags/attributes (polymorphic)categorizables- Pivot table for categoriestaggables- Pivot table for tagsmedia- Centralized media storage (owned by vendors)mediables- Pivot table for many-to-many media relationshipsfavourites- User favouritesuser_consents- GDPR consent tracking
Important Relationships
- User → Vendor (one-to-one via
user_id) - User → Favourites (one-to-many via
user_id) - Vendor → Listings (one-to-many via
vendor_id) - Vendor → Media (one-to-many ownership via
vendor_id) - Listing → Favourites (one-to-many via
listing_id) - Media → Vendors/Listings (many-to-many via
mediables) - Vendor/Listing → Categories (many-to-many via
categorizables) - Vendor/Listing → Tags (many-to-many via
taggables)
Enhanced Media System
The media system uses a many-to-many polymorphic relationship to allow media reusability. See Media Migration section above for full details on the architecture and migration process.
Migration Verification
After running migrations, use the Migration Dashboard to verify data integrity:
# Access via admin panel
/admin/migration-dashboardDashboard shows:
- WordPress vs Laravel counts for all entity types
- Role distribution (dynamic, excludes super_admin)
- Field coverage percentages
- Data integrity checks (users without roles, duplicate WP IDs)
- Migration health status (healthy/warning/critical)
Health Indicators:
- 🟢 Healthy: All counts match, no integrity issues
- 🟡 Warning: Counts match OR no integrity issues (but not both)
- 🔴 Critical: Count mismatches AND integrity issues detected
Permissions
The migration dashboard requires the view migration dashboard permission, which is automatically granted to the wedissimo_admin role.
To grant access to other roles:
# Run the permission seeder
docker exec -it wedissimo-api php artisan db:seed --class=PermissionSeeder
# Or grant manually
docker exec -it wedissimo-api php artisan tinker
>>> $role = \Spatie\Permission\Models\Role::findByName('wedissimo_admin');
>>> $role->givePermissionTo('view migration dashboard');Troubleshooting
Database Connection Timeout
SQLSTATE[08006] connection to server at "X.X.X.X", port 5432 failed: timeout expiredSolution: Ensure your database container/service is running:
docker compose up -d
# or
sail up -dMissing WordPress Database
SQLSTATE[HY000] [1049] Unknown database 'wedissimo_legacy'Solution: Verify your legacy database environment variables are correctly configured (see Required Environment Variables) and ensure the WordPress database is accessible:
# Test legacy database connection
docker exec -it wedissimo-api php artisan tinker
>>> DB::connection('mysql_legacy')->select('SELECT COUNT(*) as count FROM wp_users');If using Cloud SQL Proxy, ensure it's running:
docker-compose up -d wedissimo-cloud-sql-proxyMemory Issues with Large Datasets
If migrations timeout or run out of memory, they will automatically use ->limit() on queries when in test mode. For production migrations, ensure adequate PHP memory:
memory_limit = 512M
max_execution_time = 300Duplicate Key Violations
If re-running migrations after partial completion, use updateOrCreate() which is already implemented in all migration commands to safely handle duplicates.
Missing Roles
If you see "Role not found" errors:
docker exec -it wedissimo-api php artisan db:seed --class=RoleSeeder
docker exec -it wedissimo-api php artisan db:seed --class=PermissionSeederFull Migration Sequence
Before starting, ensure you have configured all Required Environment Variables.
Here's the complete sequence to migrate from a fresh database:
# 0. Verify environment variables are configured
# Check your .env file has all required database credentials and APP_ENV is set correctly
# 1. Fresh database with all migrations
docker exec -it wedissimo-api php artisan migrate:fresh --force OR
docker exec -it wedissimo-api php artisan migrate:fresh --seed ( you can ignore #2)
# 2. Seed roles and permissions
docker exec -it wedissimo-api php artisan db:seed --class=RoleSeeder
docker exec -it wedissimo-api php artisan db:seed --class=PermissionSeeder
# 3. Migrate categories and tags (MUST run before vendors and listings)
docker exec -it wedissimo-api php artisan wedissimo:migrate-categories
docker exec -it wedissimo-api php artisan wedissimo:migrate-taxonomy-and-tags
# 4. Migrate users (includes consents)
docker exec -it wedissimo-api php artisan wedissimo:migrate-users
# 5. Migrate vendors (includes taxonomies)
docker exec -it wedissimo-api php artisan wedissimo:migrate-vendors
# 6. Migrate listings (core data only)
docker exec -it wedissimo-api php artisan wedissimo:migrate-listings
# 7. Migrate listing taxonomies (syncs relationships)
docker exec -it wedissimo-api php artisan wedissimo:migrate-listing-taxonomies
# 8. Migrate attributes (definitions, options, and values)
docker exec -it wedissimo-api php artisan wedissimo:migrate-attributes
# 9. Migrate video examples
docker exec -it wedissimo-api php artisan wedissimo:migrate-video-examples
# 10. Migrate media (MUST run after vendors and listings)
docker exec -it wedissimo-api php artisan wedissimo:migrate-media
# 11. Migrate orders
docker exec -it wedissimo-api php artisan wedissimo:migrate-orders
# 12. Migrate favourites (MUST run after users and listings)
docker exec -it wedissimo-api php artisan wedissimo:migrate-favourites
# 13. Verify via dashboard
# Navigate to: /admin/migration-dashboardOr run all migrations at once:
docker exec -it wedissimo-api php artisan wedissimo:migrate-allTest Mode Sequence
Before starting, ensure you have configured all Required Environment Variables.
For testing with limited data:
# 0. Verify environment variables are configured
# Check your .env file has all required database credentials and APP_ENV is set correctly
# 1. Fresh database
docker exec -it wedissimo-api php artisan migrate:fresh --force
# 2. Seed roles and permissions
docker exec -it wedissimo-api php artisan db:seed --class=RoleSeeder
docker exec -it wedissimo-api php artisan db:seed --class=PermissionSeeder
# 3. Test migrations (10 records each, except categories/tags which is full)
docker exec -it wedissimo-api php artisan wedissimo:migrate-categories
docker exec -it wedissimo-api php artisan wedissimo:migrate-taxonomy-and-tags
docker exec -it wedissimo-api php artisan wedissimo:migrate-users --test
docker exec -it wedissimo-api php artisan wedissimo:migrate-vendors --test
docker exec -it wedissimo-api php artisan wedissimo:migrate-listings --test
docker exec -it wedissimo-api php artisan wedissimo:migrate-listing-taxonomies --test
docker exec -it wedissimo-api php artisan wedissimo:migrate-attributes --test
docker exec -it wedissimo-api php artisan wedissimo:migrate-video-examples
docker exec -it wedissimo-api php artisan wedissimo:migrate-media --test
docker exec -it wedissimo-api php artisan wedissimo:migrate-orders --test
docker exec -it wedissimo-api php artisan wedissimo:migrate-favourites --test
# 4. Check dashboard for verificationNotes
- All migrations use
updateOrCreate()to be idempotent - safe to re-run - WordPress IDs are preserved in
wp_idfields for reference - Timestamps (
created_at,updated_at) are preserved from WordPress - All UUIDs are auto-generated for new Laravel records
- Activity logging is disabled during migration for performance
- Email obfuscation behavior is controlled by
APP_ENV(see Required Environment Variables) - Progress bars show real-time migration progress
- Statistics are displayed after each migration completes
- Migrations are optimized and separated by concern:
- Core data migrations (users, vendors, listings)
- Taxonomy relationships (categories and tags for listings)
- User relationships (favourites/wishlists)
- Media/gallery attachments
- Attribute definitions and values
- Video example links
- Order data
- Slug caching in vendor migration reduces database queries by 98%
- Taxonomy pre-loading prevents N+1 queries to WordPress
- Data quality issues (duplicate slugs, etc.) are tracked and reported automatically