|  | 
| 1 |  | -public virtual class Soql implements Comparable { | 
|  | 1 | +public class Soql implements Comparable { | 
| 2 | 2 | 
 | 
| 3 | 3 |     public enum Aggregate { AVG, COUNT, COUNT_DISTINCT, MAX, MIN, SUM } | 
| 4 |  | -    public enum FieldCategory { ACCESSIBLE, UPDATEABLE, STANDARD, CUSTOM } | 
|  | 4 | +    public enum FieldCategory { ACCESSIBLE, UPDATEABLE, STANDARD, CUSTOM, IGNORE_FLS } | 
| 5 | 5 |     public enum Scope { EVERYTHING, DELEGATED, TEAM, MINE, MY_TERRITORY, MY_TEAM_TERRITORY } | 
| 6 | 6 |     public enum SortOrder { ASCENDING, DESCENDING } | 
| 7 | 7 | 
 | 
| @@ -146,8 +146,15 @@ public virtual class Soql implements Comparable { | 
| 146 | 146 |     } | 
| 147 | 147 | 
 | 
| 148 | 148 |     public Soql filterWhere(Soql.QueryField queryField, String operator, Object value) { | 
| 149 |  | -        String whereFilter = queryField + ' ' + String.escapeSingleQuotes(operator) + ' ' + this.formatObjectForQueryString(value); | 
| 150 |  | -        this.whereFilters.add(whereFilter); | 
|  | 149 | +        return this.filterWhere(new QueryFilter(queryField, operator, value)); | 
|  | 150 | +    } | 
|  | 151 | + | 
|  | 152 | +    public Soql filterWhere(QueryFilter filter) { | 
|  | 153 | +        return this.filterWhere(new List<QueryFilter>{filter}); | 
|  | 154 | +    } | 
|  | 155 | + | 
|  | 156 | +    public Soql filterWhere(List<QueryFilter> filters) { | 
|  | 157 | +        for(QueryFilter filter : filters) this.whereFilters.add(filter.toString()); | 
| 151 | 158 |         return this.setHasChanged(); | 
| 152 | 159 |     } | 
| 153 | 160 | 
 | 
| @@ -326,60 +333,8 @@ public virtual class Soql implements Comparable { | 
| 326 | 333 |         return 'format(' + fieldApiName + ') ' + fieldApiName.replace('.', '_') + '__Formatted'; | 
| 327 | 334 |     } | 
| 328 | 335 | 
 | 
| 329 |  | -    private String formatObjectForQueryString(Object valueToFormat) { | 
| 330 |  | -        if(valueToFormat == null) return null; | 
| 331 |  | -        else if(valueToFormat instanceOf List<Object>) return this.convertListToQueryString((List<Object>)valueToFormat); | 
| 332 |  | -        else if(valueToFormat instanceOf Set<Object>) return this.convertSetToQueryString(valueToFormat); | 
| 333 |  | -        else if(valueToFormat instanceOf Map<Object, Object>) return this.convertMapToQueryString(valueToFormat); | 
| 334 |  | -        else if(valueToFormat instanceOf Date) return String.valueOf((Date)valueToFormat).left(10); | 
| 335 |  | -        else if(valueToFormat instanceOf Datetime) { | 
| 336 |  | -            Datetime datetimeValue = (Datetime)valueToFormat; | 
| 337 |  | -            return datetimeValue.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'', 'Greenwich Mean Time'); | 
| 338 |  | -        } | 
| 339 |  | -        else if(valueToFormat instanceOf Sobject) { | 
| 340 |  | -            Sobject record = (Sobject)valueToFormat; | 
| 341 |  | -            return this.wrapInSingleQuotes(((Sobject)valueToFormat).Id); | 
| 342 |  | -        } | 
| 343 |  | -        else if(valueToFormat instanceOf String) { | 
| 344 |  | -            // Escape single quotes to prevent SOQL/SOSL injection | 
| 345 |  | -            String stringArgument = String.escapeSingleQuotes((String)valueToFormat); | 
| 346 |  | -            return this.wrapInSingleQuotes(stringArgument); | 
| 347 |  | -        } | 
| 348 |  | -        else return String.valueOf(valueToFormat); | 
| 349 |  | -    } | 
| 350 |  | - | 
| 351 |  | -    private String wrapInSingleQuotes(String input) { | 
| 352 |  | -        input = input.trim(); | 
| 353 |  | -        if(input.left(1) != '\'') input = '\'' + input; | 
| 354 |  | -        if(input.right(1) != '\'') input = input + '\''; | 
| 355 |  | -        return input; | 
| 356 |  | -    } | 
| 357 |  | - | 
| 358 |  | -    private String convertListToQueryString(List<Object> valueList) { | 
| 359 |  | -        List<String> parsedValueList = new List<String>(); | 
| 360 |  | -        for(Object value : valueList) { | 
| 361 |  | -            parsedValueList.add(this.formatObjectForQueryString(value)); | 
| 362 |  | -        } | 
| 363 |  | -        return '(' + String.join(parsedValueList, ', ') + ')'; | 
| 364 |  | -    } | 
| 365 |  | - | 
| 366 |  | -    private String convertSetToQueryString(Object valueSet) { | 
| 367 |  | -        String unformattedString = String.valueOf(valueSet).replace('{', '').replace('}', ''); | 
| 368 |  | -        List<String> parsedValueList = new List<String>(); | 
| 369 |  | -        for(String collectionItem : unformattedString.split(',')) { | 
| 370 |  | -            parsedValueList.add(this.formatObjectForQueryString(collectionItem)); | 
| 371 |  | -        } | 
| 372 |  | -        return '(' + String.join(parsedValueList, ', ') + ')'; | 
| 373 |  | -    } | 
| 374 |  | - | 
| 375 |  | -    private String convertMapToQueryString(Object valueMap) { | 
| 376 |  | -        Map<String, Object> m = (Map<String, Object>)JSON.deserializeUntyped(JSON.serialize(valueMap)); | 
| 377 |  | -        return this.convertSetToQueryString(m.keySet()); | 
| 378 |  | -    } | 
| 379 |  | - | 
| 380 | 336 |     private List<String> getFieldsToQuery(QueryField queryField, FieldCategory fieldCat) { | 
| 381 | 337 |         List<String> fieldsToReturn = new List<String>(); | 
| 382 |  | -        //Schema.SobjectField field = this.sobjectDescribe.fields.getMap().get(fieldApiName); | 
| 383 | 338 | 
 | 
| 384 | 339 |         if(fieldCat == null) return fieldsToReturn; | 
| 385 | 340 |         else if(fieldCat == FieldCategory.ACCESSIBLE && !queryField.getDescribe().isAccessible()) return fieldsToReturn; | 
| @@ -545,6 +500,71 @@ public virtual class Soql implements Comparable { | 
| 545 | 500 | 
 | 
| 546 | 501 |     } | 
| 547 | 502 | 
 | 
|  | 503 | +    public virtual class QueryArgument { | 
|  | 504 | + | 
|  | 505 | +        private String value; | 
|  | 506 | + | 
|  | 507 | +        public QueryArgument(Object valueToFormat) { | 
|  | 508 | +            this.value = this.formatObjectForQueryString(valueToFormat); | 
|  | 509 | +        } | 
|  | 510 | + | 
|  | 511 | +        public override String toString() { | 
|  | 512 | +            return this.value; | 
|  | 513 | +        } | 
|  | 514 | + | 
|  | 515 | +        private String formatObjectForQueryString(Object valueToFormat) { | 
|  | 516 | +            if(valueToFormat == null) return null; | 
|  | 517 | +            else if(valueToFormat instanceOf List<Object>) return this.convertListToQueryString((List<Object>)valueToFormat); | 
|  | 518 | +            else if(valueToFormat instanceOf Set<Object>) return this.convertSetToQueryString(valueToFormat); | 
|  | 519 | +            else if(valueToFormat instanceOf Map<Object, Object>) return this.convertMapToQueryString(valueToFormat); | 
|  | 520 | +            else if(valueToFormat instanceOf Date) return String.valueOf((Date)valueToFormat).left(10); | 
|  | 521 | +            else if(valueToFormat instanceOf Datetime) { | 
|  | 522 | +                Datetime datetimeValue = (Datetime)valueToFormat; | 
|  | 523 | +                return datetimeValue.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'', 'Greenwich Mean Time'); | 
|  | 524 | +            } | 
|  | 525 | +            else if(valueToFormat instanceOf Sobject) { | 
|  | 526 | +                Sobject record = (Sobject)valueToFormat; | 
|  | 527 | +                return this.wrapInSingleQuotes(((Sobject)valueToFormat).Id); | 
|  | 528 | +            } | 
|  | 529 | +            else if(valueToFormat instanceOf String) { | 
|  | 530 | +                // Escape single quotes to prevent SOQL/SOSL injection | 
|  | 531 | +                String stringArgument = String.escapeSingleQuotes((String)valueToFormat); | 
|  | 532 | +                return this.wrapInSingleQuotes(stringArgument); | 
|  | 533 | +            } | 
|  | 534 | +            else return String.valueOf(valueToFormat); | 
|  | 535 | +        } | 
|  | 536 | + | 
|  | 537 | +        private String wrapInSingleQuotes(String input) { | 
|  | 538 | +            input = input.trim(); | 
|  | 539 | +            if(input.left(1) != '\'') input = '\'' + input; | 
|  | 540 | +            if(input.right(1) != '\'') input = input + '\''; | 
|  | 541 | +            return input; | 
|  | 542 | +        } | 
|  | 543 | + | 
|  | 544 | +        private String convertListToQueryString(List<Object> valueList) { | 
|  | 545 | +            List<String> parsedValueList = new List<String>(); | 
|  | 546 | +            for(Object value : valueList) { | 
|  | 547 | +                parsedValueList.add(this.formatObjectForQueryString(value)); | 
|  | 548 | +            } | 
|  | 549 | +            return '(' + String.join(parsedValueList, ', ') + ')'; | 
|  | 550 | +        } | 
|  | 551 | + | 
|  | 552 | +        private String convertSetToQueryString(Object valueSet) { | 
|  | 553 | +            String unformattedString = String.valueOf(valueSet).replace('{', '').replace('}', ''); | 
|  | 554 | +            List<String> parsedValueList = new List<String>(); | 
|  | 555 | +            for(String collectionItem : unformattedString.split(',')) { | 
|  | 556 | +                parsedValueList.add(this.formatObjectForQueryString(collectionItem)); | 
|  | 557 | +            } | 
|  | 558 | +            return '(' + String.join(parsedValueList, ', ') + ')'; | 
|  | 559 | +        } | 
|  | 560 | + | 
|  | 561 | +        private String convertMapToQueryString(Object valueMap) { | 
|  | 562 | +            Map<String, Object> m = (Map<String, Object>)JSON.deserializeUntyped(JSON.serialize(valueMap)); | 
|  | 563 | +            return this.convertSetToQueryString(m.keySet()); | 
|  | 564 | +        } | 
|  | 565 | + | 
|  | 566 | +    } | 
|  | 567 | + | 
| 548 | 568 |     public class QueryField { | 
| 549 | 569 | 
 | 
| 550 | 570 |         private final String queryField; | 
| @@ -614,4 +634,29 @@ public virtual class Soql implements Comparable { | 
| 614 | 634 | 
 | 
| 615 | 635 |     } | 
| 616 | 636 | 
 | 
|  | 637 | +    public class QueryFilter implements Comparable { | 
|  | 638 | + | 
|  | 639 | +        private String value; | 
|  | 640 | + | 
|  | 641 | +        public QueryFilter(Schema.SobjectField field, String operator, Object value) { | 
|  | 642 | +            this(new QueryField(field), operator, value); | 
|  | 643 | +        } | 
|  | 644 | + | 
|  | 645 | +        public QueryFilter(QueryField queryField, String operator, Object value) { | 
|  | 646 | +            this.value = queryField + ' ' + operator + ' ' + new QueryArgument(value); | 
|  | 647 | +        } | 
|  | 648 | + | 
|  | 649 | +        public Integer compareTo(Object compareTo) { | 
|  | 650 | +            QueryFilter compareToQueryFilter = (QueryFilter)compareTo; | 
|  | 651 | +            if(this.toString() == compareToQueryFilter.toString()) return 0; | 
|  | 652 | +            else if(this.toString() > compareToQueryFilter.toString()) return 1; | 
|  | 653 | +            else return -1; | 
|  | 654 | +        } | 
|  | 655 | + | 
|  | 656 | +        public override String toString() { | 
|  | 657 | +            return this.value; | 
|  | 658 | +        } | 
|  | 659 | + | 
|  | 660 | +    } | 
|  | 661 | + | 
| 617 | 662 | } | 
0 commit comments