orm: isAttributeChanged improvement
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user