Weave Code
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 Personal Data Export Laravel Package

spatie/laravel-personal-data-export

Generate GDPR-style personal data exports as ZIP files in Laravel. Define what to include via a model method, add JSON and files (local or S3), store zips privately, email users a download link, and clean up old exports with an artisan command.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require spatie/laravel-personal-data-export
    php artisan vendor:publish --provider="Spatie\PersonalDataExport\PersonalDataExportServiceProvider"
    
    • Publishes config (config/personal-data-export.php) and migrations (if using the built-in storage).
  2. Configure Storage Update config/personal-data-export.php to define:

    • storage (default: local, options: local, s3, ftp, rackspace).
    • disk (e.g., public, s3).
    • path (e.g., personal-data-exports).
    • expiration_in_minutes (default: 60, auto-deletes old exports).
  3. First Use Case: Basic Export Trigger an export for the current user (e.g., in a controller or command):

    use Spatie\PersonalDataExport\Jobs\CreatePersonalDataExportJob;
    
    dispatch(new CreatePersonalDataExportJob(auth()->user()));
    
    • The job queues asynchronously, emails the user a download link, and stores the ZIP in the configured disk.
  4. Verify Setup

    • Check storage/path/to/exports (or configured disk) for generated ZIPs.
    • Test the email link (requires user authentication to download).

Implementation Patterns

Core Workflow

  1. Define Exportable Data Use the Exportable trait or implement Spatie\PersonalDataExport\Concerns\ExportsPersonalData:

    use Spatie\PersonalDataExport\Concerns\ExportsPersonalData;
    
    class User extends Authenticatable
    {
        use ExportsPersonalData;
    
        public function getPersonalDataExportables(): array
        {
            return [
                'user_data' => [
                    'email' => $this->email,
                    'name' => $this->name,
                    'created_at' => $this->created_at->format('Y-m-d'),
                ],
                'related_orders' => $this->orders->map(fn ($order) => [
                    'id' => $order->id,
                    'total' => $order->total,
                ]),
            ];
        }
    }
    
    • Key Methods:
      • getPersonalDataExportables(): Returns an associative array of data (keys become folder names in the ZIP).
      • getPersonalDataExportFilename(): Customize the ZIP filename (default: user-{id}-export-{timestamp}.zip).
  2. Customize Export Format

    • Override Defaults: Extend Spatie\PersonalDataExport\PersonalDataExporter to modify serialization (e.g., JSON vs. CSV):
      public function export(array $data): string
      {
          return json_encode($data, JSON_PRETTY_PRINT);
      }
      
    • Add Metadata: Include a README.md or LICENSE.txt in the ZIP:
      public function getPersonalDataExportAdditionalFiles(): array
      {
          return [
              'README.md' => 'This file contains your personal data.',
          ];
      }
      
  3. Trigger Exports Programmatically

    • Manual Dispatch:
      dispatch(new CreatePersonalDataExportJob($user, $customFilename));
      
    • Event-Based: Listen for user actions (e.g., account deletion):
      UserDeleted::dispatch($user);
      // In a listener:
      CreatePersonalDataExportJob::dispatch($user)->delay(now()->addMinutes(5));
      
    • API Endpoint:
      Route::post('/export-data', function () {
          $this->authorize('export-data');
          CreatePersonalDataExportJob::dispatch(auth()->user());
          return response()->json(['message' => 'Export initiated']);
      });
      
  4. Handle Large Exports

    • Chunk Data: For models with large relationships, use cursor() or chunk():
      public function getPersonalDataExportables(): array
      {
          return [
              'posts' => $this->posts->cursor()->map(fn ($post) => [
                  'title' => $post->title,
              ]),
          ];
      }
      
    • Stream ZIP: Override Spatie\PersonalDataExport\PersonalDataExporter to stream files directly to the ZIP (avoids memory issues):
      public function createZip(): void
      {
          $zip = new ZipArchive;
          $zip->open('path/to/export.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
          foreach ($this->data as $folder => $items) {
              foreach ($items as $filename => $content) {
                  $zip->addFromString("{$folder}/{$filename}.json", json_encode($content));
              }
          }
          $zip->close();
      }
      
  5. Integrate with Notifications

    • Customize the email notification by publishing the view:
      php artisan vendor:publish --tag=personal-data-export-views
      
    • Extend the notification logic in app/Notifications/PersonalDataExportNotification.php:
      public function toMail($notifiable)
      {
          $url = route('personal-data-export.download', [
              'export' => $this->export->id,
              'signature' => $this->export->signature,
          ]);
          return (new MailMessage)
              ->subject('Your Personal Data Export')
              ->line("Click here to download your data: {$url}");
      }
      

Gotchas and Tips

Pitfalls

  1. Storage Permissions

    • Ensure the configured disk (e.g., public) has write permissions:
      chmod -R 775 storage/app/public/personal-data-exports
      
    • For S3/FTP, verify credentials in .env and test connectivity:
      php artisan storage:link  # If using public disk
      
  2. Memory Limits

    • Large exports may hit PHP’s memory_limit. Increase it in php.ini or .env:
      memory_limit = 512M
      
    • For huge datasets, use database cursors or chunking (see Implementation Patterns).
  3. Expiration Quirks

    • Exports older than expiration_in_minutes are auto-deleted by the DeleteOldPersonalDataExports command.
    • Manual Deletion: Run:
      php artisan personal-data-export:delete-old
      
    • Edge Case: If exports disappear unexpectedly, check:
      • Cron job scheduling (if using schedule:run).
      • Disk space or cleanup scripts.
  4. Authentication Bypass

    • By default, downloads require authentication. To allow public access:
      • Set public_download_url in config to a signed URL (e.g., S3 pre-signed URL).
      • Or override the download route middleware:
        Route::get('/download-export/{export}', [ExportController::class, 'download'])
            ->middleware('guest'); // Remove auth if needed
        
  5. Data Serialization Issues

    • Non-Serializable Objects: Avoid adding closures, resources, or non-JsonSerializable objects to getPersonalDataExportables().
    • Circular References: Use ->toArray() or json_encode() to flatten relationships:
      'profile' => $this->profile->toArray(),
      
  6. Job Failures

    • Check the failed_jobs table for queued job errors.
    • Debugging: Temporarily disable the queue and run the job sync:
      (new CreatePersonalDataExportJob($user))->handle();
      

Debugging Tips

  1. Log Export Contents Add a temporary method to inspect data before export:

    public function debugExportData()
    {
        dd($this->getPersonalDataExportables());
    }
    
  2. Test with Minimal Data Start with a single model and small dataset to verify the pipeline works.

  3. Check Disk Events Listen for personal-data-export.created events to log export paths:

    PersonalDataExportCreated::listen(function ($export) {
        Log::info('Export created:', ['path' => $export->path]);
    });
    

Extension Points

  1. Custom Exporters Create a custom exporter for non-standard formats (e.g., XML):

    namespace App\Exports;
    
    use Spatie\PersonalDataExport\PersonalDataExporter;
    
    class XmlExporter extends PersonalDataExporter
    {
        protected function export(array $data): string
        {
            return $this->arrayToXml($data);
        }
    
        private function arrayToXml(array $data): string
        {
            // Implement XML conversion logic
        }
    }
    

    Register it in config/personal-data-export.php:

    'exporter' => \App\Exports\XmlExporter::class,
    
  2. Dynamic Data Sources Fetch data dynamically (e.g., from an API) in `getPersonalData

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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport