orm: isAttributeChanged improvement

This commit is contained in:
yuri
2018-07-24 18:22:04 +03:00
parent 1f2efcd716
commit e1a76b9924
5 changed files with 330 additions and 30 deletions
+25 -19
View File
@@ -718,7 +718,7 @@ abstract class Mapper implements IMapper
public function insert(IEntity $entity)
{
$dataArr = $this->toArray($entity);
$dataArr = $this->toValueMap($entity);
$fieldArr = array();
$valArr = array();
@@ -745,33 +745,35 @@ abstract class Mapper implements IMapper
public function update(IEntity $entity)
{
$dataArr = $this->toArray($entity);
$valueMap = $this->toValueMap($entity);
$setArr = array();
foreach ($dataArr as $field => $value) {
if ($field == 'id') {
$setArr = [];
foreach ($valueMap as $attribute => $value) {
if ($attribute == 'id') {
continue;
}
$type = $entity->fields[$field]['type'];
$type = $entity->getAttributeType($attribute);
if ($type == IEntity::FOREIGN) {
continue;
}
if ($entity->getFetched($field) === $value && $type != IEntity::JSON_ARRAY && $type != IEntity::JSON_OBJECT) {
if (!$entity->isAttributeChanged($attribute)) {
continue;
}
$value = $this->prepareValueForInsert($type, $value);
$setArr[] = "`" . $this->toDb($field) . "` = " . $this->quote($value);
$setArr[] = "`" . $this->toDb($attribute) . "` = " . $this->quote($value);
}
if (count($setArr) == 0) {
return $entity->id;
}
$setPart = implode(', ', $setArr);
$wherePart = $this->query->getWhere($entity, array('id' => $entity->id, 'deleted' => 0));
$wherePart = $this->query->getWhere($entity, ['id' => $entity->id, 'deleted' => 0]);
$sql = $this->composeUpdateQuery($this->toDb($entity->getEntityType()), $setPart, $wherePart);
@@ -815,21 +817,25 @@ abstract class Mapper implements IMapper
return $this->update($entity);
}
protected function toArray(IEntity $entity, $onlyStorable = true)
protected function toValueMap(IEntity $entity, $onlyStorable = true)
{
$arr = array();
foreach ($entity->fields as $field => $fieldDefs) {
if ($entity->has($field)) {
$data = [];
foreach ($entity->getAttributes() as $attribute => $defs) {
if ($entity->has($attribute)) {
if ($onlyStorable) {
if (!empty($fieldDefs['notStorable']) || !empty($fieldDefs['autoincrement']) || isset($fieldDefs['source']) && $fieldDefs['source'] != 'db')
continue;
if ($fieldDefs['type'] == IEntity::FOREIGN)
continue;
if (
!empty($defs['notStorable'])
||
!empty($defs['autoincrement'])
||
isset($defs['source']) && $defs['source'] != 'db'
) continue;
if ($defs['type'] == IEntity::FOREIGN) continue;
}
$arr[$field] = $entity->get($field);
$data[$attribute] = $entity->get($attribute);
}
}
return $arr;
return $data;
}
protected function fromRow(IEntity $entity, $data)
+67 -9
View File
@@ -369,25 +369,83 @@ abstract class Entity implements IEntity
return $this->isFetched;
}
public function isFieldChanged($fieldName)
public function isFieldChanged($name)
{
return $this->has($fieldName) && ($this->get($fieldName) != $this->getFetched($fieldName));
return $this->has($name) && ($this->get($name) != $this->getFetched($name));
}
public function isAttributeChanged($fieldName)
public function isAttributeChanged($name)
{
return $this->has($fieldName) && ($this->get($fieldName) != $this->getFetched($fieldName));
if (!$this->has($name)) return false;
if (!$this->hasFetched($name)) {
return true;
}
return !self::areValuesEqual($this->getAttributeType($name), $this->get($name), $this->getFetched($name));
return $this->get($name) != $this->getFetched($name);
}
public function setFetched($fieldName, $value)
public static function areValuesEqual($type, $v1, $v2)
{
$this->fetchedValuesContainer[$fieldName] = $value;
if ($type === self::JSON_ARRAY) {
if (is_array($v1) && is_array($v2)) {
if ($v1 != $v2) {
return false;
}
foreach ($v1 as $i => $itemValue) {
if (is_object($v1[$i]) && is_object($v2[$i])) {
if (!self::areValuesEqual(self::JSON_OBJECT, $v1[$i], $v2[$i])) {
return false;
}
continue;
}
if ($v1[$i] !== $v2[$i]) {
return false;
}
}
return true;
}
} else if ($type === self::JSON_OBJECT) {
if (is_object($v1) && is_object($v2)) {
if ($v1 != $v2) {
return false;
}
$a1 = get_object_vars($v1);
$a2 = get_object_vars($v2);
foreach ($v1 as $key => $itemValue) {
if (is_object($a1[$key]) && is_object($a2[$key])) {
if (!self::areValuesEqual(self::JSON_OBJECT, $a1[$key], $a2[$key])) {
return false;
}
continue;
}
if (is_array($a1[$key]) && is_array($a2[$key])) {
if (!self::areValuesEqual(self::JSON_ARRAY, $a1[$key], $a2[$key])) {
return false;
}
continue;
}
if ($a1[$key] !== $a2[$key]) {
return false;
}
}
return true;
}
}
return $v1 === $v2;
}
public function getFetched($fieldName)
public function setFetched($name, $value)
{
if (isset($this->fetchedValuesContainer[$fieldName])) {
return $this->fetchedValuesContainer[$fieldName];
$this->fetchedValuesContainer[$name] = $value;
}
public function getFetched($name)
{
if (isset($this->fetchedValuesContainer[$name])) {
return $this->fetchedValuesContainer[$name];
}
return null;
}
+32 -2
View File
@@ -35,6 +35,7 @@ use Espo\Entities\Post;
use Espo\Entities\Comment;
use Espo\Entities\Tag;
use Espo\Entities\Note;
use Espo\Entities\Job;
require_once 'tests/unit/testData/DB/Entities.php';
require_once 'tests/unit/testData/DB/MockPDO.php';
@@ -348,6 +349,36 @@ class DBMapperTest extends \PHPUnit\Framework\TestCase
$this->db->update($this->post);
}
public function testUpdateArray1()
{
$query = "UPDATE `job` SET `array` = '[\"2\",\"1\"]' WHERE job.id = '1' AND job.deleted = '0'";
$this->mockQuery($query, true);
$job = new \Espo\Entities\Job();
$job->id = '1';
$job->setFetched('array', ['1', '2']);
$job->set('array', ['2', '1']);
$this->db->update($job);
}
public function testUpdateArray2()
{
$query = "UPDATE `job` SET `array` = NULL WHERE job.id = '1' AND job.deleted = '0'";
$this->mockQuery($query, true);
$job = new \Espo\Entities\Job();
$job->id = '1';
$job->setFetched('array', ['1', '2']);
$job->set('array', null);
$this->db->update($job);
}
public function testRemoveRelationHasMany()
{
$query = "UPDATE `comment` SET post_id = NULL WHERE comment.deleted = '0' AND comment.id = '100'";
@@ -448,6 +479,5 @@ class DBMapperTest extends \PHPUnit\Framework\TestCase
)
));
}
}
+181
View File
@@ -0,0 +1,181 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\ORM\DB\MysqlMapper;
use Espo\ORM\DB\Query\Mysql as Query;
use Espo\ORM\EntityFactory;
require_once 'tests/unit/testData/DB/Entities.php';
class EntityTest extends \PHPUnit\Framework\TestCase
{
protected function setUp()
{
}
protected function tearDown()
{
}
public function testIsAttributeChanged()
{
$job = new \Espo\Entities\Job();
$job->setFetched('string', 'test');
$this->assertFalse($job->isAttributeChanged('string'));
$job = new \Espo\Entities\Job();
$job->setFetched('string', 'test');
$job->set('string', 'hello');
$this->assertTrue($job->isAttributeChanged('string'));
$job = new \Espo\Entities\Job();
$job->set('string', 'hello');
$this->assertTrue($job->isAttributeChanged('string'));
$job = new \Espo\Entities\Job();
$job->set('string', null);
$this->assertTrue($job->isAttributeChanged('string'));
$job = new \Espo\Entities\Job();
$job->setFetched('array', ['1', '2']);
$job->set('array', ['2', '1']);
$this->assertTrue($job->isAttributeChanged('array'));
$job = new \Espo\Entities\Job();
$job->setFetched('array', ['1', '2']);
$job->set('array', ['1', '2']);
$this->assertFalse($job->isAttributeChanged('array'));
$job = new \Espo\Entities\Job();
$job->setFetched('array', ['1', '2']);
$job->set('array', ['1', 2]);
$this->assertTrue($job->isAttributeChanged('array'));
$job = new \Espo\Entities\Job();
$job->setFetched('array', [
(object) ['1' => 'v1']
]);
$job->set('array', [
(object) ['1' => 'v1']
]);
$this->assertFalse($job->isAttributeChanged('array'));
$job = new \Espo\Entities\Job();
$job->setFetched('array', [
(object) ['k1' => 'v1']
]);
$job->set('array', [
(object) ['k1' => 'v2']
]);
$this->assertTrue($job->isAttributeChanged('array'));
$job = new \Espo\Entities\Job();
$job->setFetched('array', [
(object) ['k1' => 'v1']
]);
$job->set('array', [
(object) ['k1' => 'v1', 'k2' => 'v2'],
]);
$this->assertTrue($job->isAttributeChanged('array'));
$job = new \Espo\Entities\Job();
$job->setFetched('array', ['1', '2']);
$job->set('array', ['1', '2', '3']);
$this->assertTrue($job->isAttributeChanged('array'));
$job = new \Espo\Entities\Job();
$job->set('array', ['1', '2', '3']);
$this->assertTrue($job->isAttributeChanged('array'));
$job = new \Espo\Entities\Job();
$job->set('array', null);
$this->assertTrue($job->isAttributeChanged('array'));
$job = new \Espo\Entities\Job();
$job->setFetched('array', null);
$this->assertFalse($job->isAttributeChanged('array'));
$job = new \Espo\Entities\Job();
$job->setFetched('object', (object) ['1' => 'value-1']);
$job->set('object', (object) ['1' => 'value-1']);
$this->assertFalse($job->isAttributeChanged('object'));
$job = new \Espo\Entities\Job();
$job->setFetched('object', (object) ['1' => 'value-1']);
$job->set('object', ['1' => 'value-1']);
$this->assertTrue($job->isAttributeChanged('object'));
$job = new \Espo\Entities\Job();
$job->setFetched('object', (object) ['1' => '1']);
$job->set('object', (object) ['1' => 1]);
$this->assertTrue($job->isAttributeChanged('object'));
$job = new \Espo\Entities\Job();
$job->setFetched('object', (object) [
'k1' => (object) [
'k11' => 'v1'
]
]);
$job->set('object', (object) [
'k1' => (object) [
'k11' => 'v2'
]
]);
$this->assertTrue($job->isAttributeChanged('object'));
$job = new \Espo\Entities\Job();
$job->setFetched('object', (object) [
'k1' => [
'k11' => 'v1'
]
]);
$job->set('object', (object) [
'k1' => (object) [
'k11' => 'v1'
]
]);
$this->assertTrue($job->isAttributeChanged('object'));
$job = new \Espo\Entities\Job();
$job->setFetched('object', [
'k1' => [
'k11' => 'v1'
]
]);
$job->set('object', (object) [
'k1' => (object) [
'k11' => 'v1'
]
]);
$this->assertTrue($job->isAttributeChanged('object'));
}
}
+25
View File
@@ -242,3 +242,28 @@ class Article extends TEntity
)
);
}
class Job extends TEntity
{
public $fields = array(
'id' => array(
'type' => Entity::ID
),
'string' => array(
'type' => Entity::VARCHAR,
'len' => 50
),
'array' => array(
'type' => Entity::JSON_ARRAY
),
'object' => array(
'type' => Entity::JSON_OBJECT
),
'deleted' => array(
'type' => Entity::BOOL,
'default' => 0
)
);
}