Personal Data Annotation
Knowing which fields contain personal data is essential for privacy compliance and for understanding what your application stores about users. AuditChain provides two ways to annotate personal data fields on your models: the #[PersonalData] PHP 8 attribute and the $personalData array. This also helps satisfy GDPR Article 5(1)(a) and Article 30, which require organizations to document exactly which fields contain personal data.
Once annotated, AuditChain automatically tracks which personal data fields are accessed or modified in every audit log entry via the personal_data_accessed column. This powers data export and anonymization.
Using the PHP 8 Attribute
The #[PersonalData] attribute lets you annotate individual properties directly on the class. This is the recommended approach — it keeps the annotation close to the property definition and supports an optional description.
use GrayMatter\AuditChain\Attributes\PersonalData; use GrayMatter\AuditChain\Concerns\HasAuditTrail; use GrayMatter\AuditChain\Contracts\Auditable; class User extends Model implements Auditable { use HasAuditTrail; #[PersonalData(description: 'User email address')] public string $email; #[PersonalData(description: 'Full legal name')] public string $name; #[PersonalData] public string $phone; }
The description parameter is optional. It serves as documentation for your team and can be useful when generating data processing records.
Using the Array
If you prefer a simpler approach or need dynamic field lists, define a $personalData array on your model:
use GrayMatter\AuditChain\Concerns\HasAuditTrail; use GrayMatter\AuditChain\Contracts\Auditable; class Customer extends Model implements Auditable { use HasAuditTrail; protected array $personalData = ['email', 'name', 'phone', 'address']; }
Note: If both the
$personalDataarray and#[PersonalData]attributes are present, the array takes priority. Use one approach per model.
Retrieving Personal Data Fields
Call getPersonalDataFields() on any auditable model to get the list of annotated field names:
$user = new User(); $fields = $user->getPersonalDataFields(); // ['email', 'name', 'phone']
This method is used internally by exportPersonalData(), exportFullSubjectData(), and anonymize(). You can also call it directly when building data processing inventories or responding to data subject requests.
How It Affects Audit Logs
When an audit event is recorded, AuditChain compares the changed fields against the personal data fields. If any overlap, the personal_data_accessed column on the audit log captures which personal data fields were involved:
$user->update(['email' => 'new@example.com', 'role' => 'admin']); // The resulting audit log will contain: // personal_data_accessed => ['email'] // (role is not personal data, so it's not listed)
For retrieved events (when read tracking is enabled), all personal data fields are recorded as accessed — since the entire model was loaded.
Practical Tips
- Be thorough: Include all fields that could identify or relate to a natural person — names, emails, phone numbers, addresses, IP addresses, identifiers.
- Don't annotate non-personal fields: Fields like
role,status, orcreated_atare typically not personal data. Over-annotating dilutes the usefulness of the tracking. - Review periodically: When you add new columns to a model, check whether they should be annotated as personal data.