You'll need to have a look at "describe calls" - set of methods that let us learn at runtime what fields are on given object. The topic is pretty long so it might take you a while to go through it all and understand it...
In your case a good starting point would be something like this:
Map<String, Schema.SObjectField> describeResult = Schema.SObjectType.Event__c.fields.getMap();
Set<String> allFieldNames = describeResult.keySet();
System.debug(JSON.serializePretty(allFieldNames));
This should give you set with all field API names (the other part - Schema.SObjectField
we skip for now). The next trick is that you can fetch field values dynamically:
Event__c e = new Event__c(Name = 'Something');
System.debug(e.Name == (String) e.get('Name'));
So you could loop through trigger.new and trigger.old comparing all fields by fetching their values with get(String fieldName
) method.
There's more to it because you'll realize that sometimes data renders ugly:
- date fields cast to string look so-so out of the box;
- lookup fields will report that value changed from one Id to another - not too user-friendly);
- picklists will always be in English (or whatever is your org's default language) while user would like to see the email in French.
- etc.
Around this time you'll have to consider looking at the other piece of the equation, the Schema.SObjectField
we got in the map. And get back to reading how we can use this to learn more about the field type (is it Date/lookup/picklist; if lookup - where to get the Name value; if picklist - what would the localized label be)...
If your object is small (less than 20 fields or you can convince SF support to increase the limit) - maybe simply set field history tracking on every field ;) It'll also require some tricks (@future maybe because these don't get written until the commit) but might be less hassle...