Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix snapshot setting when create/update operation made #12678

Merged
merged 1 commit into from
Mar 4, 2017
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- Added `prepend` parameter to `Phalcon\Loader::register` to specify autoloader's loading order to top most
- Fixed `Phalcon\Session\Bag::remove` to initialize the bag before removing a value [#12647](https://github.com/phalcon/cphalcon/pull/12647)
- Fixed `Phalcon\Mvc\Model::getChangedFields` to correct detect changes from NULL to Zero [#12628](https://github.com/phalcon/cphalcon/issues/12628)
- Fixed `Phalcon\Mvc\Model` to create/refresh snapshot after create/update/refresh operation [#11007](https://github.com/phalcon/cphalcon/issues/11007), [#11818](https://github.com/phalcon/cphalcon/issues/11818), [#11424](https://github.com/phalcon/cphalcon/issues/11424)

# [3.0.4](https://github.com/phalcon/cphalcon/releases/tag/v3.0.4) (2017-02-20)
- Fixed Isnull check is not correct when the model field defaults to an empty string. [#12507](https://github.com/phalcon/cphalcon/issues/12507)
Expand Down
46 changes: 38 additions & 8 deletions phalcon/mvc/model.zep
Original file line number Diff line number Diff line change
Expand Up @@ -2237,13 +2237,15 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
{
var bindSkip, fields, values, bindTypes, attributes, bindDataTypes, automaticAttributes,
field, columnMap, value, attributeField, success, bindType,
defaultValue, sequenceName, defaultValues, source, schema;
defaultValue, sequenceName, defaultValues, source, schema, snapshot, lastInsertedId, manager;
boolean useExplicitIdentity;

let bindSkip = Column::BIND_SKIP;
let manager = <ManagerInterface> this->_modelsManager;

let fields = [],
values = [],
snapshot = [],
bindTypes = [];

let attributes = metaData->getAttributes(this),
Expand Down Expand Up @@ -2297,13 +2299,18 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
}

let fields[] = field, values[] = value, bindTypes[] = bindType;

let snapshot[attributeField] = value;
} else {

if isset defaultValues[field] {
let values[] = connection->getDefaultValue();
/**
* This is default value so we set null, keep in mind it's value in database!
*/
let snapshot[attributeField] = null;
} else {
let values[] = value;
let snapshot[attributeField] = value;
}

let fields[] = field, bindTypes[] = bindSkip;
Expand Down Expand Up @@ -2401,7 +2408,14 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
/**
* Recover the last "insert id" and assign it to the object
*/
let this->{attributeField} = connection->lastInsertId(sequenceName);
let lastInsertedId = connection->lastInsertId(sequenceName);

let this->{attributeField} = lastInsertedId;
let snapshot[attributeField] = lastInsertedId;

if manager->isKeepingSnapshots(this) {
let this->_snapshot = snapshot;
}

/**
* Since the primary key was modified, we delete the _uniqueParams
Expand All @@ -2425,13 +2439,14 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
{
var bindSkip, fields, values, dataType, dataTypes, bindTypes, manager, bindDataTypes, field,
automaticAttributes, snapshotValue, uniqueKey, uniqueParams, uniqueTypes,
snapshot, nonPrimary, columnMap, attributeField, value, primaryKeys, bindType;
snapshot, nonPrimary, columnMap, attributeField, value, primaryKeys, bindType, newSnapshot, success;
boolean useDynamicUpdate, changed;

let bindSkip = Column::BIND_SKIP,
fields = [],
values = [],
bindTypes = [],
newSnapshot = [],
manager = <ManagerInterface> this->_modelsManager;

/**
Expand Down Expand Up @@ -2564,8 +2579,10 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
let bindTypes[] = bindType;
}
}
let newSnapshot[attributeField] = value;

} else {
let newSnapshot[attributeField] = null;
let fields[] = field, values[] = null, bindTypes[] = bindSkip;
}
}
Expand Down Expand Up @@ -2611,8 +2628,10 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
}

if fetch value, this->{attributeField} {
let newSnapshot[attributeField] = value;
let uniqueParams[] = value;
} else {
let newSnapshot[attributeField] = null;
let uniqueParams[] = null;
}
}
Expand All @@ -2622,11 +2641,17 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
* We build the conditions as an array
* Perform the low level update
*/
return connection->update(table, fields, values, [
let success = connection->update(table, fields, values, [
"conditions" : uniqueKey,
"bind" : uniqueParams,
"bindTypes" : uniqueTypes
], bindTypes);

if success && manager->isKeepingSnapshots(this) {
let this->_snapshot = array_merge(this->_snapshot, newSnapshot);
}

return success;
}

/**
Expand Down Expand Up @@ -3343,14 +3368,15 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
public function refresh() -> <Model>
{
var metaData, readConnection, schema, source, table,
uniqueKey, tables, uniqueParams, dialect, row, fields, attribute;
uniqueKey, tables, uniqueParams, dialect, row, fields, attribute, manager, columnMap;

if this->_dirtyState != self::DIRTY_STATE_PERSISTENT {
throw new Exception("The record cannot be refreshed because it does not exist or is deleted");
}

let metaData = this->getModelsMetaData(),
readConnection = this->getReadConnection();
readConnection = this->getReadConnection(),
manager = <ManagerInterface> this->_modelsManager;

let schema = this->getSchema(),
source = this->getSource();
Expand Down Expand Up @@ -3403,7 +3429,11 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface
* Assign the resulting array to the this object
*/
if typeof row == "array" {
this->assign(row, metaData->getColumnMap(this));
let columnMap = metaData->getColumnMap(this);
this->assign(row, columnMap);
if manager->isKeepingSnapshots(this) {
this->setSnapshotData(row, columnMap);
}
}

return this;
Expand Down
72 changes: 72 additions & 0 deletions tests/unit/Mvc/Model/SnapshotTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,76 @@ function () {
}
);
}

/**
* When model is created/updated snapshot should be set/updated
*
* @issue 11007, 11818, 11424
* @author Wojciech Ślawski <[email protected]>
* @since 2017-03-03
*/
public function testIssue11007()
{
$this->specify(
'Snapshot is not created/updated when create/update operation was made',
function () {
$this->setUpModelsManager();
$robots = new Robots(
[
'name' => 'test',
'type' => 'mechanical',
'year' => 2017,
'datetime' => (new \DateTime())->format('Y-m-d'),
'text' => 'asd',
]
);

expect($robots->create())->true();
expect($robots->getSnapshotData())->notEmpty();
expect($robots->getSnapshotData())->equals($robots->toArray());

$robots->name = "testabc";
expect($robots->hasChanged('name'))->true();
expect($robots->update())->true();
expect($robots->name)->equals("testabc");
expect($robots->getSnapshotData())->notEmpty();
expect($robots->getSnapshotData())->equals($robots->toArray());
expect($robots->hasChanged('name'))->false();
}
);
}

/**
* When model is refreshed snapshot should be updated
*
* @issue 11007, 11818, 11424
* @author Wojciech Ślawski <[email protected]>
* @since 2017-03-03
*/
public function testIssue11007Refresh()
{
$this->specify(
'Snapshot is not updated when refresh operation was made',
function () {
$this->setUpModelsManager();
$robots = new Robots(
[
'name' => 'test',
'year' => 2017,
'datetime' => (new \DateTime())->format('Y-m-d'),
'text' => 'asd',
]
);

expect($robots->create())->true();
expect($robots->getSnapshotData())->notEmpty();
expect($robots->getSnapshotData())->equals($robots->toArray());

expect($robots->refresh())->isInstanceOf(Robots::class);
expect($robots->type)->equals('mechanical');
expect($robots->getSnapshotData())->notEmpty();
expect($robots->getSnapshotData())->equals($robots->toArray());
}
);
}
}
8 changes: 4 additions & 4 deletions unit-tests/ModelsDynamicOperationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,17 @@ protected function _executeTestsNormal($di, &$tracer)
$this->assertEquals(count($tracer), 3);

$persona->nombres = 'Other Name '.mt_rand(0, 150000);
$this->assertEquals($persona->getChangedFields(), array('nombres'));
$this->assertTrue($persona->save());

$this->assertEquals('UPDATE `personas` SET `nombres` = ? WHERE `cedula` = ?', $tracer[3]);
$this->assertEquals($persona->getChangedFields(), array('nombres'));

$persona->nombres = 'Other Name '.mt_rand(0, 150000);
$persona->direccion = 'Address '.mt_rand(0, 150000);
$this->assertEquals($persona->getChangedFields(), array('nombres', 'direccion'));
$this->assertTrue($persona->save());

$this->assertEquals('UPDATE `personas` SET `nombres` = ?, `direccion` = ? WHERE `cedula` = ?', $tracer[4]);
$this->assertEquals($persona->getChangedFields(), array('nombres', 'direccion'));
}

protected function _executeTestsRenamed($di, &$tracer)
Expand All @@ -124,17 +124,17 @@ protected function _executeTestsRenamed($di, &$tracer)
$this->assertEquals(count($tracer), 3);

$personer->navnes = 'Other Name '.mt_rand(0, 150000);
$this->assertEquals($personer->getChangedFields(), array('navnes'));
$this->assertTrue($personer->save());

$this->assertEquals('UPDATE `personas` SET `nombres` = ? WHERE `cedula` = ?', $tracer[3]);
$this->assertEquals($personer->getChangedFields(), array('navnes'));

$personer->navnes = 'Other Name '.mt_rand(0, 150000);
$personer->adresse = 'Address '.mt_rand(0, 150000);
$this->assertEquals($personer->getChangedFields(), array('navnes', 'adresse'));
$this->assertTrue($personer->save());

$this->assertEquals('UPDATE `personas` SET `nombres` = ?, `direccion` = ? WHERE `cedula` = ?', $tracer[4]);
$this->assertEquals($personer->getChangedFields(), array('navnes', 'adresse'));
}

}