Skip to content

Commit

Permalink
fix(HasManyDeep): Account for compound keys in hasManyDeep relationships
Browse files Browse the repository at this point in the history
  • Loading branch information
elpete committed Feb 5, 2025
1 parent 38b2479 commit 71d9c5a
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 16 deletions.
38 changes: 26 additions & 12 deletions models/Relationships/HasManyDeep.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -159,28 +159,38 @@ component
required any localKey
) {
var joins = [];
if ( isArray( arguments.localKey ) ) {

arguments.localKey = arrayWrap( arguments.localKey );
arguments.foreignKey = arrayWrap( arguments.foreignKey );

// This handles polymorphic relationships
if ( arguments.localKey.len() > arguments.foreignKey.len() ) {
arguments.builder.where(
arguments.throughParent.qualifyColumn( arguments.localKey[ 1 ] ),
arguments.predecessor.mappingName()
);

arguments.localKey = arguments.localKey[ 2 ];
arguments.localKey = arraySlice( arguments.localKey, 2 );
}

if ( isArray( arguments.foreignKey ) ) {
// This handles polymorphic relationships
if ( arguments.foreignKey.len() > arguments.localKey.len() ) {
arguments.builder.where(
arguments.predecessor.qualifyColumn( arguments.foreignKey[ 1 ] ),
arguments.throughParent.mappingName()
);

arguments.foreignKey = arguments.foreignKey[ 2 ];
arguments.foreignKey = arraySlice( arguments.foreignKey, 2 );
}

joins.append( [
arguments.throughParent.qualifyColumn( arguments.localKey ),
arguments.predecessor.qualifyColumn( arguments.foreignKey )
] );
guardAgainstKeyLengthMismatch( arguments.foreignKey, arguments.localKey );

for ( var i = 1; i <= arguments.localKey.len(); i++ ) {
joins.append( [
arguments.throughParent.qualifyColumn( arguments.localKey[ i ] ),
arguments.predecessor.qualifyColumn( arguments.foreignKey[ i ] )
] );
}

return joins;
}
Expand Down Expand Up @@ -242,15 +252,19 @@ component
.split( "\s(?:[Aa][Ss]\s)?" );
var alias = segments[ 2 ] ?: "";

var localKeys = [];
var qualifiedLocalKeys = [];
for ( var i = 1; i <= variables.localKeys.len(); i++ ) {
if ( i == 1 ) {
localKeys.append( variables.parent.qualifyColumn( variables.localKeys[ i ] ) );
arrayWrap( variables.localKeys[ i ] ).each( function( localKey ) {
qualifiedLocalKeys.append( variables.parent.qualifyColumn( localKey ) );
} );
} else {
localKeys.append( variables.throughParents[ i - 1 ].qualifyColumn( variables.localKeys[ i ] ) );
arrayWrap( variables.localKeys[ i ] ).each( function( localKey ) {
qualifiedLocalKeys.append( variables.throughParents[ i - 1 ].qualifyColumn( localKey ) );
} );
}
}
return localKeys;
return qualifiedLocalKeys;
}

public boolean function addEagerConstraints( required array entities, required any baseEntity ) {
Expand Down
2 changes: 1 addition & 1 deletion server.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"app":{
"cfengine":"adobe@2021"
"cfengine":"adobe@2023"
},
"JVM":{
"javaVersion":"openjdk21"
Expand Down
8 changes: 8 additions & 0 deletions tests/resources/app/models/Country.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,14 @@ component extends="quick.models.BaseEntity" accessors="true" {
return hasManyThrough( [ "roles", "permissions" ] );
}

function playingFields() {
return hasMany( "PlayingField", "country_id" );
}

function games() {
return hasManyThrough( [ "playingFields", "games" ] );
}

function keyType() {
return variables._wirebox.getInstance( "UUIDKeyType@quick" );
}
Expand Down
2 changes: 1 addition & 1 deletion tests/resources/app/models/inLeague/PlayingField.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ component extends="quick.models.BaseEntity" accessors="true" {
return variables._wirebox.getInstance( "NullKeyType@quick" );
}

function field() {
function games() {
return hasMany(
relationName = "Game",
foreignKey = [ "fieldID", "clientID" ],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@ component {
t.unsignedInteger( "clientID" ).nullable();
t.string( "fieldName" );
t.primaryKey( [ "fieldID", "clientID" ] );
t.uuid( "country_id" ).nullable();
} );

qb.table( "playing_fields" ).insert( [
{
"fieldID": 1,
"clientID": 1,
"fieldName": "First Field"
"fieldName": "First Field",
"country_id": "02B84D66-0AA0-F7FB-1F71AFC954843861" // United States
},
{
"fieldID": 1,
"clientID": 2,
"fieldName": "Second Field"
"fieldName": "Second Field",
"country_id": "02BA2DB0-EB1E-3F85-5F283AB5E45608C6" // Argentina
}
] );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,19 @@ component extends="tests.resources.ModuleIntegrationSpec" {
expect( officemates[ 2 ].getId() ).toBe( 3 );
expect( officemates[ 3 ].getId() ).toBe( 4 );
} );

it( "can go through entities with multiple local and foreign keys", () => {
var argentina = getInstance( "Country" ).where( "name", "Argentina" ).firstOrFail();
var argentinaGames = argentina.getGames();
expect( argentinaGames ).toBeArray();
expect( argentinaGames ).toHaveLength( 1 );
expect( argentinaGames[ 1 ].getId() ).toBe( 1 );

var unitedStates = getInstance( "Country" ).where( "name", "United States" ).firstOrFail();
var usGames = unitedStates.getGames();
expect( usGames ).toBeArray();
expect( usGames ).toBeEmpty();
} );
} );
}

Expand Down

0 comments on commit 71d9c5a

Please sign in to comment.