directorytree/anonymize
Anonymize swaps sensitive Eloquent model attributes with realistic Faker data. Deterministic per model ID, cached for performance, and easy to toggle globally or per instance—ideal for dev, demos, and safely sharing production-like datasets.
Installation:
composer require directorytree/anonymize
Ensure your project meets the requirements (PHP 8.2+, Laravel 11+).
Enable Anonymization:
Add this to a service provider (e.g., AppServiceProvider):
use DirectoryTree\Anonymize\Facades\Anonymize;
public function boot(): void
{
if (session('anonymize')) {
Anonymize::enable();
}
}
First Use Case:
Anonymize a User model for a demo environment:
// app/Models/User.php
use DirectoryTree\Anonymize\{Anonymizable, Anonymized};
class User extends Model implements Anonymizable
{
use Anonymized;
public function getAnonymizedAttributes(Generator $faker): array
{
return [
'name' => $faker->name(),
'email' => $faker->safeEmail(),
];
}
}
Now, when Anonymize::enable() is called, User instances will return fake data for name and email.
Model Integration:
Anonymizable interface and use Anonymized trait.getAnonymizedAttributes() with Faker-generated values.public function getAnonymizedAttributes(Generator $faker): array
{
return [
'credit_card' => $faker->creditCardNumber(),
'ssn' => $faker->nineDigitNumber(),
];
}
Conditional Anonymization:
public function getAnonymizedAttributes(Generator $faker): array
{
$attributes = ['email' => $faker->safeEmail()];
if (app()->environment('local')) {
$attributes['password'] = '********';
}
return $attributes;
}
Resource Anonymization:
use DirectoryTree\Anonymize\AnonymizedResource;
class UserResource extends JsonResource implements Anonymizable
{
use AnonymizedResource;
public function toArray(Request $request): array
{
return $this->toAnonymized([
'id', 'name', 'email', // Only these fields are exposed
]);
}
public function getAnonymizedAttributes(Generator $faker): array
{
return ['name' => $faker->name()];
}
}
Global Control:
// Middleware: Enable for demo routes
public function handle(Request $request, Closure $next)
{
Anonymize::enable();
return $next($request);
}
Consistency:
getAnonymizableSeed() for deterministic fake data:
public function getAnonymizableSeed(): string
{
return "user:{$this->id}:{$this->email}";
}
Anonymize::enable() in test environments to avoid exposing real data:
public function test_user_anonymization()
{
Anonymize::enable();
$user = User::find(1);
$this->assertNotEquals($user->email, 'real@example.com');
}
Anonymize::clearCache();
anonymize.enabled/anonymize.disabled events to trigger side effects (e.g., logging, notifications).Static State:
Anonymize facade maintains static state. Ensure it’s reset between tests or requests if needed:
Anonymize::disable(); // Reset after tests
Anonymize::flush() to clear cached data.Nested Resources:
JsonResource anonymization may require explicit configuration. Ensure parent resources delegate to child resources:
// ParentResource.php
public function toArray(Request $request): array
{
return $this->toAnonymized([
'id',
'children' => $this->whenLoaded('children', function () {
return $this->children->map(fn ($child) => new ChildResource($child));
}),
]);
}
Seed Collisions:
user:{id}:{email}) for reliability.Performance:
phpbench and optimize seeds or caching.Faker Locale:
en_US vs. ja_JP). Set it globally in config/app.php or per instance:
$faker = Faker\Factory::create('ja_JP');
$user = User::find(1);
dd($user->isAnonymized()); // true/false
public function getAnonymizedAttributes(Generator $faker): array
{
logger()->debug("Seed: {$this->getAnonymizableSeed()}");
return [...];
}
php artisan anonymize:clear-cache
Custom Faker Providers:
Extend Faker with domain-specific providers (e.g., fakeCompanyName()):
$faker->addProvider(new CustomFakerProvider($faker));
Inject the extended Faker into getAnonymizedAttributes().
Dynamic Attribute Mapping: Use closures for dynamic anonymization logic:
public function getAnonymizedAttributes(Generator $faker): array
{
return collect($this->getDirty())
->filter(fn ($value, $key) => str_contains($key, 'sensitive_'))
->mapWithKeys(fn ($value, $key) => [
$key => $faker->{$this->getFakeMethod($key)}()
])->toArray();
}
Anonymization Policies: Create a policy-based system to centralize rules:
class UserAnonymizationPolicy
{
public function shouldAnonymize(User $user, string $attribute): bool
{
return $user->isPublic() && in_array($attribute, ['email', 'phone']);
}
}
Integrate via getAnonymizedAttributes():
public function getAnonymizedAttributes(Generator $faker): array
{
$policy = app(UserAnonymizationPolicy::class);
return collect($this->attributes)
->filter(fn ($value, $key) => $policy->shouldAnonymize($this, $key))
->mapWithKeys(fn ($value, $key) => [$key => $faker->safeEmail()])
->toArray();
}
Event-Driven Anonymization:
Trigger anonymization on model events (e.g., retrieved):
protected static function booted()
{
static::retrieved(function ($model) {
if (Anonymize::isEnabled()) {
$model->anonymize();
}
});
}
anonymize service container key. Override bindings if extending:
$this->app->bind('anonymize', function () {
return new CustomAnonymizeManager();
});
local, staging):
if (in_array(app()->environment(), ['local', 'staging'])) {
Anonymize::enable();
}
How can I help you explore Laravel packages today?