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
38 changes: 25 additions & 13 deletions models/BaseEntity.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -616,12 +616,14 @@ component accessors="true" {
if ( !hasAttribute( key ) ) {
continue;
}
variables._data[ retrieveColumnForAlias( key ) ] = (
!arguments.attributes.keyExists( key ) || isNull( arguments.attributes[ key ] )
) ? javacast( "null", "" ) : castValueForGetter( key, arguments.attributes[ key ] );
variables[ retrieveAliasForColumn( key ) ] = (
var value = castValueForGetter(
key,
!arguments.attributes.keyExists( key ) || isNull( arguments.attributes[ key ] )
) ? javacast( "null", "" ) : castValueForGetter( key, arguments.attributes[ key ] );
? javacast( "null", "" )
: arguments.attributes[ key ]
);
variables._data[ retrieveColumnForAlias( key ) ] = isNull( value ) ? javacast( "null", "" ) : value;
variables[ retrieveAliasForColumn( key ) ] = isNull( value ) ? javacast( "null", "" ) : value;
}
}

Expand Down Expand Up @@ -3456,24 +3458,26 @@ component accessors="true" {
}

if ( !structKeyExists( variables._casts, arguments.key ) ) {
return arguments.value;
}

if ( !isVirtualAttribute( arguments.key ) && isNullValue( arguments.key, arguments.value ) ) {
return arguments.value;
return isNull( arguments.value ) ? javacast( "null", "" ) : arguments.value;
}

var castMapping = variables._casts[ arguments.key ];
if ( !variables._casterCache.keyExists( arguments.key ) ) {
variables._casterCache[ arguments.key ] = variables._wirebox.getInstance( dsl = castMapping );
}
var caster = variables._casterCache[ arguments.key ];
variables._castCache[ arguments.key ] = caster.get(
var caster = variables._casterCache[ arguments.key ];
var castedValue = caster.get(
entity = this,
key = arguments.key,
value = isNull( arguments.value ) ? javacast( "null", "" ) : arguments.value
);
return variables._castCache[ arguments.key ];
if ( isNull( castedValue ) ) {
structDelete( variables._castCache, arguments.key );
return javacast( "null", "" );
}

variables._castCache[ arguments.key ] = castedValue;
return castedValue;
}

/**
Expand Down Expand Up @@ -3524,6 +3528,14 @@ component accessors="true" {
}
var caster = variables._casterCache[ key ];
var attrs = caster.set( this, key, castedValue );
if ( isNull( attrs ) ) {
assignAttribute(
name = key,
value = javacast( "null", "" ),
cast = false
);
continue;
}
if ( !isStruct( attrs ) ) {
attrs = { "#key#" : attrs };
}
Expand Down
10 changes: 9 additions & 1 deletion models/Casts/BooleanCast.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ component singleton {
required string key,
any value
) {
return isNull( arguments.value ) ? false : !!arguments.value;
if ( isNull( arguments.value ) ) {
return javacast( "null", "" );
}

return arguments.entity.isNullValue( arguments.key, arguments.value ) ? arguments.value : !!arguments.value;
}

/**
Expand All @@ -32,6 +36,10 @@ component singleton {
required string key,
any value
) {
if ( isNull( arguments.value ) || arguments.entity.isNullValue( arguments.key, arguments.value ) ) {
return javacast( "null", "" );
}

return arguments.value ? 1 : 0;
}

Expand Down
10 changes: 10 additions & 0 deletions tests/resources/app/models/CustomCastPhoneNumber.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
component
extends ="quick.models.BaseEntity"
table ="phone_numbers"
accessors="true"
{

property name="id";
property name="confirmed" casts="NullValueCast";

}
21 changes: 21 additions & 0 deletions tests/resources/app/models/NullValueCast.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
component singleton {

public any function get(
required any entity,
required string key,
any value
) {
return isNull( arguments.value ) || arguments.entity.isNullValue( arguments.key, arguments.value )
? "casted-null"
: arguments.value;
}

public any function set(
required any entity,
required string key,
any value
) {
return isNull( arguments.value ) ? javacast( "null", "" ) : arguments.value;
}

}
16 changes: 12 additions & 4 deletions tests/specs/integration/BaseEntity/AttributeCastsSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,21 @@ component extends="tests.resources.ModuleIntegrationSpec" {
expect( theme.getConfig().primaryColor ).toBe( "orange" );
} );

it( "can still allow nulls when using casts", () => {
it( "preserves null values when using BooleanCast", () => {
var pn = getInstance( "PhoneNumber" ).find( 3 );
expect( pn.isNullAttribute( "confirmed" ) ).toBeTrue( "[confirmed] should be considered null" );
expect( pn.getConfirmed() ).toBe( "" );
// expect( function() {
pn.update( { "active" : false } );
// } ).notToThrow( message = "PhoneNumber should be able to be saved with a `null` [confirmed] value" );

pn.update( { "active" : false } ).refresh();

expect( pn.isNullAttribute( "confirmed" ) ).toBeTrue( "[confirmed] should remain null after saving" );
expect( pn.getConfirmed() ).toBe( "" );
} );

it( "allows custom casts to handle null database values", () => {
var pn = getInstance( "CustomCastPhoneNumber" ).find( 3 );

expect( pn.getConfirmed() ).toBe( "casted-null" );
} );

it( "correctly casts child entities", () => {
Expand Down
Loading