diff --git a/models/BaseEntity.cfc b/models/BaseEntity.cfc index 8900ec48..22c75983 100644 --- a/models/BaseEntity.cfc +++ b/models/BaseEntity.cfc @@ -1292,6 +1292,7 @@ component accessors="true" { } ), arguments.options ); + refreshAttributesOnSave(); assignOriginalAttributes( retrieveAttributesData() ); markLoaded(); fireEvent( @@ -1326,8 +1327,9 @@ component accessors="true" { result.result[ getParentDefinition().joincolumn ] = variables._data[ getParentDefinition().joinColumn ]; } retrieveKeyType().postInsert( this, result ); - assignOriginalAttributes( retrieveAttributesData() ); markLoaded(); + refreshAttributesOnSave(); + assignOriginalAttributes( retrieveAttributesData() ); fireEvent( "postInsert", { @@ -1359,6 +1361,46 @@ component accessors="true" { return this; } + /** + * Refreshes attributes whose values are generated or changed by the database + * during persistence. + */ + private void function refreshAttributesOnSave() { + var attributesToRefresh = {}; + for ( var name in variables._attributes ) { + if ( variables._attributes[ name ].refreshOnSave ) { + attributesToRefresh[ name ] = variables._attributes[ name ]; + } + } + + if ( attributesToRefresh.isEmpty() ) { + return; + } + + var refreshQuery = newQuery().withoutGlobalScope(); + var entityKeys = keyNames(); + var entityValues = keyValues(); + for ( var i = 1; i <= entityKeys.len(); i++ ) { + refreshQuery.where( entityKeys[ i ], entityValues[ i ] ); + } + var refreshedEntity = refreshQuery.first(); + if ( isNull( refreshedEntity ) ) { + return; + } + + var refreshedData = refreshedEntity.retrieveAttributesData( withNulls = true ); + for ( var name in attributesToRefresh ) { + var attribute = attributesToRefresh[ name ]; + if ( isNull( refreshedData[ attribute.column ] ) ) { + variables._data[ attribute.column ] = javacast( "null", "" ); + variables[ attribute.name ] = javacast( "null", "" ); + } else { + variables._data[ attribute.column ] = refreshedData[ attribute.column ]; + variables[ attribute.name ] = refreshedData[ attribute.column ]; + } + } + } + /** * Deletes the entity from the database. * This function can only be called on loaded entities. @@ -3411,6 +3453,7 @@ component accessors="true" { param attr.sqltype = ""; param attr.insert = true; param attr.update = true; + param attr.refreshOnSave = false; param attr.virtual = false; param attr.exclude = false; param attr.isParentColumn = false; @@ -3420,6 +3463,9 @@ component accessors="true" { if ( !isBoolean( attr.fillable ) ) { attr.fillable = lCase( trim( attr.fillable & "" ) ) == "true"; } + if ( !isBoolean( attr.refreshOnSave ) ) { + attr.refreshOnSave = lCase( trim( attr.refreshOnSave & "" ) ) == "true"; + } variables._nullValues[ attr.name ] = attr.nullValue; return arguments.attr; } diff --git a/tests/resources/app/models/DatabaseGeneratedUser.cfc b/tests/resources/app/models/DatabaseGeneratedUser.cfc new file mode 100644 index 00000000..df746bdd --- /dev/null +++ b/tests/resources/app/models/DatabaseGeneratedUser.cfc @@ -0,0 +1,16 @@ +component + extends ="quick.models.BaseEntity" + accessors="true" + table ="users" +{ + + property name="id"; + property name="username"; + property name="firstName" column="first_name"; + property name="lastName" column="last_name"; + property + name ="createdDate" + column ="created_date" + refreshOnSave="true"; + +} diff --git a/tests/specs/integration/BaseEntity/SaveSpec.cfc b/tests/specs/integration/BaseEntity/SaveSpec.cfc index a3b99ef1..5e2eba00 100644 --- a/tests/specs/integration/BaseEntity/SaveSpec.cfc +++ b/tests/specs/integration/BaseEntity/SaveSpec.cfc @@ -56,6 +56,18 @@ component extends="tests.resources.ModuleIntegrationSpec" { expect( newUser.retrieveAttributesData() ).toHaveKey( "id" ); } ); + it( "retrieves database-generated attributes marked to refresh on save", function() { + var newUser = getInstance( "DatabaseGeneratedUser" ) + .setUsername( "database-timestamp-user" ) + .setFirstName( "Database" ) + .setLastName( "Timestamp" ) + .save(); + + expect( newUser.getCreatedDate() ).notToBe( "" ); + expect( newUser.getCreatedDate() ).toBeDate(); + expect( newUser.isDirty( "createdDate" ) ).toBeFalse(); + } ); + it( "a saved entity is not dirty", function() { var newUser = getInstance( "User" ); newUser.setUsername( "new_user" );