Skip to content

Commit 9c5d69e

Browse files
committed
fix(ChildEntity): Fix for bringing back columns correct when multiple child entities share the same column
1 parent 21c3e03 commit 9c5d69e

File tree

5 files changed

+112
-46
lines changed

5 files changed

+112
-46
lines changed

models/QuickBuilder.cfc

Lines changed: 83 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,7 @@ component accessors="true" transientCache="false" {
744744
entity
745745
.getDiscriminations()
746746
.each( function( discriminator, data ) {
747-
// only join if this is a polymorphicn association
747+
// only join if this is a polymorphic association
748748
if ( !entity.isSingleTableInheritance() ) {
749749
variables.qb.join(
750750
data.table,
@@ -754,7 +754,14 @@ component accessors="true" transientCache="false" {
754754
"left outer"
755755
);
756756
}
757-
variables.qb.addSelect( data.childColumns );
757+
variables.qb.addSelect(
758+
data.childColumns.map( ( column ) => {
759+
if ( column == data.joincolumn ) {
760+
return "#column# AS #column#"
761+
}
762+
return column;
763+
} )
764+
);
758765
} );
759766
}
760767
}
@@ -1324,11 +1331,11 @@ component accessors="true" transientCache="false" {
13241331
arguments.data[ listLast( getEntity().get_meta().localMetadata.discriminatorColumn, "." ) ]
13251332
)
13261333
) {
1327-
var childClass = variables._wirebox.getInstance(
1328-
getEntity().getDiscriminations()[
1329-
arguments.data[ listLast( getEntity().get_meta().localMetadata.discriminatorColumn, "." ) ]
1330-
].mapping
1331-
);
1334+
var discrimination = getEntity().getDiscriminations()[
1335+
arguments.data[ listLast( getEntity().get_meta().localMetadata.discriminatorColumn, "." ) ]
1336+
];
1337+
1338+
var childClass = variables._wirebox.getInstance( discrimination.mapping );
13321339

13331340
// add any virtual attributes present in the parent entity to child entity
13341341
getEntity()
@@ -1337,6 +1344,19 @@ component accessors="true" transientCache="false" {
13371344
childClass.appendVirtualAttribute( item );
13381345
} );
13391346

1347+
// find the correct child columns to use, in the case that multiple child tables contain the same column
1348+
for ( var column in data ) {
1349+
if ( listLen( column, "." ) > 1 ) {
1350+
if ( listFirst( column, "." ) == discrimination.table ) {
1351+
data[ listRest( column, "." ) ] = data[ column ];
1352+
data.delete( column );
1353+
} else {
1354+
// if the column is not in the discrimination table, then we need to remove it
1355+
data.delete( column );
1356+
}
1357+
}
1358+
}
1359+
13401360
return childClass
13411361
.assignAttributesData( arguments.data )
13421362
.assignOriginalAttributes( arguments.data )
@@ -1431,4 +1451,60 @@ component accessors="true" transientCache="false" {
14311451
return isArray( arguments.value ) ? arguments.value : [ arguments.value ];
14321452
}
14331453

1454+
/**
1455+
* Accepts an array of arrays and calls a callback passing each item of
1456+
* the same index from each of the arrays.
1457+
*
1458+
* @arrays An array of arrays. All arrays must have the same length.
1459+
* @callback The callback to call. It will be passed an item from each
1460+
* array passed in at the same index.
1461+
*
1462+
* @throws ArrayZipLengthMismatch
1463+
*
1464+
* @return The original array of arrays passed in.
1465+
*/
1466+
private array function arrayZipEach( required array arrays, required any callback ) {
1467+
if ( arguments.arrays.isEmpty() ) {
1468+
return arguments.arrays;
1469+
}
1470+
1471+
var lengths = arguments.arrays.map( function( arr ) {
1472+
return arr.len();
1473+
} );
1474+
1475+
if ( unique( lengths ).len() > 1 ) {
1476+
throw(
1477+
type = "ArrayZipLengthMismatch",
1478+
message = "The arrays do not have the same length. Lengths: [#serializeJSON( lengths )#]",
1479+
extendedInfo = serializeJSON( arguments.arrays )
1480+
);
1481+
}
1482+
1483+
for ( var i = 1; i <= arguments.arrays[ 1 ].len(); i++ ) {
1484+
var args = {};
1485+
for ( var j = 1; j <= arguments.arrays.len(); j++ ) {
1486+
args[ j ] = arguments.arrays[ j ][ i ];
1487+
}
1488+
callback( argumentCollection = args );
1489+
}
1490+
1491+
return arguments.arrays;
1492+
}
1493+
1494+
/**
1495+
* Returns an array of the unique items of an array.
1496+
*
1497+
* @items An array of items.
1498+
*
1499+
* @doc_generic any
1500+
* @return [any]
1501+
*/
1502+
public array function unique( required array items ) {
1503+
if ( arrayIsEmpty( arguments.items ) ) {
1504+
return arguments.items;
1505+
}
1506+
1507+
return arraySlice( createObject( "java", "java.util.HashSet" ).init( arguments.items ).toArray(), 1 );
1508+
}
1509+
14341510
}

tests/resources/app/models/Comment.cfc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
component
1+
component
22
extends="quick.models.BaseEntity"
3-
discriminatorColumn="designation"
4-
accessors="true"
3+
discriminatorColumn="designation"
4+
accessors="true"
55
{
66

77
property name="id";
@@ -32,9 +32,9 @@ component
3232
}
3333

3434
// chose this sql function as it present in both mysql and sql server
35-
function scopeAddUpperBody(qb){
35+
function scopeAddUpperBody( qb ) {
3636
qb.selectRaw( "UPPER(body) as upperBody" );
37-
appendVirtualAttribute( "upperBody" );
37+
appendVirtualAttribute( "upperBody" );
3838
}
3939

4040
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
component
1+
component
22
accessors="true"
33
extends="Comment"
44
table="pictureComments"
55
joincolumn="FK_comment"
66
discriminatorValue="picture"
77
{
8+
89
property name="filename";
10+
911
}

tests/specs/integration/BaseEntity/ChildClassSpec.cfc

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -358,58 +358,46 @@ component extends="tests.resources.ModuleIntegrationSpec" {
358358

359359
it( "Will return an mix of child classes when retrieving all discriminated comments", function() {
360360
var comments = getInstance( "Comment" )
361-
.where('designation', '!=', 'public')
362-
.orderBy('id')
361+
.where( "designation", "!=", "public" )
362+
.orderBy( "id" )
363363
.get();
364364

365-
expect( comments[1] ).toBeInstanceOf( "InternalComment" );
366-
expect( comments[2] ).toBeInstanceOf( "PictureComment" );
365+
expect( comments[ 1 ] ).toBeInstanceOf( "InternalComment" );
366+
expect( comments[ 2 ] ).toBeInstanceOf( "PictureComment" );
367367
} );
368368

369369
it( "Will load foreign key when retrieving different child classes through parent", function() {
370370
// comment id 4 = internal comment
371-
var internalMemento = getInstance( "Comment" )
372-
.findOrFail(4)
373-
.getMemento();
371+
var internalComment = getInstance( "Comment" ).findOrFail( 4 );
374372

375-
expect( internalMemento ).toHaveKey( "FK_comment" );
376-
expect( internalMemento['FK_comment'] ).toBe( 4 )
373+
expect( internalComment.hasAttribute( "FK_comment" ) ).toBeTrue( "FK_comment should be a valid attribute" );
374+
expect( internalComment.retrieveAttribute( "FK_comment" ) ).toBe( 4 );
377375

378376
// comment id 5 = picture comment
379-
var pictureMemento = getInstance( "Comment" )
380-
.findOrFail(5)
381-
.getMemento();
377+
var pictureComment = getInstance( "Comment" ).findOrFail( 5 );
382378

383-
expect( pictureMemento ).toHaveKey( "FK_comment" );
384-
expect( pictureMemento['FK_comment'] ).toBe( 5 )
385-
} );
379+
expect( pictureComment.hasAttribute( "FK_comment" ) ).toBeTrue( "FK_comment should be a valid attribute" );
380+
expect( pictureComment.retrieveAttribute( "FK_comment" ) ).toBe( 5 );
381+
} );
386382

387383
it( "Can update each child class when fetching from parent class", function() {
388384
// comment id 4 = internal comment
389385
var internalComment = getInstance( "Comment" )
390-
.findOrFail(4)
391-
.update({
392-
reason: 'Super private, ya know?'
393-
});
386+
.findOrFail( 4 )
387+
.update( { reason : "Super private, ya know?" } );
394388

395-
var uInternalComment = getInstance( "Comment" )
396-
.findOrFail(4);
389+
var uInternalComment = getInstance( "Comment" ).findOrFail( 4 );
397390

398-
expect( uInternalComment).toBeInstanceOf( "internalComment" );
399-
expect( uInternalComment.getReason() ).toBe('Super private, ya know?');
391+
expect( uInternalComment ).toBeInstanceOf( "internalComment" );
392+
expect( uInternalComment.getReason() ).toBe( "Super private, ya know?" );
400393

401394
// comment id 5 = picture comment
402-
var pictureComment = getInstance( "Comment" )
403-
.findOrFail(5)
404-
.update({
405-
filename: 'Lenna.jpeg'
406-
});
395+
var pictureComment = getInstance( "Comment" ).findOrFail( 5 ).update( { filename : "Lenna.jpeg" } );
407396

408-
var uPictureComment = getInstance( "Comment" )
409-
.findOrFail(5);
397+
var uPictureComment = getInstance( "Comment" ).findOrFail( 5 );
410398

411-
expect( uPictureComment).toBeInstanceOf( "pictureComment" );
412-
expect( uPictureComment.getFileName() ).toBe('Lenna.jpeg');
399+
expect( uPictureComment ).toBeInstanceOf( "pictureComment" );
400+
expect( uPictureComment.getFileName() ).toBe( "Lenna.jpeg" );
413401
} );
414402

415403
it( "returns an array of discriminated entities when loading through a relationship", () => {

tests/specs/integration/BaseEntity/Relationships/RelationshipsAggregatesSpec.cfc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ component extends="tests.resources.ModuleIntegrationSpec" {
276276
expect( commentsCountBuilder ).toBeArray();
277277
expect( commentsCountBuilder ).toHaveLength( 1 );
278278
expect( commentsCountBuilder[ 1 ] ).toBeInstanceOf( "QuickBuilder" );
279-
expect( commentsCountBuilder[ 1 ].toSQL() ).toBe( "SELECT COUNT(*) FROM `comments` LEFT OUTER JOIN `internalComments` ON `comments`.`id` = `internalComments`.`FK_comment` WHERE (`my_posts`.`post_pk` = `comments`.`commentable_id`) AND `comments`.`commentable_type` = ?" );
279+
expect( commentsCountBuilder[ 1 ].toSQL() ).toBe( "SELECT COUNT(*) FROM `comments` LEFT OUTER JOIN `pictureComments` ON `comments`.`id` = `pictureComments`.`FK_comment` LEFT OUTER JOIN `internalComments` ON `comments`.`id` = `internalComments`.`FK_comment` WHERE (`my_posts`.`post_pk` = `comments`.`commentable_id`) AND `comments`.`commentable_type` = ?" );
280280
} );
281281
} );
282282

0 commit comments

Comments
 (0)