0

I am definitely in the beginning stages of my Apex learning, so I was hoping if I could ask for help on a problem I am working on. I would like to create a trigger which would auto subscribe a user to a chatter feed. For example, I have a a custom object Story__c which has chatter enabled for it. I also have a child object called Story_Task__c, which does not have chatter enabled. What I am trying to accomplish here is to auto assign the Owner__c from the Story_Task__c as a subscriber on the Story__c object. The issue I am facing is on the Update. If the owner changes of an existing Story_Task__c. My code compiles without any bugs, but does not execute, so I am certain there is a logical error.

if(trigger.isUpdate){

            List<EntitySubscription> sttEntitySubListUpdate = new List<EntitySubscription>();
            List<Story_Task__c> taskList = new List<Story_Task__c>();
            List<id>OldOwnerIdList = new List<Id>();

        for (Story_Task__c task :trigger.new) {
            Story_Task__c oldTask = Trigger.oldMap.get(task.ID);  
            if (task.Owner__c != oldTask.Owner__c) {
                OldOwnerIdList.add(oldTask.Owner__c);
                taskList.add(task);

            }
        }

delete[select ID from EntitySubscription where ParentId IN :taskList AND SubscriberId IN :OldOwnerIdList];
    Map<Id, Set<Id>>MapOfExistingSubscriber = new Map<Id, Set<Id>>();

        for(EntitySubscription ESObj : [Select Id, ParentId, SubscriberId From EntitySubscription WHERE ParentID IN: taskList]){
            if(MapOfExistingSubscriber.containsKey(ESObj.ParentID)){
                MapOfExistingSubscriber.get(ESObj.ParentId).add(ESObj.SubscriberId);
            }
            else{
                MapOfExistingSubscriber.put(ESObj.ParentID, new Set<Id>{ESObj.SubscriberId});

            }
        }

        for(Story_Task__c task : taskList){
            if(task.Owner__c != Null){
                EntitySubscription e = new EntitySubscription();
                e.ParentId = task.Id;  ///
                if(MapOfExistingSubscriber.containsKey(task.id) && MapOfExistingSubscriber.get (task.id).size() > 0 && MapOfExistingSubscriber.get(task.id).contains(task.Owner__c)){
                //Owner Exists
                }
                else{
                e.SubscriberId = task.Owner__c;
                sttEntitySubListUpdate.add(e);
                }
            }
        }
        if(sttEntitySubListUpdate.size() > 0) 

        insert sttEntitySubListUpdate;

    }

I believe my code does not execute because in my delete SOQL statement, I am referencing ParentID in :taskList the issue is that my list of Story_Taks__c does not have chatter enabled, so that is why nothing happens. Could someone help me specify that I would like to look into the Task's parent object Story__c? If I am checking my id to the associated Story__c, then I think my code would execute.

  • You should look at separating your code out into Service and Handler patterns. – Adrian Larson Mar 22 '17 at 12:54
  • I agree. I have seen examples of how and why that is being done, but considering I am still trying to learn the syntax, I thought of learning how to write a trigger would be a good first step in my learning process. – Ivo Dimov Mar 22 '17 at 13:06
  • You've got it backwards. – Adrian Larson Mar 22 '17 at 13:07
1

Independently of whether the functionality is correct or not, that is a ton of code to put into your trigger body. I recommend you have a read through Trigger Frameworks and Apex Trigger Best Practices

Logic-less Triggers

Another widely-recognized best practice is to make your Triggers logic-less. That means, the role of the Trigger is just to delegate the logic responsibilities to some other handler class. There are many reasons to do this. For one, testing a Trigger is difficult if all of the application logic is in the trigger itself. If you write methods in your Triggers, those can’t be exposed for test purposes. You also can’t expose logic to be re-used anywhere else in your org. Good old OO principles tell us that this is a bad practice. And to top it all off, cramming all of your logic into a Trigger is going to make for a mess one day. To remedy this scenario, just create a handler class and let your Trigger delegate to it.

I don't love the patterns they lay out in the above linked article, but the principles are sound. My biggest problem with what they've written is that they put the method implementations in the Handler, when really they belong in the Service. I recommend you work through the Apex Enterprise Patterns: Service Layer module on Trailhead in addition to reading that article.

If I were going to lay out a service for this functionality, it might look something like:

public with sharing class StoryTaskService
{
    public static void removeStorySubscriptions(List<Story_Task__c> records)
    {
        // delete implementation
    }
    public static void addStorySubscriptions(List<Story_Task__c> records)
    {
        // insert implementation
    }

    public static List<Story_Task__c> hasOwnerChanged
        (List<Story_Task__c> newRecords, Map<Id, Story_Task__c> oldMap)
    {
        // filter implementation
    }
    public static List<Story_Task__c> needsSubscription
        (List<Story_Task__c> newRecords, Map<Id, Story_Task__c> oldMap)
    {
        // filter implementation
    }
}

A service layer designed in this way would really let you test the nitty-gritty details of how you do your filtering, and how you act on that input to create subscriptions. It seems likely that your error is in the filtering layer.

  • Thank you for the input Adrian, it is greatly appreciated. I totally agree that the pattern you mentioned is the one to be followed, but me lacking the syntax knowledge would still lead me to the same problem as where I am now. I will give it a try, maybe that would help me resolve my problem. Thanks – Ivo Dimov Mar 22 '17 at 13:37
  • If you're not getting compile errors then syntax isn't a problem for you so far... Anyway that's all the more reason to work your way through specific Trailhead modules. – Adrian Larson Mar 22 '17 at 14:20
  • Yea,I should start from scratch and work in Trailhead first. Thanks – Ivo Dimov Mar 22 '17 at 15:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.