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
PaxPax 

Apex trigger for junction object

Hi and thanks in advance for your help! I've been working on an apex trigger for a junction object that has two custom master-detail fields, but I'm stuck and could use some help.

I have created a junction object to create a connection between two Accounts (1st: Account object; 2nd: Connected_Account custom object). Connected_Account__c functions as a one-to-one relationship with Account (every Account has itself as a Connected_Account)

junction object:
Connection__c
- Connection__c.Name : auto number {0}
- Account1__c : master-detail(Account)
- Account2__c : master-detail(Connected_Account__c)

Connected_Account__c
- Connected_Account__c : lookup(Account)

User creates a new Connection__c on the Account Page Layout for Account X:

Connection__c.Name = 1
// Account1 autopopulated, Account1 = X
Account1__c = X
// User selects Account2, Connected_Account = Y
Account2__c = Y

Now, what I'm trying to accomplish is a trigger such that after a User creates Connection__c.Name=1, the trigger creates a second Connection__c.Name=2 where the field values are inverted:

Connection__c.Name = 2
Account1__c = Y
Account2__c = X
 
ConnectionHandler.apxc :

public with sharing class ConnectionHandler {
    public static void CreateNewConnection(List<Connection__c> connects) {
        for(Connection__c c : connects) {
            Connection__c connect = new Connection__c();
            connect.Account1__c = c.Account2__c;
            connect.Account2__c = c.Account1__c;
            connects.add(connect);
        }
        if(connects.size() > 0) {
            insert connects;
        }
    }
}


ConnectionTrigger.apxt :

trigger ConnectionTrigger on Connection__c (before insert, before update, before delete, after insert, after update, after delete, after undelete) {
	if(Trigger.isAfter && Trigger.isInsert) {
        ConnectionHandler.CreateNewConnection(Trigger.New);
    }
}

Any advice and/or suggestions would be greatly appreciated. Thanks again!
Best Answer chosen by Pax
Naveen IlaNaveen Ila
We cann't add records to the collection(sObjects on which the trigger was invoked) in trigger context. 
 
public static void CreateNewConnection(List<Connection__c> connects) {
        isAfterInsertDone = TRUE
        for(Connection__c c : connects) {
            Connection__c connect = new Connection__c();
            connect.Account1__c = c.Account2__c;
            connect.Account2__c = c.Account1__c;
            connects.add(connect); // here you are adding to the Same collection instead we have to take a new collection
        }
        if(connects.size() > 0) {
            insert connects;
        }
    }

Replace above code with the below one. 


 
public static void CreateNewConnection(List<Connection__c> connects) {
        isAfterInsertDone = TRUE
        List<Connection__c> connectionList = new List<Connection__c>();

        for(Connection__c c : connects) {
            Connection__c connect = new Connection__c();
            connect.Account1__c = c.Account2__c;
            connect.Account2__c = c.Account1__c;
            connectionList .add(connect);
        }
        if(connectionList .size() > 0) {
            insert connectionList;
        }
    }



 

All Answers

Naveen IlaNaveen Ila
I hope, you have recursive trigger issue. Which can be overcome by using static variable. use the same like below(Comment with my name )
 
ConnectionHandler.apxc :

public with sharing class ConnectionHandler {
    

   // Naveen Ila: create a static variable like below 
   public static Boolean isAfterInsertDone = FALSE;

    public static void CreateNewConnection(List<Connection__c> connects) {
        isAfterInsertDone = TRUE
        for(Connection__c c : connects) {
            Connection__c connect = new Connection__c();
            connect.Account1__c = c.Account2__c;
            connect.Account2__c = c.Account1__c;
            connects.add(connect);
        }
        if(connects.size() > 0) {
            insert connects;
        }
    }
}


ConnectionTrigger.apxt :

trigger ConnectionTrigger on Connection__c (before insert, before update, before delete, after insert, after update, after delete, after undelete) {
	if(Trigger.isAfter && Trigger.isInsert  && !ConnectionHandler .isAfterInsertDone ) {
                  
        ConnectionHandler.CreateNewConnection(Trigger.New);
    }
}

 
PaxPax
Thanks for your help and suggestion Naveen! much appreciated.
I've implemented what you suggest, but the trigger returns the error: 
ConnectionTrigger: execution of AfterInsert caused by: System.FinalException: Collection is read-only
What I don't understand is that I'm just trying to insert, not update. So I don't get why I'm getting the error thrown "Collection is read-only".
PaxPax
I found this as a possible explanation:
https://salesforce.stackexchange.com/questions/23922/system-finalexception-record-is-read-only-trigger-updatecompetitors-line-24#23927

"""
This is because you are in an after insert/update trigger and the records are read only in that context as they have been written, but not committed, to the database.

Unfortunately your trigger is relying on the ids of the records, which means you won't be able to use before insert as the ids won't be populated at that time (as the records haven't been written to the database at that point so while you can write to them, database generated fields aren't populated).

In this instance you'll need to clone the record (or query anew via SOQL) and make changes to the new copy of the record, then execute the update against the new copies.
"""

can someone confirm this answer, that this is the best/only route?

thanks!
Naveen IlaNaveen Ila
We cann't add records to the collection(sObjects on which the trigger was invoked) in trigger context. 
 
public static void CreateNewConnection(List<Connection__c> connects) {
        isAfterInsertDone = TRUE
        for(Connection__c c : connects) {
            Connection__c connect = new Connection__c();
            connect.Account1__c = c.Account2__c;
            connect.Account2__c = c.Account1__c;
            connects.add(connect); // here you are adding to the Same collection instead we have to take a new collection
        }
        if(connects.size() > 0) {
            insert connects;
        }
    }

Replace above code with the below one. 


 
public static void CreateNewConnection(List<Connection__c> connects) {
        isAfterInsertDone = TRUE
        List<Connection__c> connectionList = new List<Connection__c>();

        for(Connection__c c : connects) {
            Connection__c connect = new Connection__c();
            connect.Account1__c = c.Account2__c;
            connect.Account2__c = c.Account1__c;
            connectionList .add(connect);
        }
        if(connectionList .size() > 0) {
            insert connectionList;
        }
    }



 
This was selected as the best answer