function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion

Need help finishing a trigger that runs on the attachment object

I'm a newb, but I got some great help at Dreamforce this week on some code I'm trying to implement. Here's the deal: when an image-type Attachment is added to an Asset, I want to set the custom picklist field on the Asset called Picture_Type__c to "Specific Picture". I got that working, but now of course I need the reverse, too, which is that if all image-type Attachments are removed from an Asset, I want to set Picture_Type__c back to empty. Here's the code I have so far:



trigger ImageOnAsset on Attachment (after insert, after undelete, after delete) { Set<Id> assetIdSet = new Set<Id>(); if (Trigger.isDelete) { List<Attachment> attachmentParents = [SELECT ParentId FROM Attachment Where Id IN :Trigger.old]; Map<Id,Set<Id>> assetImages = new Map<Id,Set<Id>>(); for (Attachment attachment : attachmentParents) { assetImages.put(attachment.ParentId, new Set<Id>()); } for (Attachment attachment : [SELECT Id, ParentId, ContentType FROM Attachment WHERE ParentId IN :assetImages.keySet()]) { if(attachment.contentType != null && attachment .contentType.startswith('image')) { assetImages.get(attachment.ParentId).add(attachment.Id); } } // for (Map.Entry<Id,Set<Id>> assetImage : assetImages) { // todo: here's where I need help! // } For(Attachment oldAttachment : Trigger.old) { if(oldAttachment.contentType != null && oldAttachment.contentType.startswith('image')) assetIdSet.add(oldAttachment.ParentId); } } else { For(Attachment newAttachment :{ if(newAttachment.contentType != null && newAttachment.contentType.startswith('image')) assetIdSet.add(newAttachment.ParentId); } } if(!assetIdSet.isEmpty()) { For(Asset ass : [SELECT Asset.Id, Asset.Picture_Type__c FROM Asset where Id IN :assetIdSet]){ ass.Picture_Type__c = 'Specific Picture'; update ass; } } }


 In addition to the "here's where I need help", I also am not sure where/how I can say "only do this if the parent object is an Asset.








The way I do it is to build a map of the attachment IDs and lists of Assets.  You have to include the Picture_Type__c field in the asset select list.  You use nested for loops to build this map:



List<Asset> allAssets = new List<Asset>();
List<Asset> tmpAssets = new List<Asset>();
Map<Id, List<Asset> assetImages = new Map<Id, List<Asset>()>();

allAssets = [select Id, Picture_Type__c from Asset where Id in : Trigger.old.keyset()];
for(Attachment attach : Trigger.old) {
for(Asset loopAsset : allAssets) {
if(attach.parentID == loopAsset.ID) {
assetImages.put(attach.Id, tmpAssets);




Once you have a map of the lists, then you can modify the contents of the lists with a sequential for loop, getting from the map, and assigning a value.


After you change a specific asset record in the map, you add it to a separate list of assets during the loop.  You will use  this separate list in the INSERT at the end.



for(integer i = 0; i < 10, i++) {
[attachment loop]

[conditional statements]

assetImages.get(attach.ID)[i].Picture_Type__c = "your value";
resultAssets = assetImages.get(attach.ID)[i];


 There may be better ways of doing this, but this the only way I could figure out.


Message Edited by dmcheng on 11-22-2009 04:31 PM
Message Edited by dmcheng on 11-22-2009 04:33 PM

First, thank you so much for your help - I really appreciate it! Second, sorry I didn't get back to you - pesky holidays and family got in the way. I was just trying to add your code in, and I tried putting it a few places and it never worked - I got a "Compile Error: unexpected token: 'Map'" regarding the third line of code (starting with "Map<Id). Did you mean for this code to replace existing code, or be inserted at particular points?


Sorry, I was working away from my main computer and got the loop relationships totally screwed up.


Here is code that should work.  It should replace all of your trigger code. It should work for insert / update / delete.



Set<Id> assetIDs = Set<Id>;
Asset[] allAssets = new List<Asset>();
Asset[] tmpAssets = new List<Asset>();
Attachments[] allAttachments = new List<Attachment>();
Attachments[] tmpAttachments = new List<Attachment>();
Map<Id, Asset[]> assetImages = new Map<Id, List<Asset>()>();

"your code here to get all asset IDs from attachments in Trigger"

//Get the assets, then the appropriate attachments for the assets.
allAssets = [select Id, Picture_Type__c from Asset where Id in : assetIDs];
allAttachments = [select Id, ParentId, ContentType from Attachment where ParentId in : assetIDs and "your ContentType conditions here"];

//Map each asset ID to a list of its attachments
for(Asset loopAsset : allAssets) {
for(Attachment attach : allAttachments) {
if(attach.parentID == loopAsset.ID) {
assetImages.put(asset.Id, tmpAttachments);

Integer flag;

//Loop through the list for each asset ID.
//If there are items in a list, then run a traditional for loop.
for(Asset loopAsset : allAssets) {
//Flag = 1 if there is an image attachment.
flag = 0;
//The map.get() method returns the list of attachments for the asset.
if(assetImages.get(loopAsset.Id).size() > 0) {
for(Integer i = 0; i < assetImages.get(loopAsset.Id).size(); i++) {
//We use i to go through the attachment list array. If condition matches, set flag = 1.
if(assetImages.get(loopAsset.Id)[i].ContentType == "your condition here") {
flag = 1;
if(flag == 0) {
loopAsset.Picture_Type__c = "Your value";
} else {
loopAsset.Picture_Type__c = "Your other value";

//Update the asset records.
update allAssets;


Remember these points:

* Lists are arrays.

* Get method on a map<ID, List<>> will return the List object for the ID.

* Use a traditional for loop to go through an array and process each item in the array.



Message Edited by dmcheng on 11-28-2009 12:53 PM
Message Edited by dmcheng on 11-28-2009 12:53 PM
Message Edited by dmcheng on 11-28-2009 12:55 PM