Skip to content

Commit 5296afc

Browse files
committed
Merge pull request #106 from AlexeyDsov/storableDaoUnite
Улучшение сравнения измененных свойст объекта для создания SQL Update
2 parents 32b7681 + b6e7a56 commit 5296afc

File tree

6 files changed

+292
-37
lines changed

6 files changed

+292
-37
lines changed

doc/ChangeLog

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
2012-06-27 Alexey S. Denisov
2+
3+
* main/Base/AbstractProtoClass.class.php
4+
main/Base/InnerMetaProperty.class.php
5+
main/Base/LightMetaProperty.class.php
6+
main/DAOs/StorableDAO.class.php
7+
test/core/AbstractProtoClassFillQueryTest.class.php:
8+
9+
moved and updated StorableDAO::unite "properties compare logic" to AbstractProtoClass
10+
and (Inner|Light)MetaProperty and covered with tests
11+
112
2012-06-04 Evgeny M. Stepanov
213

314
* main/Base/Hstore.class.php: fix
@@ -12,7 +23,7 @@
1223
core/Cache/PeclMemcached.class.php
1324
core/Cache/{Memcached.class.php → SocketMemcached.class.php}
1425

15-
Code clean & add SocketMemcached instead of Memcached
26+
Code clean & add SocketMemcached instead of Memcached
1627

1728
2012-05-23 Georgiy T. Kutsurua
1829

main/Base/AbstractProtoClass.class.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,24 @@ public function makeForm($prefix = null)
211211
* @return InsertOrUpdateQuery
212212
**/
213213
public function fillQuery(
214-
InsertOrUpdateQuery $query, Prototyped $object
214+
InsertOrUpdateQuery $query,
215+
Prototyped $object,
216+
Prototyped $old = null
215217
)
216218
{
219+
if ($old) {
220+
if ($object instanceof Identifiable) {
221+
Assert::isNotNull($object->getId());
222+
223+
Assert::isTypelessEqual(
224+
$object->getId(), $old->getId(),
225+
'cannot merge different objects'
226+
);
227+
}
228+
}
229+
217230
foreach ($this->getPropertyList() as $property) {
218-
$property->fillQuery($query, $object);
231+
$property->fillQuery($query, $object, $old);
219232
}
220233

221234
return $query;

main/Base/InnerMetaProperty.class.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,20 @@ public function fillForm(Form $form, $prefix = null)
5252

5353
public function fillQuery(
5454
InsertOrUpdateQuery $query,
55-
Prototyped $object
55+
Prototyped $object,
56+
Prototyped $old = null
5657
)
5758
{
59+
$inner = $object->{$this->getGetter()}();
60+
$oldInner = $old ? $old->{$this->getGetter()}() : null;
61+
5862
return
5963
$this->getProto()->fillQuery(
6064
$query,
61-
$object->{$this->getGetter()}()
65+
$inner,
66+
//when old and objects have one value object
67+
// we'll update all valueObject fields:
68+
$oldInner !== $inner ? $oldInner : null
6269
);
6370
}
6471

main/Base/LightMetaProperty.class.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,8 @@ public function fillForm(Form $form, $prefix = null)
346346
**/
347347
public function fillQuery(
348348
InsertOrUpdateQuery $query,
349-
Prototyped $object
349+
Prototyped $object,
350+
Prototyped $old = null
350351
)
351352
{
352353
if (
@@ -370,6 +371,26 @@ public function fillQuery(
370371
}
371372

372373
$value = $object->{$getter}();
374+
if ($old) {
375+
$oldValue = $old->{$getter}();
376+
if ($oldValue === null && $value === $oldValue) {
377+
return $query;
378+
} elseif (
379+
$this->relationId
380+
&& $this->strategyId == FetchStrategy::LAZY
381+
&& ($value === $oldValue)
382+
) {
383+
return $query;
384+
} elseif (
385+
$value instanceof Identifiable
386+
&& $oldValue instanceof Identifiable
387+
&& $value->getId() === $oldValue->getId()
388+
) {
389+
return $query;
390+
} elseif (serialize($value) == serialize($oldValue)) {
391+
return $query;
392+
}
393+
}
373394

374395
if ($this->type == 'binary') {
375396
$query->set($this->columnName, new DBBinary($value));

main/DAOs/StorableDAO.class.php

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -77,40 +77,16 @@ public function unite(
7777
Identifiable $object, Identifiable $old
7878
)
7979
{
80-
Assert::isNotNull($object->getId());
81-
82-
Assert::isTypelessEqual(
83-
$object->getId(), $old->getId(),
84-
'cannot merge different objects'
85-
);
86-
87-
$query = OSQL::update($this->getTable());
88-
89-
foreach ($this->getProtoClass()->getPropertyList() as $property) {
90-
$getter = $property->getGetter();
91-
92-
if ($property->getClassName() === null) {
93-
$changed = ($old->$getter() !== $object->$getter());
94-
} else {
95-
/**
96-
* way to skip pointless update and hack for recursive
97-
* comparsion.
98-
**/
99-
$changed =
100-
($old->$getter() !== $object->$getter())
101-
|| ($old->$getter() != $object->$getter());
102-
}
103-
104-
if ($changed)
105-
$property->fillQuery($query, $object);
106-
}
80+
$query = $this->getProtoClass()
81+
->fillQuery(OSQL::update($this->getTable()), $object, $old);
10782

10883
if (!$query->getFieldsCount())
10984
return $object;
11085

111-
$this->targetizeUpdateQuery($query, $object);
112-
113-
return $this->doInject($query, $object);
86+
return $this->doInject(
87+
$this->targetizeUpdateQuery($query, $object),
88+
$object
89+
);
11490
}
11591

11692
/**
@@ -124,4 +100,4 @@ private function targetizeUpdateQuery(
124100
return $query->where(Expression::eqId($this->getIdName(), $object));
125101
}
126102
}
127-
?>
103+
?>
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
<?php
2+
final class AbstractProtoClassFillQueryTest extends TestCase
3+
{
4+
public function testFullInsertQueryByCity()
5+
{
6+
$city = $this->spawnCity();
7+
8+
$insertCity = $city->proto()->fillQuery(OSQL::insert(), $city);
9+
$this->assertEquals(
10+
'INSERT INTO (id, name, capital, large) VALUES (20, Saint-Peterburg, TRUE, TRUE)',
11+
$insertCity->toDialectString(ImaginaryDialect::me())
12+
);
13+
}
14+
15+
public function testFullUpdateQueryByUser()
16+
{
17+
$city = $this->spawnCity();
18+
$user = $this->spawnUser(array('city' => $city));
19+
$updateUser = $user->proto()->fillQuery(OSQL::update(), $user);
20+
$this->assertEquals(
21+
'UPDATE SET id = 77, nickname = NULL, password = NULL, '
22+
.'very_custom_field_name = 2011-12-31 00:00:00, '
23+
.'registered = 2011-12-30 00:00:00, strange_time = 01:23:45, '
24+
.'city_id = 20, first_optional_id = NULL, second_optional_id = NULL, '
25+
.'url = https://www.github.com, '
26+
.'properties = "a"=>"apple","b"=>"bananas",, ip = 127.0.0.1',
27+
$updateUser->toDialectString(ImaginaryDialect::me())
28+
);
29+
}
30+
31+
public function testFullUpdateQueryByUserWithContactExt()
32+
{
33+
$contactValue = $this->spawnContactValueExt();
34+
$user = $this->spawnUserWithContactExt(array('contactExt' => $contactValue));
35+
36+
$updateUser = $user->proto()->fillQuery(OSQL::update(), $user);
37+
$this->assertEquals(
38+
'UPDATE SET id = 77, name = Aleksey, surname = Alekseev, email = [email protected], '
39+
.'icq = 12345678, phone = 89012345678, city_id = NULL, '
40+
.'web = https://www.github.com/, skype = github',
41+
$updateUser->toDialectString(ImaginaryDialect::me())
42+
);
43+
}
44+
45+
public function testUpdateQueryByCityOneBoolean()
46+
{
47+
$cityOld = $this->spawnCity(array('capital' => false));
48+
$city = $this->spawnCity(array('capital' => true)); //1918
49+
50+
$updateCity = $city->proto()->fillQuery(OSQL::update(), $city, $cityOld);
51+
$this->assertEquals(
52+
'UPDATE SET capital = TRUE',
53+
$updateCity->toDialectString(ImaginaryDialect::me())
54+
);
55+
}
56+
57+
public function testUpdateQueryByCityOneString()
58+
{
59+
$cityOld = $this->spawnCity(array('name' => 'Leningrad'));
60+
$city = $this->spawnCity(array('name' => 'Saint-Peterburg'));
61+
62+
$updateCity = $city->proto()->fillQuery(OSQL::update(), $city, $cityOld);
63+
$this->assertEquals(
64+
'UPDATE SET name = Saint-Peterburg',
65+
$updateCity->toDialectString(ImaginaryDialect::me())
66+
);
67+
}
68+
69+
public function testUpdateQueryByUserTimesAndUrl()
70+
{
71+
$oldUser = $this->spawnUser(array(
72+
'strangeTime' => Time::create('12:12:12'),
73+
'url' => HttpUrl::create()->parse('http://code.google.com/'),
74+
));
75+
$user = $this->spawnUser(array('lastLogin' => Timestamp::create('2012-01-01')));
76+
77+
$updateUser = $user->proto()->fillQuery(OSQL::update(), $user, $oldUser);
78+
$this->assertEquals(
79+
'UPDATE SET very_custom_field_name = 2012-01-01 00:00:00, '
80+
.'strange_time = 01:23:45, url = https://www.github.com',
81+
$updateUser->toDialectString(ImaginaryDialect::me())
82+
);
83+
}
84+
85+
public function testUpdateQueryByUserCitiesAndHstore()
86+
{
87+
$moscow = $this->spawnCity(array('id' => 1, 'name' => 'Moscow'));
88+
$piter = $this->spawnCity(array('id' => 2, 'name' => 'Saint-Peterburg'));
89+
$omsk = $this->spawnCity(array('id' => 3, 'name' => 'Omsk'));
90+
91+
$userParams = array(
92+
'city' => $moscow,
93+
'firstOptional' => $piter,
94+
'secondOptional' => null,
95+
'properties' => Hstore::make(array()),
96+
);
97+
$oldUser = $this->spawnUser($userParams);
98+
$userParams = array(
99+
'city' => $piter,
100+
'firstOptional' => null,
101+
'secondOptional' => $omsk,
102+
'properties' => Hstore::make(array('param' => 'value')),
103+
);
104+
$user = $this->spawnUser($userParams);
105+
106+
$updateUser = $user->proto()->fillQuery(OSQL::update(), $user, $oldUser);
107+
$this->assertEquals(
108+
'UPDATE SET city_id = 2, first_optional_id = NULL, '
109+
.'second_optional_id = 3, properties = "param"=>"value",',
110+
$updateUser->toDialectString(ImaginaryDialect::me())
111+
);
112+
}
113+
114+
public function testUpdateQueryByUserWithValueObject()
115+
{
116+
$moscow = $this->spawnCity();
117+
$oldContactExt = $this->spawnContactValueExt(array('email' => '[email protected]'));
118+
$oldUser = $this->spawnUserWithContactExt(array('contactExt' => $oldContactExt));
119+
$contactExt = $this->spawnContactValueExt(array('city' => $moscow));
120+
$user = $this->spawnUserWithContactExt(array('contactExt' => $contactExt));
121+
122+
$updateUser = $user->proto()->fillQuery(OSQL::update(), $user, $oldUser);
123+
$this->assertEquals(
124+
'UPDATE SET email = [email protected], city_id = 20',
125+
$updateUser->toDialectString(ImaginaryDialect::me())
126+
);
127+
}
128+
129+
public function testUpdateQueryByUserWithSameValueObject()
130+
{
131+
//if value object same for both main objects - we'll update all fields from value object
132+
$contactExt = $this->spawnContactValueExt();
133+
$oldUser = $this->spawnUserWithContactExt(array('contactExt' => $contactExt));
134+
$user = $this->spawnUserWithContactExt(array('contactExt' => $contactExt));
135+
136+
$updateUser = $user->proto()->fillQuery(OSQL::update(), $user, $oldUser);
137+
$this->assertEquals(
138+
'UPDATE SET email = [email protected], icq = 12345678, '
139+
.'phone = 89012345678, city_id = NULL, '
140+
.'web = https://www.github.com/, skype = github',
141+
$updateUser->toDialectString(ImaginaryDialect::me())
142+
);
143+
}
144+
145+
/**
146+
* @return TestCity
147+
*/
148+
private function spawnCity($options = array())
149+
{
150+
$options += array(
151+
'capital' => true,
152+
'large' => true,
153+
'name' => 'Saint-Peterburg',
154+
'id' => 20,
155+
);
156+
157+
return $this->spawnObject(TestCity::create(), $options);
158+
}
159+
160+
/**
161+
* @return TestUser
162+
*/
163+
private function spawnUser($options = array())
164+
{
165+
$options += array(
166+
'id' => '77',
167+
'credentials' => Credentials::create(),
168+
'lastLogin' => Timestamp::create('2011-12-31'),
169+
'registered' => Timestamp::create('2011-12-30'),
170+
'strangeTime' => Time::create('01:23:45'),
171+
'city' => null,
172+
'firstOptional' => null,
173+
'secondOptional' => null,
174+
'url' => HttpUrl::create()->parse('https://www.github.com'),
175+
'properties' => Hstore::make(array('a' => 'apple', 'b' => 'bananas')),
176+
'ip' => IpAddress::create('127.0.0.1'),
177+
);
178+
179+
return $this->spawnObject(TestUser::create(), $options);
180+
181+
}
182+
183+
/**
184+
* @return TestContactValueExtended
185+
*/
186+
private function spawnContactValueExt($options = array())
187+
{
188+
$options += array(
189+
'web' => 'https://www.github.com/',
190+
'skype' => 'github',
191+
'email' => '[email protected]',
192+
'icq' => 12345678,
193+
'phone' => '89012345678',
194+
'city' => null,
195+
);
196+
197+
return $this->spawnObject(TestContactValueExtended::create(), $options);
198+
}
199+
200+
/**
201+
* @return TestUserWithContactExtended
202+
*/
203+
private function spawnUserWithContactExt($options = array())
204+
{
205+
$options += array(
206+
'id' => '77',
207+
'name' => 'Aleksey',
208+
'surname' => 'Alekseev',
209+
'contactExt' => null,
210+
);
211+
212+
return $this->spawnObject(TestUserWithContactExtended::create(), $options);
213+
}
214+
215+
private function spawnObject(Prototyped $object, array $options)
216+
{
217+
foreach ($object->proto()->getPropertyList() as $propName => $property) {
218+
/* @var $property LightMetaProperty */
219+
if (isset($options[$propName])) {
220+
$setter = $property->getSetter();
221+
$object->{$setter}($options[$propName]);
222+
}
223+
}
224+
return $object;
225+
}
226+
}
227+
?>

0 commit comments

Comments
 (0)