Skip to content

Commit

Permalink
feat(Subselects): Add subselect helper
Browse files Browse the repository at this point in the history
The subselect helper will ensure you can access your
property via getters.  The property cannot be updated or inserted.
It additionally ensures the original columns are selected (if needed)
and limits the result of the subquery to 1.
  • Loading branch information
elpete committed May 3, 2019
1 parent d419eb6 commit cf13ddd
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 7 deletions.
34 changes: 27 additions & 7 deletions models/BaseEntity.cfc
Expand Up @@ -307,8 +307,11 @@ component accessors="true" {
.set_Loaded( true );
}

function newEntity() {
return variables._entityCreator.new( this );
function newEntity( name ) {
if ( isNull( name ) ) {
return variables._entityCreator.new( this );
}
return variables._wirebox.getInstance( name );
}

function reset() {
Expand Down Expand Up @@ -746,6 +749,27 @@ component accessors="true" {
return variables.query;
}

public function addSubselect( name, subselect ) {
if ( ! variables._attributes.keyExists( retrieveAliasForColumn( name ) ) ) {
variables._attributes[ name ] = name;
variables._meta.properties.append( { "name" = name, "update" = false, "insert" = false } );
}

if ( retrieveQuery().getColumns().isEmpty() ||
( retrieveQuery().getColumns().len() == 1 && isSimpleValue( retrieveQuery().getColumns()[ 1 ] ) && retrieveQuery().getColumns()[ 1 ] == "*" ) ) {
retrieveQuery().select( retrieveQuery().getFrom() & ".*" );
}

var subselectQuery = subselect;
if ( isClosure( subselectQuery ) ) {
subselectQuery = retrieveQuery().newQuery();
subselectQuery = subselect( subselectQuery );
}

retrieveQuery().subselect( name, subselectQuery.retrieveQuery().limit( 1 ) );
return retrieveQuery();
}

/*=====================================
= Magic Methods =
=====================================*/
Expand Down Expand Up @@ -781,12 +805,8 @@ component accessors="true" {

var columnName = variables._str.slice( missingMethodName, 4 );

if ( isColumnAlias( columnName ) ) {
return retrieveAttribute( retrieveColumnForAlias( columnName ) );
}

if ( hasAttribute( columnName ) ) {
return retrieveAttribute( columnName );
return retrieveAttribute( retrieveColumnForAlias( columnName ) );
}

return;
Expand Down
20 changes: 20 additions & 0 deletions tests/resources/app/models/User.cfc
Expand Up @@ -19,6 +19,26 @@ component extends="quick.models.BaseEntity" accessors="true" {
return query.where( "type", type );
}

function scopeWithLatestPostId( query ) {
addSubselect( "latestPostId", newEntity( "Post" )
.select( "post_pk" )
.whereColumn( "users.id", "user_id" )
.latest() );

/*
// can also use closures
addSubselect( "latestPostId", function( q ) {
// you don't have to use an entity.
// it just helps with scopes, column names, tables, etc.
// there is also a query passed to you.
return newEntity( "Post" )
.select( "post_pk" )
.whereColumn( "users.id", "userId" )
.latest();
} );
*/
}

function posts() {
return hasMany( "Post", "user_id" );
}
Expand Down
27 changes: 27 additions & 0 deletions tests/specs/integration/BaseEntity/SubqueriesSpec.cfc
@@ -0,0 +1,27 @@
component extends="tests.resources.ModuleIntegrationSpec" appMapping="/app" {

function beforeAll() {
super.beforeAll();
controller.getInterceptorService().registerInterceptor( interceptorObject = this );
}

function run() {
describe( "Subqueries Spec", function() {
beforeEach( function() {
variables.queries = [];
} );

it( "can add a subquery to an entity", function() {
var elpete = getInstance( "User" ).withLatestPostId().findOrFail( 1 );
expect( elpete.getLatestPostId() ).notToBeNull();
expect( elpete.getLatestPostId() ).toBe( 523526 );
expect( variables.queries ).toHaveLength( 1, "Only one query should have been executed. #arrayLen( variables.queries )# were instead." );
} );
} );
}

function preQBExecute( event, interceptData, buffer, rc, prc ) {
arrayAppend( variables.queries, interceptData );
}

}

0 comments on commit cf13ddd

Please sign in to comment.