Logging interactions against either a Contact or an Organization without forcing a join through one or the other.
constraint: Some interactions are with a specific person (a call to a contact); some are with the org as a whole (a meeting with no named attendee). Both need to show up in the same timeline, ordered by date.
Interaction has nullable contact_id AND organization_id, plus type (email/call/meeting/note/event). The timeline query is one Eloquent scope that joins both as left-joins and projects a unified row. created_by + updated_by are filled by an auth-aware observer so the audit trail does not need extra writes.
class Contact extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'first_name', 'last_name', 'email', 'phone',
'position', 'organization_id',
'tags', 'custom_fields',
'status', 'source', 'created_by',
];
protected $casts = [
'tags' => 'array',
'custom_fields' => 'array',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
];
}