Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Anonymizer Laravel Package

php-shark-tank/anonymizer

Anonymizer for Laravel Eloquent models: define per-model rules to replace sensitive personal data with safe values, helping with GDPR compliance and anonymized test or export data.

View on GitHub
Deep Wiki
Context7

Getting Started

This is the initial public release (1.0.2), so treat it as a greenfield adoption — there is no migration path to worry about, but also no battle-tested conventions beyond what ships here.

Install and wire up

composer require <vendor>/<package>   # 1.0.2

The service provider is auto-discovered. Publish the config early — this is where the defaults you'll fight with later live:

php artisan vendor:publish --tag=<package>-config

Check three keys first: the default handler, the exclusion strategy list, and the event-dispatching flag.

First use case

Start with the smallest possible object — a DTO or a model you already have a factory for — and let the default handler (Faker) populate it:

$populator = app(Populator::class);

$user = $populator->populate(UserData::class);

Then tighten it property by property. The four shipped handlers map cleanly onto the usual test/seed needs:

  • Faker — default for scalars (name, email, address)
  • Null — explicit "leave this empty" for nullable columns and optional relations
  • Hash — passwords and tokens, so you never seed plaintext credentials
  • Callback — anything derived (fn ($ctx) => $ctx->email for username, computed totals, correlated values)

Where to look next, in order: the config file (defaults + exclusion list), the handler interface (before you write a custom one), and the lifecycle callback registration point (that's where persistence hooks go).

Implementation Patterns

Per-property handler mapping. The core workflow is a definition that assigns a handler to each property and falls back to the configured default for everything else. Keep definitions declarative and boring; put branching logic in Callback handlers, not in if statements around the definition.

public function definition(): array
{
    return [
        'name'     => Handler::faker()->name(),
        'email'    => Handler::faker()->safeEmail(),
        'password' => Handler::hash(),
        'nickname' => Handler::null(),
        'slug'     => Handler::callback(fn ($ctx) => Str::slug($ctx->name)),
    ];
}

Exclusion strategies are the recursion guard. With object exclusions and property exclusions both available, the standard pattern is:

  • Exclude by object type for values that are already "final" and must not be traversed — Carbon, UploadedFile, enums, backed value objects.
  • Exclude by property name for identity and audit columns — id, created_at, updated_at, deleted_at, and anything you want the database to own.

Do this once in config rather than in every definition; it's the difference between a populator that works on nested graphs and one that stack-overflows the first time a model has a belongsTo back to itself.

Lifecycle callbacks for the boring glue. Use pre-populate callbacks for normalization (trim, lowercase emails) and post-populate callbacks for persistence or wrapping. The common Laravel shape is: populate → post-callback wraps in DB::transaction() → save. Don't put persistence in the definition; definitions should stay pure data.

Event dispatching is opt-in — decide once. Because dispatching is optional, listeners you register won't fire unless it's enabled. Two viable stances:

  • Tests: leave it off. Faster, no accidental observers writing to the DB, no queued listeners firing against a RefreshDatabase transaction.
  • Seeders / production-adjacent tooling: turn it on so audit listeners, search indexing, and domain observers behave exactly as they would for real writes.

Whichever you pick, set it in config per environment (testing off, local/staging on) rather than toggling ad hoc in code.

Integration points. The natural fits are test data builders inside feature tests, seeders that need coherent relational data, and factories where you want generated values without writing a fake() call per attribute. If you already use Laravel factories, use this package to generate the attributes and let factories handle persistence — mixing both persistence layers in one flow is where duplicate inserts come from.

Gotchas and Tips

Handler precedence is the first thing to verify. Explicit per-property handlers must beat the default; if you see Faker output where you declared Null, your property key and the definition's key disagree (nested arrays vs. dot notation is the usual culprit).

Exclusion by property name is string-matching. It won't respect class hierarchies and it won't know that user_id and user are related. Keep a flat, audited list in config, and prefer object-type exclusions wherever you can — they survive renames.

Faker state is not per-call. unique() and locale are process-wide; a unique modifier used inside a Callback can exhaust its pool across a long seeding run and throw mid-run. Reset state between batches, or scope uniqueness with an explicit large pool.

Hash handler cost. Hashing every one of 50,000 seeded rows with a real bcrypt cost is minutes of CPU. For bulk seeding, either drop the hash cost in the config or swap in a null/fixed-value handler for the bulk path and hashing only for the handful of users that need to log in.

Don't dispatch events inside an open transaction you might roll back. Queued listeners see the write as committed when it isn't. If you enable dispatching in a seeding command, either dispatch after commit or disable it and dispatch deliberately at the end.

Callbacks run per property, per object. Anything doing a query or an HTTP call inside a Callback multiplies fast and produces textbook N+1. Pre-fetch lookups into the populator context once and have callbacks read from it.

Debugging a wrong value. The fastest diagnostic is a temporary log at the handler-resolution point printing property name → resolved handler class. Nine times out of ten the value came from the default handler, not the one you thought you registered.

Extension points worth knowing on day one: a custom handler implementing the handler interface (for Ulid, Uuid, money objects), and a custom exclusion strategy if your domain has a base class whose descendants should never be traversed. Both are registration-time config, not per-definition — register them once and every definition inherits the behavior.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log