1

I am trying to do (what I think is) a simple trigger that counts the number of files attached to a custom object record(SBQQ__QuoteDocument__c) and puts that number in a field on that object. I am trying to query ContentDocumentLink because after some research that seems like the correct object that holds the Quote Document Id(as LinkedEntityId) and the File Id in the same place. I am doing this so I can run validation rules to make sure a user has uploaded a file to each Quote Document record before Opportunity can be marked Closed Won, that part will be easy. I do not want to go the DLRS route or install any new apps because client is wary of doing this, so trigger seems like the best way to go. I don't get into into backend dev work very much so I get rusty easily. Here is what I have so far, neither will upload to the database successfully. Thank you in advance for any kind words of wisdom!

ContentDocumentLinkTrigger

trigger ContentDocumentLinkTrigger on ContentDocumentLink (after insert, after delete, after undelete) {

    if(trigger.isAfter){

        if(trigger.isInsert || trigger.isDelete || trigger.isUndelete){

            ContentDocumentLinkTriggerHandler.fileCount(Trigger.new);

        }

    }

}

ContentDocumentLinkTriggerHandler.cls

public with sharing class ContentDocumentLinkTriggerHandler {
    
    public static void fileCount(Trigger.new){

        Set<Id> parentQuoteDocumentIds = new Set<Id>();

        for(ContentDocumentLink cdl : Trigger.new){

            parentQuoteDocumentIds.add(cdl.LinkedEntityId);

        }

        List<SBQQ__QuoteDocument__c> quoteDocList = [SELECT Id FROM SBQQ__QuoteDocument__c WHERE Id IN :parentQuoteDocumentIds];

        for(SBQQ__QuoteDocument__c qd : quoteDocList){

            qd.Number_of_Files_Attached__c = quoteDocList.size();

        }

    }

}
1
  • 2
    DLRS of course is implemented as a trigger and does basically what you have implemented + David's answer. It is reliable, efficient, and has additional features like scheduled v realtime calculation mode. No need to write a testmethod
    – cropredy
    Jul 13, 2020 at 4:33

1 Answer 1

2
public with sharing class ContentDocumentLinkTriggerHandler {
    
    public static void fileCount(Trigger.new){

        Set<Id> parentQuoteDocumentIds = new Set<Id>();

        for(ContentDocumentLink cdl : Trigger.new){

            parentQuoteDocumentIds.add(cdl.LinkedEntityId);

You may want to do some filtering here, probably on cdl.LinkedEntityId.Type, since you're only interested in parents that are SBQQ__QuoteDocument__c. Your current code silently works because of the way your query is structured, but if you start using this collection in other ways down the line, you may not remember that your collection could contain other Ids too and find unexpected behaviors.


        }

        List<SBQQ__QuoteDocument__c> quoteDocList = [SELECT Id FROM SBQQ__QuoteDocument__c WHERE Id IN :parentQuoteDocumentIds];

        for(SBQQ__QuoteDocument__c qd : quoteDocList){

            qd.Number_of_Files_Attached__c = quoteDocList.size();

        }

Here's the meat of it, and I think what you need here is to "rubber-duck debug" this code - explain it to a silent interlocutor out loud. Here's how I might narrate it:

"So here we query all of the SBQQ__QuoteDocument__c records from the trigger set. Then, we iterate over the Quote Documents, and for each one, we set the Number_of_Files_Attached__c for that Quote Document to the number of Quote Documents we found."

... There's the problem! The logic is not quite right here. You need an additional query on ContentDocumentLink to find the information you really want - the number of files attached to the Quote Document. You can do that here as a child subquery against the relationship ContentDocumentLinks:

List<SBQQ__QuoteDocument__c> quoteDocList = [SELECT Id, (SELECT Id FROM ContentDocumentLinks) FROM SBQQ__QuoteDocument__c WHERE Id IN :parentQuoteDocumentIds];

Then, iterate over the documents and set each one's Number_of_Files_Attached__c field to qd.ContentDocumentLinks.size().

And lastly, don't forget that you have to perform DML to persist these changes:

update quoteDocList;
2
  • Thank you for your help David, I am still not quite able to get this to deploy so I can start adding your recommendations, I keep getting Invalid loop variable type expected SObject was ContentDocumentLink in the for(ContentDocumentLink cdl : Trigger.new) part, do I have something wrong with my trigger perhaps? I will update it above. Jul 13, 2020 at 16:29
  • 2
    Cast Trigger.new: (List<ContentDocumentLink>)Trigger.new
    – David Reed
    Jul 13, 2020 at 16:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .