Salesforce Junction objects are a convenient way to create a many-to-many relationship in your data model. Accourding to the docs, a many-to-many relationship allows each record of one object to be linked to multiple records from another object and vice versa. For example, you may have a custom object called Bug that relates to the standard case object such that a bug could be related to multiple cases and a case could also be related to multiple bugs. When modeling a many-to-many relationship, you use a junction object to connect the two objects you want to relate to each other.
While Junction objects are great in threory, in the real-world they are sometimes a little difficult to work with. One issue that always bugs me is that the 'Name' of the record that I am REALLY insterested in is not part of the Junction Object but part of the related object. So I thought to myself, why not add a field to one of the master objects and display a comma separated list of the 'Name' values from the other object in the relationship. These are the values I really want to see. This list would be useful for reporting, exposing via web services, integrations, etc. so you wouldn't have to worry about dealing with the collection of junction records... just their values.
Disclaimer: this is great approach for 2-5 Junction records in the list but after that it gets somewhat unweildly.
So we have this same use case at CloudSpokes. We have two objects called Challenge__c and Platform__c. <a href="http://www.cloudspokes.com/challenges"target="_blank">If you look at our site</a>, you'll notice that a challenge can be for multiple platforms.
Naturally we achieve this with a Junction object called Challenge_Platform__c. Notice the Name__c field which is a formula to add the value of the Name field from the Platform__c field to the Junction object. This comes in handy in our Apex code as this makes it easier to reference the name then by looking up the reference.
We are really interested in what platforms a challenge is for and would like to display that at the top of the page instead of making the user scroll down to the related list section. So let's write a Trigger that creates a comma separated list of the current platforms for a challenge each time we insert or delete a new Challenge_Platform__c record(s). We aren't too concerned with updates since there's really nothing to update of value on the Junction object.
ChallengePlatformTrigger
So here's the code for our trigger which fires after new Challenge_Platform__c records are inserted or deleted. The trigger code essentially builds a Set of the Challenge Ids that were affected by the insert/delete operation and passes them off to the ChallengePlatformTriggerHandler.RollupNamesToList method asynchronously to be processed. We process these records asynchronously since we don't need this new list immediately and why make users wait for a process to finish when you could perform it in the background without the delay?
ChallengePlatformTriggerHandler
The ChallengePlatformTriggerHandler does the actual work for us with a little help from the awesome apex-lang package and it's pluck method. With pluck, you can pass the method a List of Objects (from a query let's say) and it will return an array with the values from the field you specified. Not sure why pluck isn't part of Apex natively as it's super useful.
On line #17 below we create a map that contains the challenges being affects and the Name__c field of all related Junction objects. Then we iterate through the map by ID and build the string list of names. On line #28 we use the pluck method to return an Object array of the Name__c field values in the related records. Then we update the challenge depending with the new list of platform names or null if all Junction records have been deleted. Notice the Utils.ListToCSVString on line 33. This is a simple helper that takes an array of Objects and returns a comma separated list.
Test_ChallengePlatformTriggerHandler
No application for Force.com is complete with out test case! So here is your test class with 100% coverage.