2525**/
2626
2727/**
28- * QueryFactor provides an object-oriented way of building SOQL queries without resorting to string manipulation.
28+ * QueryFactory provides an object-oriented way of building SOQL queries without resorting to string manipulation.
2929 * This class is not meant to be used as a replacement for all SOQL queries, and due to the relativley high overhead in both CPU and describe calls
3030 * should be used in places where highly dynamic queries, such as those that include field sets or are mutated heavilly
3131 * in multiple locations are a good fit for use with fflib_QueryFactory.
3636 * Currently the WHERE clause of the query is manipulated as a single string, and is decidedly less OO-styled than other methods.
3737 * This is expected to be expanded upon in the future.
3838 *
39- * To include one or more sort expression (s), use one of the addOrdering methods. If not specified, the "NULLS FIRST" keywords
40- * will be included by default.
39+ * To include one or more ORDER BY clause (s), use one of the addOrdering methods. If not specified, the "NULLS FIRST" keywords
40+ * will be included by default. Constructing Ordering instances manually is discouraged.
4141 *
42- * Subselect Queries are supported with the subselectQuery method .
42+ * Subselect Queries are supported with the subselectQuery methods .
4343 * More than one sub-query can be added to a single query, but sub-queries can only be 1 level deep.
4444 * An exception will thrown from the subselectQuery method when there is an attempt to add a subquery to a sub-query
4545 * or to add a subquery to a query with an invalid relationship.
@@ -66,11 +66,6 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
6666 private Integer limitCount ;
6767 private Integer offset ;
6868 private List <Ordering > order ;
69- /**
70- * each item in sortExpressions contains the field and the direction (ascending or descending)
71- * use the addOrdering method to add fields to sort by. the sort fields
72- * appear in the SOQL query in the order they are added to the query.
73- **/
7469 /**
7570 /* Integrate checking for READ Field Level Security within the selectField(s) methods
7671 /* This can optionally be enforced (or not) by calling the setEnforceFLS method prior to calling
@@ -327,24 +322,74 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
327322 /**
328323 * Add a subquery query to this query. If a subquery for this relationship already exists, it will be returned.
329324 * If not, a new one will be created and returned.
325+ * @deprecated Replaced by {@link #subselectQuery(String relationshipName)} and {@link #subselectQuery(ChildRelationship relationship)}
330326 * @exception InvalidSubqueryRelationshipException If this method is called on a subselectQuery or with an invalid relationship
331327 * @param related The related object type
332328 **/
333329 public fflib_QueryFactory subselectQuery (SObjectType related ){
330+ System .debug (LoggingLevel .WARN , ' fflib_QueryFactory.subselectQuery(Schema.SObjectType) is deprecated and will be removed in a future release. Use fflib_QueryFactory.subselectQuery(String) or fflib_QueryFactory.subselectQuery(ChildRelationship) instead.' );
334331 return setSubselectQuery (getChildRelationship (related ), false );
335332 }
336333
337334 /**
338335 * Add a subquery query to this query. If a subquery for this relationship already exists, it will be returned.
339336 * If not, a new one will be created and returned.
337+ * @deprecated Replaced by {@link #subselectQuery(String relationshipName, Boolean assertIsAccessible)} and {@link #subselectQuery(ChildRelationship relationship, Boolean assertIsAccessible)}
340338 * @exception InvalidSubqueryRelationshipException If this method is called on a subselectQuery or with an invalid relationship
341339 * @param related The related object type
342340 * @param assertIsAccessible indicates whether to check if the user has access to the subquery object
343341 **/
344- public fflib_QueryFactory subselectQuery (SObjectType related , Boolean assertIsAccessible ){
342+ public fflib_QueryFactory subselectQuery (SObjectType related , Boolean assertIsAccessible ){
343+ System .debug (LoggingLevel .WARN , ' fflib_QueryFactory.subselectQuery(Schema.SObjectType, Boolean) is deprecated and will be removed in a future release. Use fflib_QueryFactory.subselectQuery(String, Boolean) or fflib_QueryFactory.subselectQuery(ChildRelationship, Boolean) instead.' );
345344 return setSubselectQuery (getChildRelationship (related ), assertIsAccessible );
346345 }
347346
347+ /**
348+ * Add a subquery query to this query. If a subquery for this relationshipName already exists, it will be returned.
349+ * If not, a new one will be created and returned.
350+ * @exception InvalidSubqueryRelationshipException If this method is called on a subselectQuery or with an invalid relationship
351+ * @param relationshipName The relationshipName to be added as a subquery
352+ **/
353+ public fflib_QueryFactory subselectQuery (String relationshipName ){
354+ return subselectQuery (relationshipName , false );
355+ }
356+
357+ /**
358+ * Add a subquery query to this query. If a subquery for this relationship already exists, it will be returned.
359+ * If not, a new one will be created and returned.
360+ * @exception InvalidSubqueryRelationshipException If this method is called on a subselectQuery or with an invalid relationship
361+ * @param relationshipName The relationshipName to be added as a subquery
362+ * @param assertIsAccessible indicates whether to check if the user has access to the subquery object
363+ **/
364+ public fflib_QueryFactory subselectQuery (String relationshipName , Boolean assertIsAccessible ){
365+ ChildRelationship relationship = getChildRelationship (relationshipName );
366+ if (relationship != null ) {
367+ return setSubselectQuery (relationship , assertIsAccessible );
368+ }
369+ throw new InvalidSubqueryRelationshipException (' Invalid call to subselectQuery with relationshipName = ' + relationshipName + ' . Relationship does not exist for ' + table .getDescribe ().getName ());
370+ }
371+
372+ /**
373+ * Add a subquery query to this query. If a subquery for this relationshipName already exists, it will be returned.
374+ * If not, a new one will be created and returned.
375+ * @exception InvalidSubqueryRelationshipException If this method is called on a subselectQuery or with an invalid relationship
376+ * @param relationship The ChildRelationship to be added as a subquery
377+ **/
378+ public fflib_QueryFactory subselectQuery (ChildRelationship relationship ){
379+ return subselectQuery (relationship , false );
380+ }
381+
382+ /**
383+ * Add a subquery query to this query. If a subquery for this relationship already exists, it will be returned.
384+ * If not, a new one will be created and returned.
385+ * @exception InvalidSubqueryRelationshipException If this method is called on a subselectQuery or with an invalid relationship
386+ * @param relationship The ChildRelationship to be added as a subquery
387+ * @param assertIsAccessible indicates whether to check if the user has access to the subquery object
388+ **/
389+ public fflib_QueryFactory subselectQuery (ChildRelationship relationship , Boolean assertIsAccessible ){
390+ return setSubselectQuery (relationship , assertIsAccessible );
391+ }
392+
348393 /**
349394 * Add a subquery query to this query. If a subquery for this relationship already exists, it will be returned.
350395 * If not, a new one will be created and returned.
@@ -393,6 +438,19 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
393438 throw new InvalidSubqueryRelationshipException (' Invalid call to subselectQuery. Invalid relationship for table ' + table + ' and objtype=' + objType );
394439 }
395440
441+ /**
442+ * Get the ChildRelationship from the Table for the relationship name passed in.
443+ * @param relationshipName The name of the object's ChildRelationship on get
444+ **/
445+ private Schema.ChildRelationship getChildRelationship (String relationshipName ){
446+ for (Schema .ChildRelationship childRow : table .getDescribe ().getChildRelationships ()){
447+ if (childRow .getRelationshipName () == relationshipName ){
448+ return childRow ;
449+ }
450+ }
451+ return null ;
452+ }
453+
396454 /**
397455 * Add a field to be sorted on. This may be a direct field or a field
398456 * related through an object lookup or master-detail relationship.
@@ -504,6 +562,33 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
504562 result += ' LIMIT ' + limitCount ;
505563 return result ;
506564 }
565+
566+ /**
567+ * Create a "deep" clone of this object that can be safely mutated without affecting the cloned instance
568+ * @return a deep clone of this fflib_QueryFactory
569+ **/
570+ public fflib_QueryFactory deepClone (){
571+
572+ fflib_QueryFactory clone = new fflib_QueryFactory (this .table )
573+ .setLimit (this .limitCount )
574+ .setCondition (this .conditionExpression )
575+ .setEnforceFLS (this .enforceFLS );
576+
577+ Map <Schema .ChildRelationship , fflib_QueryFactory > subqueries = this .subselectQueryMap ;
578+ if (subqueries != null ) {
579+ Map <Schema .ChildRelationship , fflib_QueryFactory > clonedSubqueries = new Map <Schema .ChildRelationship , fflib_QueryFactory >();
580+ for (Schema .ChildRelationship key : subqueries .keySet ()) {
581+ clonedSubqueries .put (key , subqueries .get (key ).deepClone ());
582+ }
583+ clone .subselectQueryMap = clonedSubqueries ;
584+ }
585+
586+ clone .relationship = this .relationship ;
587+ clone .order = this .order .clone ();
588+ clone .fields = this .fields .clone ();
589+
590+ return clone ;
591+ }
507592
508593 public class Ordering {
509594 private SortOrder direction ;
0 commit comments