email) { return new ProviderExportResult('reviews', []); } $reviews = $this->matchingReviews($subject->email)->get(); return new ProviderExportResult('reviews', $reviews->map(fn (ProductReview $review) => [ 'id' => $review->id, 'product_id' => $review->product_id, 'title' => $review->title, 'body' => $review->body, 'rating' => $review->rating, 'reviewer_name' => $review->reviewer_name, 'reviewer_email' => $review->reviewer_email, 'reviewed_at' => $review->reviewed_at?->toIso8601String(), ])->all()); } public function eraseForCustomer(CustomerSubject $subject): ProviderErasureResult { return new ProviderErasureResult('reviews', ErasureOutcome::Skipped, 'Reviews are authored by individuals, not Customer accounts.'); } public function eraseForUser(UserSubject $subject): ProviderErasureResult { if (! $subject->email) { return new ProviderErasureResult('reviews', ErasureOutcome::Skipped, 'No email on this subject to match reviews by.'); } $matched = $this->matchingReviews($subject->email)->count(); if ($matched === 0) { return new ProviderErasureResult('reviews', ErasureOutcome::Skipped, 'No reviews matched this email.'); } // The review content itself (rating/title/body) is kept — it's the // reviewer's own product feedback, not identity data on its own — only // the identifying fields are cleared. $this->matchingReviews($subject->email)->update([ 'reviewer_name' => 'Anonymous', 'reviewer_email' => null, ]); return new ProviderErasureResult( 'reviews', ErasureOutcome::Pseudonymized, 'Reviewer name/email cleared on reviews matched by email; rating/title/body text retained.' ); } private function matchingReviews(string $email): Builder { return ProductReview::where('reviewer_email', $email); } }