2

I came to the realisation that it isn't difficult to create my own String Interpolation, like this:

public class XAP_UTIL_MapInterpolation implements XAP_UTIL_InterpolationIntf {
    String template;
    Map<String, Object> valueByKeyMap;
    public XAP_UTIL_MapInterpolation(String phrase, Map<String, Object> valueByKeyMap) {
        this.template = phrase;
        this.valueByKeyMap = valueByKeyMap;
    }

    public override String toString() {
        String result = this.template;
        for (String key : this.valueByKeyMap.keySet()) {
            result = result.replace('${' + key + '}', String.valueOf(this.valueByKeyMap.get(key)));
        }
        return result;
    }
}

So I decided to get back to the problem of making a "Where Clause Interface"

But to make this work, I realised we need to move the values out of the Map in the Interpolation object and into the object which does the query, so I came up with this:

public class XAP_DAO_SOQL_Querier {
    private static Object object0;
    private static Object object1;
    private static Object object2;
    private static Object object3;
    private static Object object4;
    private static Object object5;
    private static Object object6;
    private static Object object7;
    private static Object object8;
    private static Object object9;

public List<SObject> selectFor(XAP_DAO_SOQL_QueryInterpolation queryInterpolation) {
    return Database.query(
            this.rebind(queryInterpolation)
    );
}

private String rebind(XAP_DAO_SOQL_QueryInterpolation queryInterpolation) {
    String localizedTemplate = queryInterpolation.template;
    Integer counter = 0;
    for (String key : queryInterpolation.objectsByKeyMap.keySet()) {
        localizedTemplate = this.rebind(
                localizedTemplate,
                key,
                queryInterpolation.objectsByKeyMap.get(key),
                counter++
        );
    }
    return localizedTemplate;
}

private String rebind(String localizedTemplate, String key, Object value, Integer index) {
    switch on index {
        when 0 { object0 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
        when 1 { object1 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
        when 2 { object2 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
        when 3 { object3 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
        when 4 { object4 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
        when 5 { object5 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
        when 6 { object6 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
        when 7 { object7 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
        when 8 { object8 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
        when 9 { object9 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
    }

    throw new RebindObjectsLimitException(
            'XAP_DAO_SOQL_Querier currently only supports ' + LAST_OBJECT_NUMBER + ' bindings.'
                    + '  Attempted query requires ' + index + 1 + ' bindings'
    );
}

// etc.

}

This works, such that when I anonymously execute:

 Id accountId = [
        SELECT Id
        FROM Account
        LIMIT 1
].Id;

Object idSet = new Set<Id>{
                accountId
        };

XAP_DAO_SOQL_QueryInterpolation qi = new XAP_DAO_SOQL_QueryInterpolation(
        'SELECT Name FROM Account WHERE Id IN ${idSet}',
        new Map<String, Object>{
                'idSet' => idSet
        }
);

XAP_DAO_SOQL_Querier querier = new XAP_DAO_SOQL_Querier(); 
List<Account> accountList = querier.selectFor(qi);

System.debug('##' + accountList[0].Name);

I correctly get back

19:00:09.68 (101658693)|USER_DEBUG|[21]|DEBUG|##Edge Communications

But there are a few problems with this approach:

  1. It isn't scalable (enough object and inner classes need to exist before it can be consumed).

  2. The code is very WET (Write Every Time).

Any ideas?

1 Answer 1

1

I haven't found a way to avoid having many variable declarations. But this is solvable by cut and paste up to some hard-coded maximum e.g. 100 rather than needing many classes:

private void bind(Integer i, Object value) {

    Object object0;
    Object object1;
    Object object2;
    ...

    // Set bind variable
    switch on i {
        when 0 { object0 = value; }
        when 1 { object1 = value; }
        when 2 { object3 = value; }
        ...
    }

    // Set bind variable name
    switch on i {
        ...
    }

    ...
}
2
  • I think this is the first time in Apex where I've found switch to be a better solution than combining the Interpreter or Strategy Pattern with the Business Delegation Pattern. :-) Cheers for suggesting it, though we don't actually need two switches (e.g. we can do when 0 { object0 = value; return this.rebind(localizedTemplate, key, 'object' + index);} .... but this is still kind of WET. May 9, 2019 at 5:48
  • 1
    Yeah @BrianKessler, sometimes simple/brute force is all that is left.
    – Keith C
    May 9, 2019 at 7:02

You must log in to answer this question.

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