Skip to content

Commit

Permalink
Added way to check if field was updated
Browse files Browse the repository at this point in the history
  • Loading branch information
Jurigag committed Apr 1, 2017
1 parent f1d40bd commit 50fe927
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- Added Factory Adapter loaders [#11001](https://github.com/phalcon/cphalcon/issues/11001)
- Added ability to sanitize URL to `Phalcon\Filter`
- Add argument to interface `Phalcon\Mvc\Model\Query\BuilderInterface::join()` - `type` to specify type join
- Added `Phalcon\Mvc\Model::hasUpdated` and `Phalcon\Mvc\Model:getUpdatedFields`, way to check if fields were updated after create/save/update

# [3.1.2](https://github.com/phalcon/cphalcon/releases/tag/v3.1.2) (2017-XX-XX)
- Fixed PHP 7.1 issues [#12055](https://github.com/phalcon/cphalcon/issues/12055)
Expand Down
102 changes: 92 additions & 10 deletions phalcon/mvc/model.zep
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface

protected _snapshot;

protected _oldSnapshot = [];

const OP_NONE = 0;

const OP_CREATE = 1;
Expand Down Expand Up @@ -2456,8 +2458,9 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
*/
let useDynamicUpdate = (boolean) manager->isUsingDynamicUpdate(this);

let snapshot = this->_snapshot;

if useDynamicUpdate {
let snapshot = this->_snapshot;
if typeof snapshot != "array" {
let useDynamicUpdate = false;
}
Expand Down Expand Up @@ -2650,9 +2653,11 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
], bindTypes);

if success && manager->isKeepingSnapshots(this) {
if typeof this->_snapshot == "array" {
let this->_snapshot = array_merge(this->_snapshot, newSnapshot);
if typeof snapshot == "array" {
let this->_oldSnapshot = snapshot;
let this->_snapshot = array_merge(snapshot, newSnapshot);
} else {
let this->_oldSnapshot = [];
let this->_snapshot = newSnapshot;
}
}
Expand Down Expand Up @@ -3829,6 +3834,7 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
let snapshot = data;
}

let this->_oldSnapshot = snapshot;
let this->_snapshot = snapshot;
}

Expand Down Expand Up @@ -3873,6 +3879,34 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
return count(changedFields) > 0;
}

/**
* Check if a specific attribute was updated
* This only works if the model is keeping data snapshots
*
* @param string|array fieldName
*/
public function hasUpdated(var fieldName = null, boolean allFields = false) -> boolean
{
var updatedFields;

let updatedFields = this->getUpdatedFields();

/**
* If a field was specified we only check it
*/
if typeof fieldName == "string" {
return in_array(fieldName, updatedFields);
} elseif typeof fieldName == "array" {
if allFields {
return array_intersect(fieldName, updatedFields) == fieldName;
}

return count(array_intersect(fieldName, updatedFields)) > 0;
}

return count(updatedFields) > 0;
}

/**
* Returns a list of changed values.
*
Expand All @@ -3896,13 +3930,6 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
throw new Exception("The record doesn't have a valid data snapshot");
}

/**
* Dirty state must be DIRTY_PERSISTENT to make the checking
*/
if this->_dirtyState != self::DIRTY_STATE_PERSISTENT {
throw new Exception("Change checking cannot be performed because the object has not been persisted or is deleted");
}

/**
* Return the models meta-data
*/
Expand Down Expand Up @@ -3956,6 +3983,61 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
return changed;
}

/**
* Returns a list of updated values.
*
* <code>
* $robots = Robots::findFirst();
* print_r($robots->getChangedFields()); // []
*
* $robots->deleted = 'Y';
*
* $robots->getChangedFields();
* print_r($robots->getChangedFields()); // ["deleted"]
* $robots->save();
* print_r($robots->getChangedFields()); // []
* print_r($robots->getUpdatedFields()); // ["deleted"]
* </code>
*/
public function getUpdatedFields()
{
var updated, name, snapshot,
oldSnapshot, value;

let snapshot = this->_snapshot;
let oldSnapshot = this->_oldSnapshot;

if typeof snapshot != "array" {
throw new Exception("The record doesn't have a valid data snapshot");
}

/**
* Dirty state must be DIRTY_PERSISTENT to make the checking
*/
if this->_dirtyState != self::DIRTY_STATE_PERSISTENT {
throw new Exception("Change checking cannot be performed because the object has not been persisted or is deleted");
}

let updated = [];

for name, value in snapshot {
/**
* If some attribute is not present in the oldSnapshot, we assume the record as changed
*/
if !isset oldSnapshot[name] {
let updated[] = name;
continue;
}

if value !== oldSnapshot[name] {
let updated[] = name;
continue;
}
}

return updated;
}

/**
* Sets if a model must use dynamic update instead of the all-field update
*
Expand Down
86 changes: 86 additions & 0 deletions tests/unit/Mvc/Model/SnapshotTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -316,4 +316,90 @@ function () {
}
);
}

/**
* Tests get updated fields new instance exception
*
* @author Wojciech Ślawski <[email protected]>
* @since 2017-03-28
*/
public function testUpdatedFieldsNewException()
{
$this->specify(
'When getting updated fields from not persistent instance there should be exception',
function () {
$robots = new Robots(
[
'name' => 'test',
'year' => 2017,
'datetime' => (new \DateTime())->format('Y-m-d'),
'text' => 'asd',
]
);

$robots->getUpdatedFields();
},
[
'throws' => ['Phalcon\Mvc\Model\Exception', 'The record doesn\'t have a valid data snapshot'],
]
);
}

/**
* Tests get updated fields deleted instance exception
*
* @author Wojciech Ślawski <[email protected]>
* @since 2017-03-28
*/
public function testUpdatedFieldsDeleteException()
{
$this->specify(
'When getting updated fields from deleted instance there should be exception',
function () {
$robots = new Robots(
[
'name' => 'test',
'year' => 2017,
'datetime' => (new \DateTime())->format('Y-m-d'),
'text' => 'asd',
]
);

$robots->create();
$robots->delete();

$robots->getUpdatedFields();
},
[
'throws' => [
'Phalcon\Mvc\Model\Exception',
'Change checking cannot be performed because the object has not been persisted or is deleted',
],
]
);
}

/**
* Tests get updated fields
*
* @author Wojciech Ślawski <[email protected]>
* @since 2017-03-28
*/
public function testUpdatedFields()
{
$this->specify(
'Getting updated fields is not working correctly',
function () {
$robots = Robots::findFirst();
$robots->name = 'changedName';
expect($robots->getSnapshotData())->notEmpty();
expect($robots->hasChanged('name'))->true();
expect($robots->hasUpdated('name'))->false();
$robots->save();
expect($robots->getSnapshotData())->notEmpty();
expect($robots->hasChanged('name'))->false();
expect($robots->hasUpdated('name'))->true();
}
);
}
}

0 comments on commit 50fe927

Please sign in to comment.