Skip to content

Commit

Permalink
feat(Scopes): Register global scopes for entities
Browse files Browse the repository at this point in the history
Each entity can define a `applyGlobalScopes` method that
calls one or more scopes on the entity.  These scopes
will be applied to each query method.  Additionally, these
scopes can be ignored once using a `withoutGlobalScope`
method when defining the query.
  • Loading branch information
elpete committed May 3, 2019
1 parent cf13ddd commit 995706b
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 1 deletion.
26 changes: 25 additions & 1 deletion models/BaseEntity.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ component accessors="true" {
property name="_relationshipsLoaded" persistent="false";
property name="_eagerLoad" persistent="false";
property name="_loaded" persistent="false";
property name="_globalScopeExclusions" persistent="false";

this.constraints = {};

Expand All @@ -46,6 +47,7 @@ component accessors="true" {
function assignDefaultProperties() {
assignAttributesData( {} );
assignOriginalAttributes( {} );
variables._globalScopeExclusions = [];
param variables._meta = {};
param variables._data = {};
param variables._relationshipsData = {};
Expand Down Expand Up @@ -225,6 +227,7 @@ component accessors="true" {
=====================================*/

function getEntities() {
applyGlobalScopes();
return retrieveQuery()
.get( options = variables._queryOptions )
.map( function( attrs ) {
Expand All @@ -236,8 +239,10 @@ component accessors="true" {
}

function all() {
resetQuery();
applyGlobalScopes();
return eagerLoadRelations(
newQuery().from( variables._table )
retrieveQuery().from( variables._table )
.get( options = variables._queryOptions )
.map( function( attrs ) {
return newEntity()
Expand All @@ -249,10 +254,12 @@ component accessors="true" {
}

function get() {
applyGlobalScopes();
return eagerLoadRelations( getEntities() );
}

function first() {
applyGlobalScopes();
var attrs = retrieveQuery().first( options = variables._queryOptions );
return structIsEmpty( attrs ) ?
javacast( "null", "" ) :
Expand All @@ -264,6 +271,7 @@ component accessors="true" {

function find( id ) {
fireEvent( "preLoad", { id = id, metadata = variables._meta } );
applyGlobalScopes();
var data = retrieveQuery()
.from( variables._table )
.find( id, variables._key, variables._queryOptions );
Expand Down Expand Up @@ -294,6 +302,7 @@ component accessors="true" {
}

function firstOrFail() {
applyGlobalScopes();
var attrs = retrieveQuery().first( options = variables._queryOptions );
if ( structIsEmpty( attrs ) ) {
throw(
Expand Down Expand Up @@ -851,6 +860,9 @@ component accessors="true" {

private function tryScopes( missingMethodName, missingMethodArguments ) {
if ( structKeyExists( variables, "scope#missingMethodName#" ) ) {
if ( arrayContains( variables._globalScopeExclusions, lcase( missingMethodName ) ) ) {
return this;
}
var scopeArgs = { "1" = this };
// this is to allow default arguments to be set for scopes
if ( ! structIsEmpty( missingMethodArguments ) ) {
Expand All @@ -864,6 +876,18 @@ component accessors="true" {
return;
}

function applyGlobalScopes() {
return this;
}

function withoutGlobalScope( name ) {
variables.name = isArray( name ) ? name : [ name ];
variables.name.each( function( n ) {
variables._globalScopeExclusions.append( lcase( name ) );
} );
return this;
}

private function forwardToQB( missingMethodName, missingMethodArguments ) {
var result = invoke( retrieveQuery(), missingMethodName, missingMethodArguments );
if ( isSimpleValue( result ) ) {
Expand Down
8 changes: 8 additions & 0 deletions tests/resources/app/models/Admin.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
component extends="User" table="users" {

function applyGlobalScopes() {
this.withLatestPostId();
this.ofType( "admin" );
}

}
20 changes: 20 additions & 0 deletions tests/specs/integration/BaseEntity/GlobalScopeSpec.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
component extends="tests.resources.ModuleIntegrationSpec" appMapping="/app" {

function run() {
describe( "Global Scopes Spec", function() {
it( "can add a global scope to an entity", function() {
var admins = getInstance( "Admin" ).all();
expect( admins ).toBeArray();
expect( admins ).toHaveLength( 1 );
expect( admins[ 1 ].getId() ).toBe( 1 );
} );

it( "can run a query without a global scope", function() {
var admins = getInstance( "Admin" ).withoutGlobalScope( "ofType" ).all();
expect( admins ).toBeArray();
expect( admins ).toHaveLength( 3 );
} );
} );
}

}

0 comments on commit 995706b

Please sign in to comment.