Skip to content
47 changes: 47 additions & 0 deletions models/BaseEntity.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,15 @@ component accessors="true" {
return arrayWrap( variables._key );
}

/**
* Returns the timestamp fields updated by `touch`.
*
* @return [String]
*/
public array function timestampFields() {
return [ "createdDate", "modifiedDate" ];
}

/**
* Returns the column name for the primary key.
*
Expand Down Expand Up @@ -1414,6 +1423,44 @@ component accessors="true" {
return save();
}

/**
* Updates the configured timestamp fields using a new query without changing
* the current entity state.
*
* @options Any options to pass to `queryExecute`. Default: {}.
*
* @return quick.models.BaseEntity
*/
public any function touch( struct options = {} ) {
guardAgainstNotLoaded( "This instance is not loaded so it cannot be touched." );
guardReadOnly();
var timestamp = now();
var timestampAttributes = timestampFields().reduce( function( attributes, field ) {
arguments.attributes[ arguments.field ] = timestamp;
return arguments.attributes;
}, {} );
guardAgainstReadOnlyAttributes( timestampAttributes );

var builder = newQuery().where( function( q ) {
keyNames().each( function( keyName ) {
q.where(
arguments.keyName,
variables._originalAttributes[ retrieveColumnForAlias( arguments.keyName ) ]
);
} );
} );
builder
.getQB()
.update(
timestampAttributes.map( function( field, value ) {
return builder.generateQueryParamStruct( field, value );
} ),
arguments.options
);

return this;
}

/**
* Creates a new entity with the given attributes and then saves the entity.
*
Expand Down
15 changes: 15 additions & 0 deletions tests/resources/app/models/CustomTimestampUser.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
component
extends ="quick.models.BaseEntity"
accessors="true"
table ="users"
{

property name="id";
property name="createdDate" column="created_date";
property name="modifiedDate" column="modified_date";

public array function timestampFields() {
return [ "createdDate" ];
}

}
42 changes: 42 additions & 0 deletions tests/specs/integration/BaseEntity/SaveSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,48 @@ component extends="tests.resources.ModuleIntegrationSpec" {
expect( userRowsPostSave ).toHaveLength( 5 );
} );

it( "can touch an entity timestamp", function() {
var user = getInstance( "User" ).findOrFail( 1 );
var originalCreated = user.getCreatedDate();
var originalModified = user.getModifiedDate();
var originalFirstName = user.getFirstName();
var originalId = user.getId();
var dirtyFirstName = "This must not be persisted";
var dirtyId = 9999;

user.setFirstName( dirtyFirstName );
user.setId( dirtyId );

user.touch();

expect( dateCompare( user.getCreatedDate(), originalCreated ) ).toBe( 0 );
expect( dateCompare( user.getModifiedDate(), originalModified ) ).toBe( 0 );
expect( user.getFirstName() ).toBe( dirtyFirstName );
expect( user.getId() ).toBe( dirtyId );
expect( user.isDirty( "firstName" ) ).toBeTrue();
expect( user.isDirty( "id" ) ).toBeTrue();
expect( user.isDirty( "createdDate" ) ).toBeFalse();
expect( user.isDirty( "modifiedDate" ) ).toBeFalse();
var freshUser = getInstance( "User" ).findOrFail( originalId );
expect( dateCompare( freshUser.getCreatedDate(), originalCreated ) ).toBe( 1 );
expect( dateCompare( freshUser.getModifiedDate(), originalModified ) ).toBe( 1 );
expect( freshUser.getFirstName() ).toBe( originalFirstName );
} );

it( "can override the timestamp fields used by touch", function() {
var user = getInstance( "CustomTimestampUser" ).findOrFail( 1 );
var originalCreated = user.getCreatedDate();
var originalModified = user.getModifiedDate();

user.touch();

expect( dateCompare( user.getCreatedDate(), originalCreated ) ).toBe( 0 );
expect( dateCompare( user.getModifiedDate(), originalModified ) ).toBe( 0 );
var freshUser = user.fresh();
expect( dateCompare( freshUser.getCreatedDate(), originalCreated ) ).toBe( 1 );
expect( dateCompare( freshUser.getModifiedDate(), originalModified ) ).toBe( 0 );
} );

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