多対多リレーションの重複禁止 Apexトリガーでの対応

元の質問 : [多対多リレーションの重複禁止](https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000JdMxm)

```
trigger BuyerCandidateTrigger on BuyerCandidate__c (before insert , before update) {
    //https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000JdMxmSAF
    //A主：Opportunity A従：BuyerCandidate__c B主：Account
    
    //Set<Opportunity__c> productIds = new Set<Opportunity__c>();
    //List<BuyerCandidate__c> ls = [SELECT CompanyName__c, Id, Name, Opportunity__c
    //FROM BuyerCandidate__c
    //WHERE Opportunity__c = :productIds]
    //Map<Id, BuyerCandidate__c> m = new Map<Id, BuyerCandidate__c>(ls);

    Map<String,BuyerCandidate__c> checkMap = new Map<String,BuyerCandidate__c>();
    Set<Id>  oppIdSet = new Set<Id>();
    Boolean sw01 = true;
    for (BuyerCandidate__c bc : Trigger.new){
        String keyStringO = bc.Opportunity__c ;
        String keyStringA = bc.Account__c;
        String keyString = keyStringO + ':' + keyStringA;
        BuyerCandidate__c keyObj = (BuyerCandidate__c) checkMap.Get(keyString);
        if (keyObj == null) {
            //同じものはない
            checkMap.put(keyString,bc);
            oppIdSet.add(bc.Opportunity__c);
        } else {
            //同じものがある
            bc.addError('Error 1 : Name ' + bc.Name + 'で選択されている商談Id' + bc.Opportunity__c + 'と取引先Id' +bc.Account__c + 'が' + keyObj.Name + 'と重複しています。');
            sw01 = false;
        }
        
    }//end of for loop
    
    if(sw01 ) {
        List<BuyerCandidate__c> bcList = [SELECT Id,Name,Opportunity__c,Account__c FROM BuyerCandidate__c WHERE Opportunity__c =: oppIdSet];
        for (BuyerCandidate__c bc : bcList){
            String keyStringO = bc.Opportunity__c ;
            String keyStringA = bc.Account__c;
            String keyString = keyStringO + ':' + keyStringA;
            BuyerCandidate__c keyObj = (BuyerCandidate__c) checkMap.Get(keyString);
            if (keyObj == null) {
                //同じものはない            
            } else {
                //同じものがある
                bc.addError('Error 2 : Name ' + keyObj.Name + 'で選択されている商談Id' + keyObj.Opportunity__c + 'と取引先Id' +keyObj.Account__c + 'が' + bc.Name + 'と重複しています。');
            }
        }//end of for loop
    }
    
}
```

テストクラス

```
@isTest
public class BuyerCandidateTrigger_test {
    
    @isTest
    public static void test01() {
        //既に入力したデータに同じものがある。
        Test.startTest();
        
        Account acc = new Account();
        acc.name = 'test';
        insert acc;
        
        Opportunity opp1 = new Opportunity();
        opp1.Name = 'test 001';
        opp1.StageName = 'Qualification'; 
        opp1.CloseDate = Date.Today() + 10;
        opp1.AccountId = acc.Id;
        insert opp1;
        
		BuyerCandidate__c bc1 = new BuyerCandidate__c();
		bc1.Opportunity__c = opp1.Id;
		bc1.Account__c = acc.Id;
		insert bc1;        
        
        try {
            BuyerCandidate__c bc2 = new BuyerCandidate__c();
			bc2.Opportunity__c = opp1.Id;
			bc2.Account__c = acc.Id;
		    insert bc2;        
        } catch (Exception e) {
            System.debug('addError');
        }
        
        
        Test.stopTest();
        
        
    }
    
    @isTest
    public static void test02() {
        //同時に入力したデータに同じものがある。
        Test.startTest();
        
        Account acc = new Account();
        acc.name = 'test';
        insert acc;
        
        Opportunity opp1 = new Opportunity();
        opp1.Name = 'test 001';
        opp1.StageName = 'Qualification'; 
        opp1.CloseDate = Date.Today() + 10;
        opp1.AccountId = acc.Id;
        insert opp1;
        
		List<BuyerCandidate__c> bcList = new List<BuyerCandidate__c>();
        
		BuyerCandidate__c bc1 = new BuyerCandidate__c();
		bc1.Opportunity__c = opp1.Id;
		bc1.Account__c = acc.Id;
		bcList.add( bc1);    
        
        BuyerCandidate__c bc2 = new BuyerCandidate__c();
		bc2.Opportunity__c = opp1.Id;
		bc2.Account__c = acc.Id;
		bcList.add( bc2);    
        
        try {
          insert bcList;
        } catch (Exception e) {
            System.debug('addError');
        }
        
        
        Test.stopTest();
        
        
    }
    
    @isTest
    public static void test03() {
        //重複なし
        Test.startTest();
        
        Account acc = new Account();
        acc.name = 'test';
        insert acc;
            
        
        Opportunity opp1 = new Opportunity();
        opp1.Name = 'test 001';
        opp1.StageName = 'Qualification'; 
        opp1.CloseDate = Date.Today() + 10;
        opp1.AccountId = acc.Id;
        insert opp1;
        
        Opportunity opp2 = new Opportunity();
        opp2.Name = 'test 002';
        opp2.StageName = 'Qualification'; 
        opp2.CloseDate = Date.Today() + 10;
        opp2.AccountId = acc.Id;
        insert opp2;
        
		BuyerCandidate__c bc1 = new BuyerCandidate__c();
		bc1.Opportunity__c = opp1.Id;
		bc1.Account__c = acc.Id;
		insert bc1;        
        
        try {
            BuyerCandidate__c bc2 = new BuyerCandidate__c();
			bc2.Opportunity__c = opp2.Id;
			bc2.Account__c = acc.Id;
		    insert bc2;        
        } catch (Exception e) {
            System.debug('addError');
        }
        
        
        Test.stopTest();
        
        
    }

}
```