Using dynamic datasources in a cfml ORM system can be hard. I am working on some project full of cform, and as mentioned in my post on dynamic datasources in qb I have a project with a lot of legacy code, full of cfquery, queryExecute, some qb queries and… tons of cform entities. And for all this code I should be able to change my datasource on the fly, based on URL or the authenticated user. I already had all solutions in place for qb and queryExecute, but cform is a showstopper. Back in CF9 when cform was introduced you could only work with ONE datasource, the default datasource. Since default datasources where introduced in CF9 I suspect this was only done to accommodate for the lack of datasource awareness of the cf9.0 orm entities. In CF9.01 this became slightly better when we could specify some datasource property, but it has always been a pain to get this right. And unfortunately we often worked with a multidatasource setup. When we switched from Lucee 4 to 5 it took several years before a very important multi datasource bug was fixed. And now we even needed dynamic datasources, which is a real NO! in cform. So how about quick?

quick was once the new kid on the block in cfml ORM land, and the first thing I was looking for was multi-datasource support. Since it was not there initially, I wrote a pull request and with a lot of help by Eric Peterson we managed to make it datasource aware. We did this by creating some datasource annotation on the quick entities, but these are quite static. So we needed something smarter. Finally we found several different ways to do it.

Option 1: instanceReady()

‌I create some quick base object:

component displayname="DynamicQuickBase" extends="quick.models.BaseEntity" {

function instanceReady() {
  var thisDataSource = 
  variables._wirebox
    .getInstance("coldbox:requestContext" )
    .getPrivateValue( "mainDatasource","" );
  if ( len( thisDataSource) ){
	variables._queryoptions["datasource"] = thisDataSource;
   variables._builder.setDefaultOptions( 
    variables._queryoptions );
	}
}
}

‌In this case I use the instanceReady() lifecycle method which fires AFTER an object is created, but before data is loaded from the database. We get the datasource from the private request collection, and only take action if it is set. If there is a value, we change the _queryoptions on the builder object. Unfortunately this is not enough, because the _queryoptions will not be read again after instanceReadyfires, so we explicitly have to set the default options on the _builder object again. Once that’s done, your quick object is ready to query your dynamic datasource. So every quick object which inherits from our custom base object can use dynamic datasources

Option 2: overriding the newQuery() method

‌The newQuery method reads the default options, and prepares a query object. We create a base object again and this time change the newQuery method:

component displayname="DynamicQuickBase" extends="quick.models.BaseEntity" {

/** 
* Configures a new query builder and returns it.
* We modify it to get a dynamic datasource
* @return  quick.models.QuickBuilder
*/
public any function newQuery() {
  var thisDataSource = variables._wirebox
    .getInstance( "coldbox:requestContext" )
    .getPrivateValue( "customerDatasource","" );
  if ( len( thisDataSource) ){
    variables._queryoptions["datasource"] = thisDataSource;
  }
  return super.newQuery();
}
}

‌In this scenario we read the dynamic datasource value again, and modify the _queryoptions to set a datasource. Once this is ready we can just call the newQuery() method of the parent object. Again, if your quick object inherits from this base, your quick object can handle the dynamic datasources.

Option 3: intercepting qb execution

‌The third option might look simpler, but offers you less control. If you read my previous post, you might know that interceptors can offer you some extra power. Quick always queries the database, and just before it does this it announces a preQBExecute interception. By creating and registering an interceptor you can change your datasource just in time. The interceptor can look like this:

component {
  //QBDynamicDataSourceInterceptor
  void function configure(){
  }
  /**
   * preQBExecute
   */
  function preQBExecute( event, data, buffer, rc, prc ){
  var thisDataSource = event
    .getPrivateValue( "mainDatasource","" );
  if ( len( thisDataSource) && 
      !data.options.keyExists("dataSource") ){
	data.options["dataSource"] = thisDataSource;
  }
}
}	

‌So in this case you don’t need your own base object (but you might need it anyway for other modifications). Beware though, this interceptor changes your datasource for ALL qb requests, no matter if you use custom base object of even if it is a regular qb query. So if qb already had some default datasource, that will also be changed. We added an extra check, so this interceptor is not touching your qb query if a datasource was already defined. You can register your interceptor in the coldbox config like this

interceptors = [
  {
	class      : 
      "interceptors.QbDynamicDatasourceInterceptor"
  },
  //.. more

Conclusion

Let me start by saying I like the flexibility of Quick and all valuable feedback by Eric Peterson. Actually creating this dynamic datasource orm stuff was a lot easier than I expected.
‌Option 2 is the most specific option you can use, and makes most sense if you see what happens in the newQuery method of quick.models.BaseEntity. Option 3 is less specific and might change your datasource in unwanted places. But if you know what you are doing it can be a very powerful way to add this dynamic datasource capability to quick.