Comparison — equals() & diff()
equals()
Returns true if both DTOs produce the same toArray() output:
$a = UserData::from(['name' => 'Alice', 'email' => 'alice@example.com']);
$b = UserData::from(['name' => 'Alice', 'email' => 'alice@example.com']);
$c = UserData::from(['name' => 'Bob', 'email' => 'bob@example.com']);
$a->equals($b); // true
$a->equals($c); // false
$a->equals($a); // true (reflexive)Comparison is value-based, not reference-based:
$a === $b; // false — different objects
$a->equals($b); // true — same dataObjects of different classes are never equal, even if their toArray() output happens to match:
$user = UserData::from(['name' => 'Alice']);
$admin = AdminData::from(['name' => 'Alice']);
$user->equals($admin); // false — different classesdiff()
Returns an array of fields that differ between two DTOs. Each entry is [this_value, other_value]:
$a = UserData::from(['name' => 'Alice', 'email' => 'alice@example.com']);
$b = UserData::from(['name' => 'Bob', 'email' => 'alice@example.com', 'phone' => '123']);
$a->diff($b);
// [
// 'name' => ['Alice', 'Bob'],
// 'phone' => [null, '123'],
// ]An empty array means the objects are equal:
$a->diff($b) === []; // equivalent to $a->equals($b)Diff on Casted Fields
diff() compares serialized values (output of toArray()), so casts are applied before comparison:
$a = EventData::from(['name' => 'Conf', 'startsAt' => '2024-01-01']);
$b = EventData::from(['name' => 'Conf', 'startsAt' => '2025-06-15']);
$a->diff($b);
// ['startsAt' => ['2024-01-01', '2025-06-15']]Encrypted and Hidden Fields
EncryptedCast produces a fresh ciphertext on every toArray(), so equals() and diff() compare those fields by their decrypted value: two objects holding the same card number are equal. diff() still reports a changed encrypted field with its ciphertexts, never the plaintext. The Eloquent cast (IsEloquentCastable) uses the same comparison, so saving a model whose data object didn't change doesn't rewrite the column.
#[Hidden] fields are not part of toArray() and are ignored by both methods.
Use Cases
// Audit log: record what changed
$before = UserData::fromModel($user);
$user->update($attributes);
$after = UserData::fromModel($user->fresh());
$changes = $before->diff($after);
AuditLog::record($user->id, $changes);// Guard against no-op updates
if ($existing->equals($incoming)) {
return; // nothing changed
}
$repository->save($incoming);
