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

Laravel Gdpr Laravel Package

soved/laravel-gdpr

A GDPR compliance toolkit for Laravel: let users download or delete their personal data, anonymize and encrypt stored personal fields, manage consent, and log data access—so your app meets EU privacy rules without rebuilding core flows.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a single-install compliance toolkit — install it, publish its config and migrations, then opt in models one at a time. The first 10 minutes should get you to a working consent log and a working data export.

composer require soved/laravel-gdpr
php artisan vendor:publish --provider="Soved\Laravel\Gdpr\GdprServiceProvider"
php artisan migrate

As of v1.10.0 the package supports Laravel 13.x (added via the Laravel Shift compatibility PR). If you're on Laravel 13, require ^1.10 explicitly — earlier tags (v1.9.x and below) will fail composer update on framework constraint conflicts. On Laravel 11/12, ^1.9 still works but there's no reason to stay behind.

The publish step drops three things you should look at in this order:

  1. config/gdpr.php — the single source of truth for what gets anonymized and what gets exported. Read every key before you touch anything else; almost every gotcha in this package comes back to this file.
  2. Migrations — adds gdpr_consents, gdpr_anonymizations, and gdpr_exports, plus a gdpr_rating (or similar) column on users. The rating column is your knobs for "how identifiable is this user, 1–5"; the package uses it to decide when to anonymize (>= 3 by default, configurable).
  3. Routes / controllers — the package ships endpoints for consent management and data-export download. They're opt-in via the config; enable them only if you actually want them public.

The first use case to prove end-to-end is consent logging:

// In your cookie banner handler (e.g. a POST endpoint)
Gdpr::consent($user, 'marketing');       // or $user->giveConsent('marketing')
Gdpr::withdrawConsent($user, 'marketing');
Gdpr::hasConsent($user, 'marketing');    // bool

If that round-trips to the gdpr_consents table with the right purpose and consentable_type, the rest of the package will work. Don't move on to anonymization until it does.

Once consent works, add the HasGdpr (or equivalent) trait to App\User — most of the facade convenience hangs off it.

Implementation Patterns

Consent is the workhorse. Every project that adopts this package ends up with two consent surfaces: a legal one (privacy policy, terms) and a processing one (marketing, analytics, third parties). Model purposes explicitly, don't conflate them — the whole point of a consent table is per-purpose granularity. Typical pattern:

// On registration
Gdpr::consent($user, 'privacy-policy');

// In an API endpoint hit by the banner
Gdpr::consent($user, request('purpose'));   // validate purpose against an allowlist

Store the text version alongside the consent (or the policy URL hash) — auditors will ask. If the package doesn't give you that column out-of-the-box, add a nullable version to the consent migration; it won't break anything.

Anonymization is a workflow, not a call. The package separates "requested" from "done" via the gdpr_anonymizations table plus a scheduled command. The pattern the package intends:

  1. User requests erasure → create an anonymization record (facade call or a route).
  2. A scheduler runs php artisan gdpr:anonymize periodically and processes pending records.
  3. For each anonymization, the package walks the registered models/fields in config/gdpr.php and nulls/overwrites them according to rating.

You wire this into the scheduler. On Laravel 11+ that's routes/console.php (or bootstrap/app.php with ->withSchedule()); on older skeletons it's app/Console/Kernel.php:

$schedule->command('gdpr:anonymize')->hourly();

For synchronous tests or smaller apps you can call the anonymization directly — but do the async path in production, or a slow export will block your web request.

Data export is a job, not a controller response. Even the "instant" path streams a file. Two patterns in the wild:

  • On-demand: user hits a "download my data" button → export row created → job or command generates file → emailed/served via signed URL.
  • Scheduled: nightly export of every user with a gdpr_rating above threshold, archived to S3 for the retention window.

Use the second if you have regulators breathing down your neck; use the first if you're a small team. Don't do both — you'll double your storage and confuse retention policy.

The compliance checklist is your onboarding doc. php artisan gdpr:checklist prints the package author's opinion of what "GDPR compliant" means. Treat it as a starting checklist, not a certification. Run it before and after wiring things up; gaps are visible immediately.

Ratings drive everything. Think of gdpr_rating as a sensitivity tag:

Rating Meaning Typical fields
1 Public display name
2 Low city, job title
3 Medium email, phone
4 High address, IP
5 Critical national ID, payment data

Anonymization replaces fields with null or a placeholder based on which rating level you tell it to strip to. Decide your strip level with legal, not by yourself.

Integrate with soft deletes carefully. If your users table uses SoftDeletes, an anonymized user still "exists" but with null fields. That's usually what you want (referential integrity preserved, personal data gone) — but it means deleted_at no longer means what your app thinks it means. Consider a separate anonymized_at column and a scope.

Gotchas and Tips

  • Laravel 13 requires v1.10.0+. The only change in v1.10.0 is framework compatibility (Laravel Shift PR #53) — no API changes, no migrations, no config keys. That means upgrading is low-risk, but it also means nothing about the package's behaviour changed: don't expect the Laravel 13 bump to fix the issues below. Budget a full regression run of consent + anonymization + export on the upgrade anyway, especially if you're also jumping framework major versions.
  • enabled is a footgun in tests. Set GDPR_ENABLED=false in phpunit.xml so you don't accidentally write consents during unrelated feature tests — or, better, use RefreshDatabase and assert on consents explicitly. Half the "flaky test" bug reports on this package are consent side-effects leaking between tests.
  • Ratings are integers, not booleans. The most common misconfiguration is setting every user to 5 "to be safe." That makes every scheduled anonymization nuke everything. Start with the conservative low value and increment intentionally, or you'll discover the config's meaning by destroying data.
  • Migrations are additive only. The package adds its own tables and (optionally) a gdpr_rating column in users. If you have an existing users table with soft deletes, encrypted columns, or a settings JSON blob, double-check that the package's anonymizer knows how to null each of them — a JSON column that's nulled wholesale can break your app while technically complying.
  • Scheduled vs. queue. gdpr:anonymize is a console command; if you also run it via a job, ensure it's idempotent. The package tracks state on the gdpr_anonymizations row — verify that a re-run of an already-completed job is a no-op, not a double-anonymization (which is harmless but pollutes your logs and may re-trigger audit alerts).
  • Foreign keys and cascades. Erasure that runs across related models (posts, orders, comments) needs every table to have nullable FKs or cascade behavior. If a child table has a non-nullable user_id, the package's per-model anonymization will succeed at the model layer but leave orphans — or, worse, the deletion step will fire a FK constraint error mid-job. Fix this in a migration batch before enabling the scheduler.
  • Export size is your problem. Large users (100k+ rows) will blow PHP memory on toJson() loops. Stream per-model and chunk with ->cursor(). The package's default export is fine for small accounts; for real ones, swap the exporter.
  • Consent withdrawal ≠ data deletion. These are frequently confused in tickets ("I withdrew marketing consent, why is my email still in the DB?"). The package separates them, and your support docs should too. Withdrawing consent stops processing; only an erasure request triggers anonymization.
  • Audit trail is on you. The consents and anonymizations tables are the ledger. If legal asks "what did user X consent to on date Y," the package's rows answer that — but only if you're not hard-deleting those rows. Add a DB-level ON DELETE RESTRICT on consentable relationships so a user deletion can't silently wipe the history.
  • Routes are opt-in but not namespaced. When you publish and enable the package routes, verify they don't collide with your app's /gdpr prefix conventions. Also check middleware — the package typically doesn't ship auth on its routes; you'll want to add it in a Route::group wrapper.
  • Checklist command is a starting point. php artisan gdpr:checklist is the author's opinion, not a lawyer's. Use it as a ticket generator, not a compliance proof. Pair it with your DPO's actual review.
  • Extension points worth knowing: the config models array (register every model that holds PII — not just User), the facade for programmatic calls, and the exporter/anonymizer bindings in the service provider. If you need to customize how a field is anonymized (e.g. sha256(email) instead of null), you subclass the anonymizer and swap the binding — don't fork the package.
  • Version pin. With a handful of dependents on Packagist and releases driven by drive-by compat PRs (the v1.10.0 Laravel 13 bump is a single external PR), pin to a specific tag in composer.json and re-run your consent/anonymization tests on every upgrade. Treat each release as "compat-only until proven otherwise," and check the changelog for whether it touched behaviour at all — v1.10.0 did not.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin