webnet-fr/database-anonymizer
Anonymize sensitive data in Laravel databases with configurable, table-specific column replacements and Faker-powered fake values. Ideal for GDPR-friendly staging, testing, and local environments, it protects personal information while keeping your database realistic and usable.
Install into the app that owns the schema, not a throwaway project.
composer require webnet-fr/database-anonymizer --dev
Because anonymization rules are tightly coupled to your migrations/models, the config should live in the same repository as the schema so it is versioned alongside it.
Confirm what Composer actually resolved.
composer show webnet-fr/database-anonymizer
composer show doctrine/dbal
v0.0.6 advertises doctrine/dbal ^3.3. Check whether that widened the constraint or replaced a ^2.x one — if it replaced it, apps pinned to dbal 2 (older Laravel) are stuck on v0.0.5 and you need to know that before you build a workflow on top of it.
Publish and inspect the config first.
php artisan vendor:publish --provider="WebnetFr\\DatabaseAnonymizer\\...ServiceProvider"
The published config file is the real documentation — it defines the per-table/per-column rule structure. Read it before writing anything.
Confirm the command name.
php artisan list | grep -i anonym
Don't assume a command name; the docs are sparse and the release cadence is slow (v0.0.6, a dependency-only bump, is the first release since 2022). Verify the actual signature and options before scripting it.
Start with one table and one column.
Pick a low-risk table (e.g. users.name, users.email) and define a single rule. Run the command against a restored, disposable copy of a dump, then query the result to confirm values changed and row counts are unchanged. This also smoke-tests that dbal 3 column introspection works against your schema.
First real use case: restore a production dump into a local/staging database, run the anonymizer, then point your .env at it. This becomes the repeatable "sanitize the dump" step in your onboarding docs instead of a wiki page of hand-written UPDATE statements.
Rules-as-config, applied per table.
Define a map of table => [column => anonymizer]. Keep the config flat and explicit; avoid clever loops that generate rules, because reviewers need to see exactly which PII columns are covered.
Faker-backed anonymizers for realistic dev data.
Use Faker for values your UI actually exercises — names, emails, phone numbers, addresses — so views and search behave like production. For columns your app validates (e.g. email with a unique index), use Faker's unique modifier or a deterministic pattern like user{id}@example.test.
Preserve referential integrity deliberately. Foreign keys are the main thing that breaks naive scripts. Two workable patterns:
user{id}@example.test) so users.id and orders.user_email stay consistent without a join.Custom anonymizers for domain columns.
For anything Faker can't express (national ID formats, hashed tokens, internal account numbers), implement a custom anonymizer class and register it in config. This is the intended extension point — keep custom classes in app/Support/Anonymizers and unit-test their output format, since the package can't validate your domain rules.
Pipeline integration. Wrap the artisan call in a shell script or Composer script so it's one command:
mysql < dump.sql && php artisan <anonymize-command> && php artisan migrate
Wire the same script into CI for ephemeral test databases, and gate it behind an APP_ENV !== production check in your wrapper — belt and braces alongside any package-level guard.
Environment-safety pattern.
Never rely solely on the package to refuse production. Add an explicit guard in your wrapper script that aborts if DB_HOST matches the production host, and require an --force-style confirmation for anything ambiguous.
Review checklist in the PR. Treat the config file like a security artifact: when a migration adds a PII column, the same PR must add a rule. Add a test that introspects the schema for known-sensitive column names and asserts each appears in the anonymization config.
email/name output will collide on unique columns with any real row count. Use unique generators or ID-derived values.update statements in your own custom anonymizer for large tables.php artisan config:clear (or config:cache) — a stale cached config silently anonymizes the wrong columns, or none.doctrine/dbal for schema introspection, dbal 2 → 3 changed the type API substantially (type objects instead of strings, dropped Type::getType usage). If you upgrade dbal alongside this release, re-verify that JSON/enum/text columns are still detected and that your rules still fire on them — a type miss can silently skip a column.settings JSON blob needs a custom anonymizer that decodes, redacts keys, and re-encodes.audit_logs, password_reset_tokens, soft-deleted rows, related history tables — pass through untouched. Grep your schema for email, phone, name, address, token, ip and cross-check the config.How can I help you explore Laravel packages today?