Anonymizing Backups
Sometimes you need a backup that carries realistic production data — correct volumes, relationships and edge cases — but without the sensitive values. The classic use case is a development or staging database that mirrors production closely, yet contains no real emails, names or phone numbers.
The obfuscate() method on the Fluent API does exactly that: it replaces the values of specific columns with generated fake data while leaving everything else untouched.
use Aaix\LaravelEasyBackups\Facades\Backup;
use Faker\Generator as Faker;
Backup::database('mysql')
->obfuscate([
'users.email' => fn (Faker $faker, array $row) => $faker->unique()->safeEmail(),
'users.name' => fn (Faker $faker, array $row) => $faker->name(),
'users.phone' => fn (Faker $faker, array $row) => $faker->phoneNumber(),
])
->excludeTableData(['password_resets', 'sessions']) // structure only, no rows at all
->compress()
->onlyLocal()
->setName('dev-anonymized')
->run();Requires Faker
Obfuscation uses fakerphp/faker, which is not a hard dependency of this package. Install it where you run anonymized backups:
composer require fakerphp/fakerIf obfuscate() is used without Faker installed, the backup fails with a clear exception.
The mapping
The argument to obfuscate() is an array keyed by 'table.column'. Each value is a callback that receives the Faker generator and the full original row:
->obfuscate([
'users.email' => fn (Faker $faker, array $row) => $faker->unique()->safeEmail(),
// The whole row is available — derive a value from another column:
'users.full_name' => fn (Faker $faker, array $row) => $row['salutation'] . ' ' . $faker->lastName(),
])NULL values are preserved
If a cell is NULL in the source row, it stays NULL — the callback is not invoked for it. This keeps the nullability distribution of your production data intact. Columns that are not part of the mapping are copied exactly as-is, including their NULLs.
Uniqueness is your responsibility
A column with a UNIQUE constraint (like users.email) must receive unique values, otherwise the restore will fail on a duplicate key. Use Faker's unique modifier explicitly for those columns:
'users.email' => fn (Faker $faker, array $row) => $faker->unique()->safeEmail(),Don't obfuscate keys
Do not obfuscate primary keys or foreign keys. Replacing them breaks referential integrity between the dumped tables. Target descriptive columns (email, name, address, phone, …) instead.
Validation
The mapping is checked before anything runs and fails with a clear exception on a malformed key, a non-callable value, a conflict with excludeTables() / excludeTableData(), or an unknown table or column — so mistakes surface immediately instead of producing a broken dump.
Under The Hood: How Obfuscation Works
Native dump tools (mysqldump, pg_dump, …) stream raw rows straight to disk — there is no hook to transform a value mid-dump. Easy Backups therefore splits the work:
- Structure only: obfuscated tables are dumped without their data (reusing the same mechanism as
excludeTableData()). - Fetch & transform: the rows are read from the live connection in chunks; mapped columns are passed through your callback, everything else is copied verbatim.
- Append: the resulting
INSERTstatements are appended to the same dump file.
All other tables are dumped natively at full speed and contain real data. The result is a single, consistent .sql file.
Because the anonymized INSERT statements are appended at the end of the file, they are wrapped in driver-specific foreign-key toggles (e.g. SET FOREIGN_KEY_CHECKS = 0; … = 1; for MySQL) so the restore order never trips a constraint.
