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

Filagora Compliance Laravel Package

filagora/filagora-compliance

Compliance add-on for Filagora that helps Laravel shops meet legal requirements with ready-made consent, policy, and GDPR-friendly tools. Lightweight and easy to integrate into existing Filagora storefronts.

View on GitHub
Deep Wiki
Context7

Getting Started

This package layers GDPR/privacy tooling onto Filagora: a Filament admin area, a privacy-request lifecycle, cookie consent, policy acceptance, data export/erasure, and retention pruning. It requires filagora/filagora-core ^0.2.1; filagora/filagora-entitlements and filagora/filagora-cms are optional.

Minimal steps:

  1. composer require filagora/filagora-compliance and run php artisan migrate. All eight tables ship in a single migration; the schema snapshot means php artisan filagora:adopt accepts an existing site's tables without a re-migration.
  2. Publish the config and read it top to bottom — it is the map of the package:
    php artisan vendor:publish --tag=filagora-compliance-config
    
    Look first at: gates, models, kpis, mail, routes, retention, schedule, anonymize, and the workflow toggles.
  3. Set the settings the package declares, at minimum dsar_request_email (where retention-failure reports and default DSAR mail go), plus cookie_policy_page and privacy_page for your site's CMS pages.
  4. Register the Filament plugin in the panel you want it in:
    ->plugin(\Filagora\Compliance\Filament\FilagoraCompliancePlugin::make())
    
    It appears as a "Privacy and compliance" navigation group with Privacy requests, Cookie groups, Cookie policy versions, Privacy policy versions, a read-only Privacy operation audit, and a KPI widget.
  5. Turn on the workflows you actually need — they default off and the services refuse politely rather than throwing:
    // config/filagora-compliance.php
    'enable_cookie_consent_workflow' => true,
    'enforce_gdpr' => true,
    'schedule' => ['enabled' => true, 'daily_at' => '02:30'],
    

First real use case: wire your own data into export/erasure. Register a PrivacyContributor in a service provider, then submit a request through the PrivacyRequests service and work it from the Filament Privacy requests screen — generate the export, download it, open its audit, and check that the requester was told.

Implementation Patterns

Submit from the front end, never by inserting rows. PrivacyRequests::submit() is where every guard lives (workflow enabled, known and offered type, details present, person not erased, no duplicate open request of that type). It refuses with a reason instead of failing, so surface that reason in your form validation rather than catching exceptions:

$result = $privacyRequests->submit(
    person: $user,
    type: PrivacyRequestType::Export,
    details: $request->string('details'),
);
// show $result's refusal reason when it did not go through

Drive the lifecycle through the service. changeStatus(), hold(), release(), reject(), reply(), fulfilExport(), fulfilErasure() all audit to PrivacyAudit and notify the person. The Filament actions call exactly the same methods, so an admin doing it by hand and a queued job doing it programmatically leave identical audit trails. Use changeStatus() (not a raw update()) even for hand-fulfilled requests — that path audits the hand fulfilment.

Contribute export and erasure sections. This is the main integration point for your own models. One contributor does both directions: PersonalDataExport writes one JSON file with a user section plus a section per registered contributor; PersonalDataErasure runs every contributor's erase, then the UserAnonymizer, all in a single transaction. Register in a provider:

public function boot(PrivacyContributorRegistry $contributors): void
{
    $contributors->register(new OrdersPrivacyContributor());
}

ConfiguredColumnsAnonymizer (the default) replaces identity columns listed under anonymize.attributes — set that carefully, because erasure is irreversible and happens last inside the transaction. The package registers its own ComplianceDataContributor (exports cookie choice, acceptances, requests; erases cookie choice) so you can see the intended shape.

Cookie consent on the front end. Use CookieConsent: enabled groups with their cookies, the active policy version, a stored choice, normalize(), save() and requiresReconsent(). normalize() applies precedence cookie's own choice → its group's → default, and forces required groups on — call it server-side before persisting, don't trust posted values. save() stores one row per user with a hashed address and user agent, which is what requiresReconsent() later compares against.

Policy acceptance. PolicyAcceptance gives you activeVersion() (latest published, cached by id for 60 seconds), hasAcknowledgedActivePolicy(), stateFor() and accept(). Gate the parts of your app that need it on enforce_gdpr being on and hasAcknowledgedActivePolicy(), and re-check after publishing a new policy version — 60-second caching means a just-published version can lag briefly.

Publish cookie policy versions from your CMS. When filagora/filagora-cms is installed, publishing the page named by cookie_policy_page creates a cookie policy version automatically. Otherwise manage them in the Cookie policy versions resource; they're day-numbered, so avoid hand-crafting versions if you can publish through the CMS.

Listen rather than poll. PrivacyRequestSubmitted, PrivacyRequestStatusChanged and PrivacyReplySent fire for every path. Hook your own notifications, Slack pings or ticket syncs there; leave the built-in mail alone or switch it off with mail.enabled once you have your own. Links in that mail are named routes under routes and are simply omitted when not configured — good for local, bad if you forget to set them in production.

Retention. Register rules with core's RetentionRuleRegistry; php artisan filagora:compliance:prune --dry-run reports what would go, and the scheduled daily run (per schedule.daily_at) executes them. A failing rule is reported and mailed to the DSAR address without stopping the others — so a broken rule is a quiet problem, not a loud one. The package's own PrivacyRequestRetention deletes settled requests by status window under retention.privacy_requests.

Authorisation. Access flows through core's AccessChecker and GateAbilities, so the package works without filagora-entitlements. Abilities are configured under gates; the release describes six abilities (see requests, handle them, erase a person, manage cookie policy and groups, manage privacy policy versions, read the audit) surfaced through three config keys (privacy_requests, policies, audit). Check the actual gate names in the published config before assigning them to roles.

Gotchas and Tips

  • Legal hold blocks erasure, not just export. A request on hold cannot be fulfilled at all. If a fulfilErasure() call appears to do nothing, check hold first — it refuses rather than failing.
  • One open request per type per person. submit() refuses a duplicate. If support staff need a resubmission, they must settle the old one; don't work around it by inserting rows, or you lose the audit trail.
  • Erasure order matters. Contributors erase first, the anonymizer runs last, all in one transaction. A contributor that throws rolls back the anonymisation too — good for consistency, but it means one bad contributor blocks erasure entirely. Test contributors against a real user before enabling enforce_gdpr.
  • Two different export deletion clocks. Export files are deleted delete_export_after_days after fulfilment while the request is kept with export_path cleared; the request itself only goes when retention prunes it, potentially years later. Don't assume a null export_path means the request is pruned.
  • Run --dry-run before you trust the schedule. prune deletes settled requests by status window. Confirm which statuses count as settled in retention.privacy_requests before enabling the daily schedule, and make sure dsar_request_email is a monitored mailbox — that's where failing rules report.
  • Rejected requests are hidden by default. The Privacy requests list filters them out; that's a filter, not a deletion. Use the filters to find them rather than assuming the data is gone.
  • Swappable models must satisfy the package's expectations. Changing filagora-compliance.models entries is supported, but your replacements need to keep the columns and relations the resources and services rely on (the audit relations, the cookie relation manager, export_path). Extend the package models rather than rewriting from scratch.
  • Existing sites: adopt, don't migrate. The schema snapshot exists so filagora:adopt accepts tables that already match "index for index". If your production tables drifted from the original site's schema, adopt will not paper over it — reconcile first.
  • Policy version is cached for 60 seconds by id. In tests, clear the cache between publishing a version and asserting acceptance state, or you'll chase a ghost. Same applies to requiresReconsent() right after a publish.
  • The KPI widget is config-driven. "Fulfilled within the deadline", "open requests" and "waiting for review" read from kpis — tune the thresholds there rather than editing the widget, and be aware a deadline-based KPI depends on the dates changeStatus() maintains; never edit status dates directly.
  • Extension points in priority order: a PrivacyContributor for your own data, the PrivacyRequests / CookieConsent / PolicyAcceptance services for behaviour, the Privacy* events for side effects, gates and models for per-app wiring. Reach for raw model updates last — everything meaningful in this package is audited, and bypassing the services is how you end up with an audit trail that doesn't match reality.
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