Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,35 @@ component {

Now that you've seen an example, [dig in to what you can do](https://quick.ortusbooks.com/) with Quick!

### Caching queries

Quick passes query options through to `queryExecute`, so applications can use the query cache provided by their CFML engine. This works with collection queries and primary-key lookups:

```javascript
var users = getInstance( "User" ).get(
options = { cachedWithin : createTimeSpan( 0, 0, 5, 0 ) }
);

var user = getInstance( "User" ).find(
rc.id,
{ cachedWithin : createTimeSpan( 0, 0, 5, 0 ) }
);
```

An entity can also configure defaults for every query by assigning `_queryOptions` in its pseudo-constructor:

```javascript
component extends="quick.models.BaseEntity" {

variables._queryOptions = {
cachedWithin : createTimeSpan( 0, 0, 5, 0 )
};

}
```

Query caching stores database results, not live Quick entities or loaded relationships. Cache lifetime and invalidation are managed by the CFML engine, so use short lifetimes for data that Quick or another process may update. For application-specific invalidation or distributed caching, cache entity mementos in CacheBox at the service layer and rehydrate them through Quick's public APIs.

### Tests and Contributing

To run the tests, first clone this repo and run a `box install`.
Expand Down
2 changes: 1 addition & 1 deletion models/QuickBuilder.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,7 @@ component accessors="true" transientCache="false" {
idQuery.where( allKeyNames[ i ], arguments.id[ i ] );
}
variables.qb.addNestedWhereQuery( idQuery, "and" );
return this.first();
return this.first( arguments.options );
}

/**
Expand Down
36 changes: 36 additions & 0 deletions tests/specs/integration/BaseEntity/GetSpec.cfc
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
component extends="tests.resources.ModuleIntegrationSpec" {

function beforeAll() {
super.beforeAll();
controller
.getInterceptorService()
.registerInterceptor( interceptorObject = this, interceptorName = "BaseEntityGetSpec" );
}

function afterAll() {
controller.getInterceptorService().unregister( "BaseEntityGetSpec" );
super.afterAll();
}

function run() {
describe( "Get Spec", function() {
it( "finds an entity by the primary key", function() {
var user = getInstance( "User" ).find( 1 );
expect( user.isLoaded() ).toBeTrue( "The user instance should be found and loaded, but was not." );
} );

it( "passes query options when finding an entity by primary key", function() {
structDelete( request, "baseEntityGetSpecPreQBExecute" );

var user = getInstance( "User" ).find( 1, { datasource : "quick" } );
var executionsWithDatasource = request.baseEntityGetSpecPreQBExecute.filter( function( execution ) {
return execution.options.keyExists( "datasource" );
} );

expect( user ).notToBeNull();
expect( executionsWithDatasource.len() ).toBeGT( 0 );
expect( executionsWithDatasource[ 1 ].options.datasource ).toBe( "quick" );
} );

it( "returns null if the record cannot be found", function() {
expect( getInstance( "User" ).find( 999 ) ).toBeNull(
"The user instance should be null because it could not be found, but was not."
Expand Down Expand Up @@ -438,4 +463,15 @@ component extends="tests.resources.ModuleIntegrationSpec" {
} );
}

function preQBExecute(
event,
interceptData,
buffer,
rc,
prc
) {
param request.baseEntityGetSpecPreQBExecute = [];
request.baseEntityGetSpecPreQBExecute.append( duplicate( arguments.interceptData ) );
}

}
Loading