Skip to content
Open
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
27 changes: 27 additions & 0 deletions models/BaseEntity.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,11 @@ component accessors="true" {
arguments.value = castValueForSetter( arguments.name, arguments.value.keyValues()[ 1 ] );
}

guardAgainstLoadedKeyMutation(
arguments.name,
isNull( arguments.value ) ? javacast( "null", "" ) : arguments.value
);

variables._data[ retrieveColumnForAlias( arguments.name ) ] = arguments.cast ? castValueForSetter(
arguments.name,
isNull( arguments.value ) ? javacast( "null", "" ) : arguments.value
Expand All @@ -961,6 +966,28 @@ component accessors="true" {
return this;
}

private void function guardAgainstLoadedKeyMutation( required string name, any value ) {
if ( !isLoaded() || !arrayContainsNoCase( keyNames(), retrieveAliasForColumn( arguments.name ) ) ) {
return;
}

var keyColumn = retrieveColumnForAlias( arguments.name );
var originalIsNull = !variables._originalAttributes.keyExists( keyColumn ) || isNull(
variables._originalAttributes[ keyColumn ]
);
var replacementIsNull = isNull( arguments.value );
if (
originalIsNull != replacementIsNull ||
( !originalIsNull && variables._originalAttributes[ keyColumn ] != arguments.value )
) {
throw(
type = "QuickPrimaryKeyMutationException",
message = "A loaded [#entityName()#] entity cannot change its primary key [#retrieveAliasForColumn( arguments.name )#].",
detail = "Create a new entity when a different primary key is required."
);
}
}

/**
* Retrieve an array of qualified column names.
*
Expand Down
2 changes: 1 addition & 1 deletion models/Relationships/HasOneOrMany.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,10 @@ component
arguments.entity = arrayWrap( arguments.entity );
guardAgainstKeyLengthMismatch( arguments.entity, variables.related.keyNames() );
arguments.entity = tap( variables.related.newEntity(), function( e ) {
e.set_loaded( true );
arrayZipEach( [ variables.related.keyNames(), entity ], function( keyName, value ) {
e.forceAssignAttribute( keyName, value );
} );
e.assignOriginalAttributes( e.retrieveAttributesData() ).set_loaded( true );
} );
}
setForeignAttributesForCreate( arguments.entity );
Expand Down
24 changes: 24 additions & 0 deletions tests/specs/integration/BaseEntity/SaveSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,30 @@ component extends="tests.resources.ModuleIntegrationSpec" {
expect( userRowsPostSave ).toHaveLength( 5 );
} );

it( "throws a helpful error when changing the key of a loaded entity", function() {
var existingUser = getInstance( "User" ).findOrFail( 1 );

expect( function() {
existingUser.setId( 2 ).save();
} ).toThrow( type = "QuickPrimaryKeyMutationException", regex = "cannot change its primary key" );
} );

it( "allows assigning the existing key value to a loaded entity", function() {
var existingUser = getInstance( "User" ).findOrFail( 1 );

expect( function() {
existingUser.setId( 1 ).save();
} ).notToThrow();
} );

it( "guards every part of a loaded composite key", function() {
var composite = getInstance( "Composite" ).findOrFail( [ 1, 2 ] );

expect( function() {
composite.setB( 1 ).save();
} ).toThrow( type = "QuickPrimaryKeyMutationException", regex = "primary key \[b\]" );
} );

it( "does not allow updating of column where update=false in property", function() {
var existingUser = getInstance( "User" ).find( 1 );
existingUser.setEmail( "test2@test.com" );
Expand Down
Loading