Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like DataObjectTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use DataObjectTest, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
6 | class DataObjectTest extends SapphireTest { |
||
7 | |||
8 | protected static $fixture_file = 'DataObjectTest.yml'; |
||
9 | |||
10 | protected $extraDataObjects = array( |
||
11 | 'DataObjectTest_Team', |
||
12 | 'DataObjectTest_Fixture', |
||
13 | 'DataObjectTest_SubTeam', |
||
14 | 'OtherSubclassWithSameField', |
||
15 | 'DataObjectTest_FieldlessTable', |
||
16 | 'DataObjectTest_FieldlessSubTable', |
||
17 | 'DataObjectTest_ValidatedObject', |
||
18 | 'DataObjectTest_Player', |
||
19 | 'DataObjectTest_TeamComment', |
||
20 | 'DataObjectTest_EquipmentCompany', |
||
21 | 'DataObjectTest_SubEquipmentCompany', |
||
22 | 'DataObjectTest\NamespacedClass', |
||
23 | 'DataObjectTest\RelationClass', |
||
24 | 'DataObjectTest_ExtendedTeamComment', |
||
25 | 'DataObjectTest_Company', |
||
26 | 'DataObjectTest_Staff', |
||
27 | 'DataObjectTest_CEO', |
||
28 | 'DataObjectTest_Fan', |
||
29 | 'DataObjectTest_Play', |
||
30 | 'DataObjectTest_Ploy', |
||
31 | 'DataObjectTest_Bogey', |
||
32 | ); |
||
33 | |||
34 | public function testDb() { |
||
35 | $obj = new DataObjectTest_TeamComment(); |
||
36 | $dbFields = $obj->db(); |
||
37 | |||
38 | // Assert fields are included |
||
39 | $this->assertArrayHasKey('Name', $dbFields); |
||
40 | |||
41 | // Assert the base fields are excluded |
||
42 | $this->assertArrayNotHasKey('Created', $dbFields); |
||
43 | $this->assertArrayNotHasKey('LastEdited', $dbFields); |
||
44 | $this->assertArrayNotHasKey('ClassName', $dbFields); |
||
45 | $this->assertArrayNotHasKey('ID', $dbFields); |
||
46 | |||
47 | // Assert that the correct field type is returned when passing a field |
||
48 | $this->assertEquals('Varchar', $obj->db('Name')); |
||
49 | $this->assertEquals('Text', $obj->db('Comment')); |
||
50 | |||
51 | $obj = new DataObjectTest_ExtendedTeamComment(); |
||
52 | $dbFields = $obj->db(); |
||
53 | |||
54 | // Assert overloaded fields have correct data type |
||
55 | $this->assertEquals('HTMLText', $obj->db('Comment')); |
||
56 | $this->assertEquals('HTMLText', $dbFields['Comment'], |
||
57 | 'Calls to DataObject::db without a field specified return correct data types'); |
||
58 | |||
59 | // assertEquals doesn't verify the order of array elements, so access keys manually to check order: |
||
60 | // expected: array('Name' => 'Varchar', 'Comment' => 'HTMLText') |
||
61 | reset($dbFields); |
||
62 | $this->assertEquals('Name', key($dbFields), 'DataObject::db returns fields in correct order'); |
||
63 | next($dbFields); |
||
64 | $this->assertEquals('Comment', key($dbFields), 'DataObject::db returns fields in correct order'); |
||
65 | } |
||
66 | |||
67 | public function testConstructAcceptsValues() { |
||
68 | // Values can be an array... |
||
69 | $player = new DataObjectTest_Player(array( |
||
70 | 'FirstName' => 'James', |
||
71 | 'Surname' => 'Smith' |
||
72 | )); |
||
73 | |||
74 | $this->assertEquals('James', $player->FirstName); |
||
75 | $this->assertEquals('Smith', $player->Surname); |
||
76 | |||
77 | // ... or a stdClass inst |
||
78 | $data = new stdClass(); |
||
79 | $data->FirstName = 'John'; |
||
80 | $data->Surname = 'Doe'; |
||
81 | $player = new DataObjectTest_Player($data); |
||
|
|||
82 | |||
83 | $this->assertEquals('John', $player->FirstName); |
||
84 | $this->assertEquals('Doe', $player->Surname); |
||
85 | |||
86 | // IDs should be stored as integers, not strings |
||
87 | $player = new DataObjectTest_Player(array('ID' => '5')); |
||
88 | $this->assertSame(5, $player->ID); |
||
89 | } |
||
90 | |||
91 | public function testValidObjectsForBaseFields() { |
||
92 | $obj = new DataObjectTest_ValidatedObject(); |
||
93 | |||
94 | foreach (array('Created', 'LastEdited', 'ClassName', 'ID') as $field) { |
||
95 | $helper = $obj->dbObject($field); |
||
96 | $this->assertTrue( |
||
97 | ($helper instanceof DBField), |
||
98 | "for {$field} expected helper to be DBField, but was " . |
||
99 | (is_object($helper) ? get_class($helper) : "null") |
||
100 | ); |
||
101 | } |
||
102 | } |
||
103 | |||
104 | public function testDataIntegrityWhenTwoSubclassesHaveSameField() { |
||
105 | // Save data into DataObjectTest_SubTeam.SubclassDatabaseField |
||
106 | $obj = new DataObjectTest_SubTeam(); |
||
107 | $obj->SubclassDatabaseField = "obj-SubTeam"; |
||
108 | $obj->write(); |
||
109 | |||
110 | // Change the class |
||
111 | $obj->ClassName = 'OtherSubclassWithSameField'; |
||
112 | $obj->write(); |
||
113 | $obj->flushCache(); |
||
114 | |||
115 | // Re-fetch from the database and confirm that the data is sourced from |
||
116 | // OtherSubclassWithSameField.SubclassDatabaseField |
||
117 | $obj = DataObject::get_by_id('DataObjectTest_Team', $obj->ID); |
||
118 | $this->assertNull($obj->SubclassDatabaseField); |
||
119 | |||
120 | // Confirm that save the object in the other direction. |
||
121 | $obj->SubclassDatabaseField = 'obj-Other'; |
||
122 | $obj->write(); |
||
123 | |||
124 | $obj->ClassName = 'DataObjectTest_SubTeam'; |
||
125 | $obj->write(); |
||
126 | $obj->flushCache(); |
||
127 | |||
128 | // If we restore the class, the old value has been lying dormant and will be available again. |
||
129 | // NOTE: This behaviour is volatile; we may change this in the future to clear fields that |
||
130 | // are no longer relevant when changing ClassName |
||
131 | $obj = DataObject::get_by_id('DataObjectTest_Team', $obj->ID); |
||
132 | $this->assertEquals('obj-SubTeam', $obj->SubclassDatabaseField); |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Test deletion of DataObjects |
||
137 | * - Deleting using delete() on the DataObject |
||
138 | * - Deleting using DataObject::delete_by_id() |
||
139 | */ |
||
140 | public function testDelete() { |
||
141 | // Test deleting using delete() on the DataObject |
||
142 | // Get the first page |
||
143 | $obj = $this->objFromFixture('DataObjectTest_Player', 'captain1'); |
||
144 | $objID = $obj->ID; |
||
145 | // Check the page exists before deleting |
||
146 | $this->assertTrue(is_object($obj) && $obj->exists()); |
||
147 | // Delete the page |
||
148 | $obj->delete(); |
||
149 | // Check that page does not exist after deleting |
||
150 | $obj = DataObject::get_by_id('DataObjectTest_Player', $objID); |
||
151 | $this->assertTrue(!$obj || !$obj->exists()); |
||
152 | |||
153 | |||
154 | // Test deleting using DataObject::delete_by_id() |
||
155 | // Get the second page |
||
156 | $obj = $this->objFromFixture('DataObjectTest_Player', 'captain2'); |
||
157 | $objID = $obj->ID; |
||
158 | // Check the page exists before deleting |
||
159 | $this->assertTrue(is_object($obj) && $obj->exists()); |
||
160 | // Delete the page |
||
161 | DataObject::delete_by_id('DataObjectTest_Player', $obj->ID); |
||
162 | // Check that page does not exist after deleting |
||
163 | $obj = DataObject::get_by_id('DataObjectTest_Player', $objID); |
||
164 | $this->assertTrue(!$obj || !$obj->exists()); |
||
165 | } |
||
166 | |||
167 | /** |
||
168 | * Test methods that get DataObjects |
||
169 | * - DataObject::get() |
||
170 | * - All records of a DataObject |
||
171 | * - Filtering |
||
172 | * - Sorting |
||
173 | * - Joins |
||
174 | * - Limit |
||
175 | * - Container class |
||
176 | * - DataObject::get_by_id() |
||
177 | * - DataObject::get_one() |
||
178 | * - With and without caching |
||
179 | * - With and without ordering |
||
180 | */ |
||
181 | public function testGet() { |
||
182 | // Test getting all records of a DataObject |
||
183 | $comments = DataObject::get('DataObjectTest_TeamComment'); |
||
184 | $this->assertEquals(3, $comments->Count()); |
||
185 | |||
186 | // Test WHERE clause |
||
187 | $comments = DataObject::get('DataObjectTest_TeamComment', "\"Name\"='Bob'"); |
||
188 | $this->assertEquals(1, $comments->Count()); |
||
189 | foreach($comments as $comment) { |
||
190 | $this->assertEquals('Bob', $comment->Name); |
||
191 | } |
||
192 | |||
193 | // Test sorting |
||
194 | $comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" ASC"); |
||
195 | $this->assertEquals(3, $comments->Count()); |
||
196 | $this->assertEquals('Bob', $comments->First()->Name); |
||
197 | $comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" DESC"); |
||
198 | $this->assertEquals(3, $comments->Count()); |
||
199 | $this->assertEquals('Phil', $comments->First()->Name); |
||
200 | |||
201 | // Test limit |
||
202 | $comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" ASC", '', '1,2'); |
||
203 | $this->assertEquals(2, $comments->Count()); |
||
204 | $this->assertEquals('Joe', $comments->First()->Name); |
||
205 | $this->assertEquals('Phil', $comments->Last()->Name); |
||
206 | |||
207 | // Test get_by_id() |
||
208 | $captain1ID = $this->idFromFixture('DataObjectTest_Player', 'captain1'); |
||
209 | $captain1 = DataObject::get_by_id('DataObjectTest_Player', $captain1ID); |
||
210 | $this->assertEquals('Captain', $captain1->FirstName); |
||
211 | |||
212 | // Test get_one() without caching |
||
213 | $comment1 = DataObject::get_one('DataObjectTest_TeamComment', array( |
||
214 | '"DataObjectTest_TeamComment"."Name"' => 'Joe' |
||
215 | ), false); |
||
216 | $comment1->Comment = "Something Else"; |
||
217 | |||
218 | $comment2 = DataObject::get_one('DataObjectTest_TeamComment', array( |
||
219 | '"DataObjectTest_TeamComment"."Name"' => 'Joe' |
||
220 | ), false); |
||
221 | $this->assertNotEquals($comment1->Comment, $comment2->Comment); |
||
222 | |||
223 | // Test get_one() with caching |
||
224 | $comment1 = DataObject::get_one('DataObjectTest_TeamComment', array( |
||
225 | '"DataObjectTest_TeamComment"."Name"' => 'Bob' |
||
226 | ), true); |
||
227 | $comment1->Comment = "Something Else"; |
||
228 | |||
229 | $comment2 = DataObject::get_one('DataObjectTest_TeamComment', array( |
||
230 | '"DataObjectTest_TeamComment"."Name"' => 'Bob' |
||
231 | ), true); |
||
232 | $this->assertEquals((string)$comment1->Comment, (string)$comment2->Comment); |
||
233 | |||
234 | // Test get_one() with order by without caching |
||
235 | $comment = DataObject::get_one('DataObjectTest_TeamComment', '', false, "\"Name\" ASC"); |
||
236 | $this->assertEquals('Bob', $comment->Name); |
||
237 | |||
238 | $comment = DataObject::get_one('DataObjectTest_TeamComment', '', false, "\"Name\" DESC"); |
||
239 | $this->assertEquals('Phil', $comment->Name); |
||
240 | |||
241 | // Test get_one() with order by with caching |
||
242 | $comment = DataObject::get_one('DataObjectTest_TeamComment', '', true, '"Name" ASC'); |
||
243 | $this->assertEquals('Bob', $comment->Name); |
||
244 | $comment = DataObject::get_one('DataObjectTest_TeamComment', '', true, '"Name" DESC'); |
||
245 | $this->assertEquals('Phil', $comment->Name); |
||
246 | } |
||
247 | |||
248 | View Code Duplication | public function testGetCaseInsensitive() { |
|
249 | // Test get_one() with bad case on the classname |
||
250 | // Note: This will succeed only if the underlying DB server supports case-insensitive |
||
251 | // table names (e.g. such as MySQL, but not SQLite3) |
||
252 | if(!(DB::get_conn() instanceof MySQLDatabase)) { |
||
253 | $this->markTestSkipped('MySQL only'); |
||
254 | } |
||
255 | |||
256 | $subteam1 = DataObject::get_one('dataobjecttest_subteam', array( |
||
257 | '"DataObjectTest_Team"."Title"' => 'Subteam 1' |
||
258 | ), true); |
||
259 | $this->assertNotEmpty($subteam1); |
||
260 | $this->assertEquals($subteam1->Title, "Subteam 1"); |
||
261 | } |
||
262 | |||
263 | public function testGetSubclassFields() { |
||
264 | /* Test that fields / has_one relations from the parent table and the subclass tables are extracted */ |
||
265 | $captain1 = $this->objFromFixture("DataObjectTest_Player", "captain1"); |
||
266 | // Base field |
||
267 | $this->assertEquals('Captain', $captain1->FirstName); |
||
268 | // Subclass field |
||
269 | $this->assertEquals('007', $captain1->ShirtNumber); |
||
270 | // Subclass has_one relation |
||
271 | $this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team1'), $captain1->FavouriteTeamID); |
||
272 | } |
||
273 | |||
274 | public function testGetRelationClass() { |
||
275 | $obj = new DataObjectTest_Player(); |
||
276 | $this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('FavouriteTeam'), |
||
277 | 'DataObjectTest_Team', 'has_one is properly inspected'); |
||
278 | $this->assertEquals(singleton('DataObjectTest_Company')->getRelationClass('CurrentStaff'), |
||
279 | 'DataObjectTest_Staff', 'has_many is properly inspected'); |
||
280 | $this->assertEquals(singleton('DataObjectTest_Team')->getRelationClass('Players'), 'DataObjectTest_Player', |
||
281 | 'many_many is properly inspected'); |
||
282 | $this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('Teams'), 'DataObjectTest_Team', |
||
283 | 'belongs_many_many is properly inspected'); |
||
284 | $this->assertEquals(singleton('DataObjectTest_CEO')->getRelationClass('Company'), 'DataObjectTest_Company', |
||
285 | 'belongs_to is properly inspected'); |
||
286 | $this->assertEquals(singleton('DataObjectTest_Fan')->getRelationClass('Favourite'), 'DataObject', |
||
287 | 'polymorphic has_one is properly inspected'); |
||
288 | } |
||
289 | |||
290 | /** |
||
291 | * Test that has_one relations can be retrieved |
||
292 | */ |
||
293 | public function testGetHasOneRelations() { |
||
294 | $captain1 = $this->objFromFixture("DataObjectTest_Player", "captain1"); |
||
295 | $team1ID = $this->idFromFixture('DataObjectTest_Team', 'team1'); |
||
296 | |||
297 | // There will be a field called (relname)ID that contains the ID of the |
||
298 | // object linked to via the has_one relation |
||
299 | $this->assertEquals($team1ID, $captain1->FavouriteTeamID); |
||
300 | |||
301 | // There will be a method called $obj->relname() that returns the object itself |
||
302 | $this->assertEquals($team1ID, $captain1->FavouriteTeam()->ID); |
||
303 | |||
304 | // Check entity with polymorphic has-one |
||
305 | $fan1 = $this->objFromFixture("DataObjectTest_Fan", "fan1"); |
||
306 | $this->assertTrue((bool)$fan1->hasValue('Favourite')); |
||
307 | |||
308 | // There will be fields named (relname)ID and (relname)Class for polymorphic |
||
309 | // entities |
||
310 | $this->assertEquals($team1ID, $fan1->FavouriteID); |
||
311 | $this->assertEquals('DataObjectTest_Team', $fan1->FavouriteClass); |
||
312 | |||
313 | // There will be a method called $obj->relname() that returns the object itself |
||
314 | $favourite = $fan1->Favourite(); |
||
315 | $this->assertEquals($team1ID, $favourite->ID); |
||
316 | $this->assertInstanceOf('DataObjectTest_Team', $favourite); |
||
317 | |||
318 | // check behaviour of dbObject with polymorphic relations |
||
319 | $favouriteDBObject = $fan1->dbObject('Favourite'); |
||
320 | $favouriteValue = $favouriteDBObject->getValue(); |
||
321 | $this->assertInstanceOf('PolymorphicForeignKey', $favouriteDBObject); |
||
322 | $this->assertEquals($favourite->ID, $favouriteValue->ID); |
||
323 | $this->assertEquals($favourite->ClassName, $favouriteValue->ClassName); |
||
324 | } |
||
325 | |||
326 | /** |
||
327 | * Simple test to ensure that namespaced classes and polymorphic relations work together |
||
328 | */ |
||
329 | public function testPolymorphicNamespacedRelations() { |
||
330 | $parent = new \DataObjectTest\NamespacedClass(); |
||
331 | $parent->Name = 'New Parent'; |
||
332 | $parent->write(); |
||
333 | |||
334 | $child = new \DataObjectTest\RelationClass(); |
||
335 | $child->Title = 'New Child'; |
||
336 | $child->write(); |
||
337 | $parent->Relations()->add($child); |
||
338 | |||
339 | $this->assertEquals(1, $parent->Relations()->count()); |
||
340 | $this->assertEquals(array('New Child'), $parent->Relations()->column('Title')); |
||
341 | $this->assertEquals('New Parent', $child->Parent()->Name); |
||
342 | } |
||
343 | |||
344 | public function testLimitAndCount() { |
||
345 | $players = DataObject::get("DataObjectTest_Player"); |
||
346 | |||
347 | // There's 4 records in total |
||
348 | $this->assertEquals(4, $players->count()); |
||
349 | |||
350 | // Testing "##, ##" syntax |
||
351 | $this->assertEquals(4, $players->limit(20)->count()); |
||
352 | $this->assertEquals(4, $players->limit(20, 0)->count()); |
||
353 | $this->assertEquals(0, $players->limit(20, 20)->count()); |
||
354 | $this->assertEquals(2, $players->limit(2, 0)->count()); |
||
355 | $this->assertEquals(1, $players->limit(5, 3)->count()); |
||
356 | } |
||
357 | |||
358 | /** |
||
359 | * Test writing of database columns which don't correlate to a DBField, |
||
360 | * e.g. all relation fields on has_one/has_many like "ParentID". |
||
361 | * |
||
362 | */ |
||
363 | public function testWritePropertyWithoutDBField() { |
||
364 | $obj = $this->objFromFixture('DataObjectTest_Player', 'captain1'); |
||
365 | $obj->FavouriteTeamID = 99; |
||
366 | $obj->write(); |
||
367 | |||
368 | // reload the page from the database |
||
369 | $savedObj = DataObject::get_by_id('DataObjectTest_Player', $obj->ID); |
||
370 | $this->assertTrue($savedObj->FavouriteTeamID == 99); |
||
371 | |||
372 | // Test with porymorphic relation |
||
373 | $obj2 = $this->objFromFixture("DataObjectTest_Fan", "fan1"); |
||
374 | $obj2->FavouriteID = 99; |
||
375 | $obj2->FavouriteClass = 'DataObjectTest_Player'; |
||
376 | $obj2->write(); |
||
377 | |||
378 | $savedObj2 = DataObject::get_by_id('DataObjectTest_Fan', $obj2->ID); |
||
379 | $this->assertTrue($savedObj2->FavouriteID == 99); |
||
380 | $this->assertTrue($savedObj2->FavouriteClass == 'DataObjectTest_Player'); |
||
381 | } |
||
382 | |||
383 | /** |
||
384 | * Test has many relationships |
||
385 | * - Test getComponents() gets the ComponentSet of the other side of the relation |
||
386 | * - Test the IDs on the DataObjects are set correctly |
||
387 | */ |
||
388 | public function testHasManyRelationships() { |
||
389 | $team1 = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
390 | |||
391 | // Test getComponents() gets the ComponentSet of the other side of the relation |
||
392 | $this->assertTrue($team1->Comments()->Count() == 2); |
||
393 | |||
394 | // Test the IDs on the DataObjects are set correctly |
||
395 | foreach($team1->Comments() as $comment) { |
||
396 | $this->assertEquals($team1->ID, $comment->TeamID); |
||
397 | } |
||
398 | |||
399 | // Test that we can add and remove items that already exist in the database |
||
400 | $newComment = new DataObjectTest_TeamComment(); |
||
401 | $newComment->Name = "Automated commenter"; |
||
402 | $newComment->Comment = "This is a new comment"; |
||
403 | $newComment->write(); |
||
404 | $team1->Comments()->add($newComment); |
||
405 | $this->assertEquals($team1->ID, $newComment->TeamID); |
||
406 | |||
407 | $comment1 = $this->objFromFixture('DataObjectTest_TeamComment', 'comment1'); |
||
408 | $comment2 = $this->objFromFixture('DataObjectTest_TeamComment', 'comment2'); |
||
409 | $team1->Comments()->remove($comment2); |
||
410 | |||
411 | $team1CommentIDs = $team1->Comments()->sort('ID')->column('ID'); |
||
412 | $this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs); |
||
413 | |||
414 | // Test that removing an item from a list doesn't remove it from the same |
||
415 | // relation belonging to a different object |
||
416 | $team1 = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
417 | $team2 = $this->objFromFixture('DataObjectTest_Team', 'team2'); |
||
418 | $team2->Comments()->remove($comment1); |
||
419 | $team1CommentIDs = $team1->Comments()->sort('ID')->column('ID'); |
||
420 | $this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs); |
||
421 | } |
||
422 | |||
423 | |||
424 | /** |
||
425 | * Test has many relationships against polymorphic has_one fields |
||
426 | * - Test getComponents() gets the ComponentSet of the other side of the relation |
||
427 | * - Test the IDs on the DataObjects are set correctly |
||
428 | */ |
||
429 | public function testHasManyPolymorphicRelationships() { |
||
430 | $team1 = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
431 | |||
432 | // Test getComponents() gets the ComponentSet of the other side of the relation |
||
433 | $this->assertTrue($team1->Fans()->Count() == 2); |
||
434 | |||
435 | // Test the IDs/Classes on the DataObjects are set correctly |
||
436 | foreach($team1->Fans() as $fan) { |
||
437 | $this->assertEquals($team1->ID, $fan->FavouriteID, 'Fan has the correct FavouriteID'); |
||
438 | $this->assertEquals('DataObjectTest_Team', $fan->FavouriteClass, 'Fan has the correct FavouriteClass'); |
||
439 | } |
||
440 | |||
441 | // Test that we can add and remove items that already exist in the database |
||
442 | $newFan = new DataObjectTest_Fan(); |
||
443 | $newFan->Name = "New fan"; |
||
444 | $newFan->write(); |
||
445 | $team1->Fans()->add($newFan); |
||
446 | $this->assertEquals($team1->ID, $newFan->FavouriteID, 'Newly created fan has the correct FavouriteID'); |
||
447 | $this->assertEquals( |
||
448 | 'DataObjectTest_Team', |
||
449 | $newFan->FavouriteClass, |
||
450 | 'Newly created fan has the correct FavouriteClass' |
||
451 | ); |
||
452 | |||
453 | $fan1 = $this->objFromFixture('DataObjectTest_Fan', 'fan1'); |
||
454 | $fan3 = $this->objFromFixture('DataObjectTest_Fan', 'fan3'); |
||
455 | $team1->Fans()->remove($fan3); |
||
456 | |||
457 | $team1FanIDs = $team1->Fans()->sort('ID')->column('ID'); |
||
458 | $this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs); |
||
459 | |||
460 | // Test that removing an item from a list doesn't remove it from the same |
||
461 | // relation belonging to a different object |
||
462 | $team1 = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
463 | $player1 = $this->objFromFixture('DataObjectTest_Player', 'player1'); |
||
464 | $player1->Fans()->remove($fan1); |
||
465 | $team1FanIDs = $team1->Fans()->sort('ID')->column('ID'); |
||
466 | $this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs); |
||
467 | } |
||
468 | |||
469 | |||
470 | public function testHasOneRelationship() { |
||
471 | $team1 = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
472 | $player1 = $this->objFromFixture('DataObjectTest_Player', 'player1'); |
||
473 | $player2 = $this->objFromFixture('DataObjectTest_Player', 'player2'); |
||
474 | $fan1 = $this->objFromFixture('DataObjectTest_Fan', 'fan1'); |
||
475 | |||
476 | // Test relation probing |
||
477 | $this->assertFalse((bool)$team1->hasValue('Captain', null, false)); |
||
478 | $this->assertFalse((bool)$team1->hasValue('CaptainID', null, false)); |
||
479 | |||
480 | // Add a captain to team 1 |
||
481 | $team1->setField('CaptainID', $player1->ID); |
||
482 | $team1->write(); |
||
483 | |||
484 | $this->assertTrue((bool)$team1->hasValue('Captain', null, false)); |
||
485 | $this->assertTrue((bool)$team1->hasValue('CaptainID', null, false)); |
||
486 | |||
487 | $this->assertEquals($player1->ID, $team1->Captain()->ID, |
||
488 | 'The captain exists for team 1'); |
||
489 | $this->assertEquals($player1->ID, $team1->getComponent('Captain')->ID, |
||
490 | 'The captain exists through the component getter'); |
||
491 | |||
492 | $this->assertEquals($team1->Captain()->FirstName, 'Player 1', |
||
493 | 'Player 1 is the captain'); |
||
494 | $this->assertEquals($team1->getComponent('Captain')->FirstName, 'Player 1', |
||
495 | 'Player 1 is the captain'); |
||
496 | |||
497 | $team1->CaptainID = $player2->ID; |
||
498 | $team1->write(); |
||
499 | |||
500 | $this->assertEquals($player2->ID, $team1->Captain()->ID); |
||
501 | $this->assertEquals($player2->ID, $team1->getComponent('Captain')->ID); |
||
502 | $this->assertEquals('Player 2', $team1->Captain()->FirstName); |
||
503 | $this->assertEquals('Player 2', $team1->getComponent('Captain')->FirstName); |
||
504 | |||
505 | |||
506 | // Set the favourite team for fan1 |
||
507 | $fan1->setField('FavouriteID', $team1->ID); |
||
508 | $fan1->setField('FavouriteClass', $team1->class); |
||
509 | |||
510 | $this->assertEquals($team1->ID, $fan1->Favourite()->ID, 'The team is assigned to fan 1'); |
||
511 | $this->assertInstanceOf($team1->class, $fan1->Favourite(), 'The team is assigned to fan 1'); |
||
512 | $this->assertEquals($team1->ID, $fan1->getComponent('Favourite')->ID, |
||
513 | 'The team exists through the component getter' |
||
514 | ); |
||
515 | $this->assertInstanceOf($team1->class, $fan1->getComponent('Favourite'), |
||
516 | 'The team exists through the component getter' |
||
517 | ); |
||
518 | |||
519 | $this->assertEquals($fan1->Favourite()->Title, 'Team 1', |
||
520 | 'Team 1 is the favourite'); |
||
521 | $this->assertEquals($fan1->getComponent('Favourite')->Title, 'Team 1', |
||
522 | 'Team 1 is the favourite'); |
||
523 | } |
||
524 | |||
525 | /** |
||
526 | * @todo Extend type change tests (e.g. '0'==NULL) |
||
527 | */ |
||
528 | public function testChangedFields() { |
||
576 | |||
577 | public function testIsChanged() { |
||
578 | $obj = $this->objFromFixture('DataObjectTest_Player', 'captain1'); |
||
579 | $obj->FirstName = 'Captain-changed'; |
||
580 | $obj->IsRetired = true; // type change only, database stores "1" |
||
581 | |||
582 | $this->assertTrue($obj->isChanged('FirstName', 1)); |
||
583 | $this->assertTrue($obj->isChanged('FirstName', 2)); |
||
584 | $this->assertTrue($obj->isChanged('IsRetired', 1)); |
||
585 | $this->assertFalse($obj->isChanged('IsRetired', 2)); |
||
586 | $this->assertFalse($obj->isChanged('Email', 1), 'Doesnt change mark unchanged property'); |
||
587 | $this->assertFalse($obj->isChanged('Email', 2), 'Doesnt change mark unchanged property'); |
||
588 | |||
589 | $newObj = new DataObjectTest_Player(); |
||
590 | $newObj->FirstName = "New Player"; |
||
591 | $this->assertTrue($newObj->isChanged('FirstName', 1)); |
||
592 | $this->assertTrue($newObj->isChanged('FirstName', 2)); |
||
593 | $this->assertFalse($newObj->isChanged('Email', 1)); |
||
594 | $this->assertFalse($newObj->isChanged('Email', 2)); |
||
595 | |||
596 | $newObj->write(); |
||
597 | $this->assertFalse($newObj->isChanged('FirstName', 1)); |
||
598 | $this->assertFalse($newObj->isChanged('FirstName', 2)); |
||
599 | $this->assertFalse($newObj->isChanged('Email', 1)); |
||
600 | $this->assertFalse($newObj->isChanged('Email', 2)); |
||
601 | |||
602 | $obj = $this->objFromFixture('DataObjectTest_Player', 'captain1'); |
||
603 | $obj->FirstName = null; |
||
604 | $this->assertTrue($obj->isChanged('FirstName', 1)); |
||
605 | $this->assertTrue($obj->isChanged('FirstName', 2)); |
||
606 | |||
607 | /* Test when there's not field provided */ |
||
608 | $obj = $this->objFromFixture('DataObjectTest_Player', 'captain1'); |
||
609 | $obj->FirstName = "New Player"; |
||
610 | $this->assertTrue($obj->isChanged()); |
||
611 | |||
612 | $obj->write(); |
||
613 | $this->assertFalse($obj->isChanged()); |
||
614 | } |
||
615 | |||
616 | public function testRandomSort() { |
||
617 | /* If we perform the same regularly sorted query twice, it should return the same results */ |
||
618 | $itemsA = DataObject::get("DataObjectTest_TeamComment", "", "ID"); |
||
619 | foreach($itemsA as $item) $keysA[] = $item->ID; |
||
620 | |||
621 | $itemsB = DataObject::get("DataObjectTest_TeamComment", "", "ID"); |
||
622 | foreach($itemsB as $item) $keysB[] = $item->ID; |
||
623 | |||
624 | /* Test when there's not field provided */ |
||
625 | $obj = $this->objFromFixture('DataObjectTest_Player', 'captain1'); |
||
626 | $obj->FirstName = "New Player"; |
||
627 | $this->assertTrue($obj->isChanged()); |
||
628 | |||
629 | $obj->write(); |
||
630 | $this->assertFalse($obj->isChanged()); |
||
631 | |||
632 | /* If we perform the same random query twice, it shouldn't return the same results */ |
||
633 | $itemsA = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random()); |
||
634 | $itemsB = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random()); |
||
635 | $itemsC = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random()); |
||
636 | $itemsD = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random()); |
||
637 | foreach($itemsA as $item) $keysA[] = $item->ID; |
||
638 | foreach($itemsB as $item) $keysB[] = $item->ID; |
||
639 | foreach($itemsC as $item) $keysC[] = $item->ID; |
||
640 | foreach($itemsD as $item) $keysD[] = $item->ID; |
||
641 | |||
642 | // These shouldn't all be the same (run it 4 times to minimise chance of an accidental collision) |
||
643 | // There's about a 1 in a billion chance of an accidental collision |
||
644 | $this->assertTrue($keysA != $keysB || $keysB != $keysC || $keysC != $keysD); |
||
645 | } |
||
646 | |||
647 | public function testWriteSavesToHasOneRelations() { |
||
648 | /* DataObject::write() should save to a has_one relationship if you set a field called (relname)ID */ |
||
649 | $team = new DataObjectTest_Team(); |
||
650 | $captainID = $this->idFromFixture('DataObjectTest_Player', 'player1'); |
||
651 | $team->CaptainID = $captainID; |
||
652 | $team->write(); |
||
653 | $this->assertEquals($captainID, |
||
654 | DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value()); |
||
655 | |||
656 | /* After giving it a value, you should also be able to set it back to null */ |
||
657 | $team->CaptainID = ''; |
||
658 | $team->write(); |
||
659 | $this->assertEquals(0, |
||
660 | DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value()); |
||
661 | |||
662 | /* You should also be able to save a blank to it when it's first created */ |
||
663 | $team = new DataObjectTest_Team(); |
||
664 | $team->CaptainID = ''; |
||
665 | $team->write(); |
||
666 | $this->assertEquals(0, |
||
667 | DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value()); |
||
668 | |||
669 | /* Ditto for existing records without a value */ |
||
670 | $existingTeam = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
671 | $existingTeam->CaptainID = ''; |
||
672 | $existingTeam->write(); |
||
673 | $this->assertEquals(0, |
||
674 | DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $existingTeam->ID")->value()); |
||
675 | } |
||
676 | |||
677 | public function testCanAccessHasOneObjectsAsMethods() { |
||
678 | /* If you have a has_one relation 'Captain' on $obj, and you set the $obj->CaptainID = (ID), then the |
||
679 | * object itself should be accessible as $obj->Captain() */ |
||
680 | $team = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
681 | $captainID = $this->idFromFixture('DataObjectTest_Player', 'captain1'); |
||
682 | |||
683 | $team->CaptainID = $captainID; |
||
684 | $this->assertNotNull($team->Captain()); |
||
685 | $this->assertEquals($captainID, $team->Captain()->ID); |
||
686 | |||
687 | // Test for polymorphic has_one relations |
||
688 | $fan = $this->objFromFixture('DataObjectTest_Fan', 'fan1'); |
||
689 | $fan->FavouriteID = $team->ID; |
||
690 | $fan->FavouriteClass = $team->class; |
||
691 | $this->assertNotNull($fan->Favourite()); |
||
692 | $this->assertEquals($team->ID, $fan->Favourite()->ID); |
||
693 | $this->assertInstanceOf($team->class, $fan->Favourite()); |
||
694 | } |
||
695 | |||
696 | public function testFieldNamesThatMatchMethodNamesWork() { |
||
697 | /* Check that a field name that corresponds to a method on DataObject will still work */ |
||
698 | $obj = new DataObjectTest_Fixture(); |
||
699 | $obj->Data = "value1"; |
||
700 | $obj->DbObject = "value2"; |
||
701 | $obj->Duplicate = "value3"; |
||
702 | $obj->write(); |
||
703 | |||
704 | $this->assertNotNull($obj->ID); |
||
705 | $this->assertEquals('value1', |
||
706 | DB::query("SELECT \"Data\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value()); |
||
707 | $this->assertEquals('value2', |
||
708 | DB::query("SELECT \"DbObject\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value()); |
||
709 | $this->assertEquals('value3', |
||
710 | DB::query("SELECT \"Duplicate\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value()); |
||
711 | } |
||
712 | |||
713 | /** |
||
714 | * @todo Re-enable all test cases for field existence after behaviour has been fixed |
||
715 | */ |
||
716 | public function testFieldExistence() { |
||
717 | $teamInstance = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
718 | $teamSingleton = singleton('DataObjectTest_Team'); |
||
719 | |||
720 | $subteamInstance = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1'); |
||
721 | $subteamSingleton = singleton('DataObjectTest_SubTeam'); |
||
722 | |||
723 | /* hasField() singleton checks */ |
||
724 | $this->assertTrue($teamSingleton->hasField('ID'), |
||
725 | 'hasField() finds built-in fields in singletons'); |
||
726 | $this->assertTrue($teamSingleton->hasField('Title'), |
||
727 | 'hasField() finds custom fields in singletons'); |
||
728 | |||
729 | /* hasField() instance checks */ |
||
730 | $this->assertFalse($teamInstance->hasField('NonExistingField'), |
||
731 | 'hasField() doesnt find non-existing fields in instances'); |
||
732 | $this->assertTrue($teamInstance->hasField('ID'), |
||
733 | 'hasField() finds built-in fields in instances'); |
||
734 | $this->assertTrue($teamInstance->hasField('Created'), |
||
735 | 'hasField() finds built-in fields in instances'); |
||
736 | $this->assertTrue($teamInstance->hasField('DatabaseField'), |
||
737 | 'hasField() finds custom fields in instances'); |
||
738 | //$this->assertFalse($teamInstance->hasField('SubclassDatabaseField'), |
||
739 | //'hasField() doesnt find subclass fields in parentclass instances'); |
||
740 | $this->assertTrue($teamInstance->hasField('DynamicField'), |
||
741 | 'hasField() finds dynamic getters in instances'); |
||
742 | $this->assertTrue($teamInstance->hasField('HasOneRelationshipID'), |
||
743 | 'hasField() finds foreign keys in instances'); |
||
744 | $this->assertTrue($teamInstance->hasField('ExtendedDatabaseField'), |
||
745 | 'hasField() finds extended fields in instances'); |
||
746 | $this->assertTrue($teamInstance->hasField('ExtendedHasOneRelationshipID'), |
||
747 | 'hasField() finds extended foreign keys in instances'); |
||
748 | //$this->assertTrue($teamInstance->hasField('ExtendedDynamicField'), |
||
749 | //'hasField() includes extended dynamic getters in instances'); |
||
750 | |||
751 | /* hasField() subclass checks */ |
||
752 | $this->assertTrue($subteamInstance->hasField('ID'), |
||
753 | 'hasField() finds built-in fields in subclass instances'); |
||
754 | $this->assertTrue($subteamInstance->hasField('Created'), |
||
755 | 'hasField() finds built-in fields in subclass instances'); |
||
756 | $this->assertTrue($subteamInstance->hasField('DatabaseField'), |
||
757 | 'hasField() finds custom fields in subclass instances'); |
||
758 | $this->assertTrue($subteamInstance->hasField('SubclassDatabaseField'), |
||
759 | 'hasField() finds custom fields in subclass instances'); |
||
760 | $this->assertTrue($subteamInstance->hasField('DynamicField'), |
||
761 | 'hasField() finds dynamic getters in subclass instances'); |
||
762 | $this->assertTrue($subteamInstance->hasField('HasOneRelationshipID'), |
||
763 | 'hasField() finds foreign keys in subclass instances'); |
||
764 | $this->assertTrue($subteamInstance->hasField('ExtendedDatabaseField'), |
||
765 | 'hasField() finds extended fields in subclass instances'); |
||
766 | $this->assertTrue($subteamInstance->hasField('ExtendedHasOneRelationshipID'), |
||
767 | 'hasField() finds extended foreign keys in subclass instances'); |
||
768 | |||
769 | /* hasDatabaseField() singleton checks */ |
||
770 | //$this->assertTrue($teamSingleton->hasDatabaseField('ID'), |
||
771 | //'hasDatabaseField() finds built-in fields in singletons'); |
||
772 | $this->assertTrue($teamSingleton->hasDatabaseField('Title'), |
||
773 | 'hasDatabaseField() finds custom fields in singletons'); |
||
774 | |||
775 | /* hasDatabaseField() instance checks */ |
||
776 | $this->assertFalse($teamInstance->hasDatabaseField('NonExistingField'), |
||
777 | 'hasDatabaseField() doesnt find non-existing fields in instances'); |
||
778 | //$this->assertTrue($teamInstance->hasDatabaseField('ID'), |
||
779 | //'hasDatabaseField() finds built-in fields in instances'); |
||
780 | $this->assertTrue($teamInstance->hasDatabaseField('Created'), |
||
781 | 'hasDatabaseField() finds built-in fields in instances'); |
||
782 | $this->assertTrue($teamInstance->hasDatabaseField('DatabaseField'), |
||
783 | 'hasDatabaseField() finds custom fields in instances'); |
||
784 | $this->assertFalse($teamInstance->hasDatabaseField('SubclassDatabaseField'), |
||
785 | 'hasDatabaseField() doesnt find subclass fields in parentclass instances'); |
||
786 | //$this->assertFalse($teamInstance->hasDatabaseField('DynamicField'), |
||
787 | //'hasDatabaseField() doesnt dynamic getters in instances'); |
||
788 | $this->assertTrue($teamInstance->hasDatabaseField('HasOneRelationshipID'), |
||
789 | 'hasDatabaseField() finds foreign keys in instances'); |
||
790 | $this->assertTrue($teamInstance->hasDatabaseField('ExtendedDatabaseField'), |
||
791 | 'hasDatabaseField() finds extended fields in instances'); |
||
792 | $this->assertTrue($teamInstance->hasDatabaseField('ExtendedHasOneRelationshipID'), |
||
793 | 'hasDatabaseField() finds extended foreign keys in instances'); |
||
794 | $this->assertFalse($teamInstance->hasDatabaseField('ExtendedDynamicField'), |
||
795 | 'hasDatabaseField() doesnt include extended dynamic getters in instances'); |
||
796 | |||
797 | /* hasDatabaseField() subclass checks */ |
||
798 | $this->assertTrue($subteamInstance->hasDatabaseField('DatabaseField'), |
||
799 | 'hasField() finds custom fields in subclass instances'); |
||
800 | $this->assertTrue($subteamInstance->hasDatabaseField('SubclassDatabaseField'), |
||
801 | 'hasField() finds custom fields in subclass instances'); |
||
802 | |||
803 | } |
||
804 | |||
805 | /** |
||
806 | * @todo Re-enable all test cases for field inheritance aggregation after behaviour has been fixed |
||
807 | */ |
||
808 | public function testFieldInheritance() { |
||
809 | $teamInstance = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
810 | $subteamInstance = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1'); |
||
811 | |||
812 | $this->assertEquals( |
||
813 | array_keys($teamInstance->inheritedDatabaseFields()), |
||
814 | array( |
||
815 | //'ID', |
||
816 | //'ClassName', |
||
817 | //'Created', |
||
818 | //'LastEdited', |
||
819 | 'Title', |
||
820 | 'DatabaseField', |
||
821 | 'ExtendedDatabaseField', |
||
822 | 'CaptainID', |
||
823 | 'HasOneRelationshipID', |
||
824 | 'ExtendedHasOneRelationshipID' |
||
825 | ), |
||
826 | 'inheritedDatabaseFields() contains all fields defined on instance: base, extended and foreign keys' |
||
827 | ); |
||
828 | |||
829 | $this->assertEquals( |
||
830 | array_keys(DataObject::database_fields('DataObjectTest_Team', false)), |
||
831 | array( |
||
832 | //'ID', |
||
833 | 'ClassName', |
||
834 | 'LastEdited', |
||
835 | 'Created', |
||
836 | 'Title', |
||
837 | 'DatabaseField', |
||
838 | 'ExtendedDatabaseField', |
||
839 | 'CaptainID', |
||
840 | 'HasOneRelationshipID', |
||
841 | 'ExtendedHasOneRelationshipID' |
||
842 | ), |
||
843 | 'databaseFields() contains only fields defined on instance, including base, extended and foreign keys' |
||
844 | ); |
||
845 | |||
846 | $this->assertEquals( |
||
847 | array_keys($subteamInstance->inheritedDatabaseFields()), |
||
848 | array( |
||
849 | //'ID', |
||
850 | //'ClassName', |
||
851 | //'Created', |
||
852 | //'LastEdited', |
||
853 | 'SubclassDatabaseField', |
||
854 | 'ParentTeamID', |
||
855 | 'Title', |
||
856 | 'DatabaseField', |
||
857 | 'ExtendedDatabaseField', |
||
858 | 'CaptainID', |
||
859 | 'HasOneRelationshipID', |
||
860 | 'ExtendedHasOneRelationshipID', |
||
861 | ), |
||
862 | 'inheritedDatabaseFields() on subclass contains all fields, including base, extended and foreign keys' |
||
863 | ); |
||
864 | |||
865 | $this->assertEquals( |
||
866 | array_keys(DataObject::database_fields('DataObjectTest_SubTeam', false)), |
||
867 | array( |
||
868 | 'SubclassDatabaseField', |
||
869 | 'ParentTeamID', |
||
870 | ), |
||
871 | 'databaseFields() on subclass contains only fields defined on instance' |
||
872 | ); |
||
873 | } |
||
874 | |||
875 | public function testSearchableFields() { |
||
876 | $player = $this->objFromFixture('DataObjectTest_Player', 'captain1'); |
||
877 | $fields = $player->searchableFields(); |
||
878 | $this->assertArrayHasKey( |
||
879 | 'IsRetired', |
||
880 | $fields, |
||
881 | 'Fields defined by $searchable_fields static are correctly detected' |
||
882 | ); |
||
883 | $this->assertArrayHasKey( |
||
884 | 'ShirtNumber', |
||
885 | $fields, |
||
886 | 'Fields defined by $searchable_fields static are correctly detected' |
||
887 | ); |
||
888 | |||
889 | $team = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
890 | $fields = $team->searchableFields(); |
||
891 | $this->assertArrayHasKey( |
||
892 | 'Title', |
||
893 | $fields, |
||
894 | 'Fields can be inherited from the $summary_fields static, including methods called on fields' |
||
895 | ); |
||
896 | $this->assertArrayHasKey( |
||
897 | 'Captain.ShirtNumber', |
||
898 | $fields, |
||
899 | 'Fields on related objects can be inherited from the $summary_fields static' |
||
900 | ); |
||
901 | $this->assertArrayHasKey( |
||
902 | 'Captain.FavouriteTeam.Title', |
||
903 | $fields, |
||
904 | 'Fields on related objects can be inherited from the $summary_fields static' |
||
905 | ); |
||
906 | |||
907 | $testObj = new DataObjectTest_Fixture(); |
||
908 | $fields = $testObj->searchableFields(); |
||
909 | $this->assertEmpty($fields); |
||
910 | } |
||
911 | |||
912 | public function testSummaryFieldsCustomLabels() { |
||
913 | $team = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
914 | $summaryFields = $team->summaryFields(); |
||
915 | |||
916 | $this->assertEquals( |
||
917 | 'Custom Title', |
||
918 | $summaryFields['Title'], |
||
919 | 'Custom title is preserved' |
||
920 | ); |
||
921 | |||
922 | $this->assertEquals( |
||
923 | 'Captain\'s shirt number', |
||
924 | $summaryFields['Captain.ShirtNumber'], |
||
925 | 'Custom title on relation is preserved' |
||
926 | ); |
||
927 | } |
||
928 | |||
929 | public function testDataObjectUpdate() { |
||
930 | /* update() calls can use the dot syntax to reference has_one relations and other methods that return |
||
931 | * objects */ |
||
932 | $team1 = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
933 | $team1->CaptainID = $this->idFromFixture('DataObjectTest_Player', 'captain1'); |
||
934 | |||
935 | $team1->update(array( |
||
936 | 'DatabaseField' => 'Something', |
||
937 | 'Captain.FirstName' => 'Jim', |
||
938 | 'Captain.Email' => '[email protected]', |
||
939 | 'Captain.FavouriteTeam.Title' => 'New and improved team 1', |
||
940 | )); |
||
941 | |||
942 | /* Test the simple case of updating fields on the object itself */ |
||
943 | $this->assertEquals('Something', $team1->DatabaseField); |
||
944 | |||
945 | /* Setting Captain.Email and Captain.FirstName will have updated DataObjectTest_Captain.captain1 in |
||
946 | * the database. Although update() doesn't usually write, it does write related records automatically. */ |
||
947 | $captain1 = $this->objFromFixture('DataObjectTest_Player', 'captain1'); |
||
948 | $this->assertEquals('Jim', $captain1->FirstName); |
||
949 | $this->assertEquals('[email protected]', $captain1->Email); |
||
950 | |||
951 | /* Jim's favourite team is team 1; we need to reload the object to the the change that setting Captain. |
||
952 | * FavouriteTeam.Title made */ |
||
953 | $reloadedTeam1 = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
954 | $this->assertEquals('New and improved team 1', $reloadedTeam1->Title); |
||
955 | } |
||
956 | |||
957 | public function testDataObjectUpdateNew() { |
||
979 | |||
980 | public function testWritingInvalidDataObjectThrowsException() { |
||
981 | $validatedObject = new DataObjectTest_ValidatedObject(); |
||
982 | |||
983 | $this->setExpectedException('ValidationException'); |
||
984 | $validatedObject->write(); |
||
985 | } |
||
986 | |||
987 | public function testWritingValidDataObjectDoesntThrowException() { |
||
988 | $validatedObject = new DataObjectTest_ValidatedObject(); |
||
989 | $validatedObject->Name = "Mr. Jones"; |
||
990 | |||
991 | $validatedObject->write(); |
||
992 | $this->assertTrue($validatedObject->isInDB(), "Validated object was not saved to database"); |
||
993 | } |
||
994 | |||
995 | public function testSubclassCreation() { |
||
996 | /* Creating a new object of a subclass should set the ClassName field correctly */ |
||
997 | $obj = new DataObjectTest_SubTeam(); |
||
998 | $obj->write(); |
||
999 | $this->assertEquals("DataObjectTest_SubTeam", |
||
1000 | DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value()); |
||
1001 | } |
||
1002 | |||
1003 | public function testForceInsert() { |
||
1004 | /* If you set an ID on an object and pass forceInsert = true, then the object should be correctly created */ |
||
1005 | $conn = DB::get_conn(); |
||
1006 | if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing('DataObjectTest_Team', true); |
||
1007 | $obj = new DataObjectTest_SubTeam(); |
||
1008 | $obj->ID = 1001; |
||
1009 | $obj->Title = 'asdfasdf'; |
||
1010 | $obj->SubclassDatabaseField = 'asdfasdf'; |
||
1011 | $obj->write(false, true); |
||
1012 | if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing('DataObjectTest_Team', false); |
||
1013 | |||
1014 | $this->assertEquals("DataObjectTest_SubTeam", |
||
1015 | DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value()); |
||
1016 | |||
1017 | /* Check that it actually saves to the database with the correct ID */ |
||
1018 | $this->assertEquals("1001", DB::query( |
||
1019 | "SELECT \"ID\" FROM \"DataObjectTest_SubTeam\" WHERE \"SubclassDatabaseField\" = 'asdfasdf'")->value()); |
||
1020 | $this->assertEquals("1001", |
||
1021 | DB::query("SELECT \"ID\" FROM \"DataObjectTest_Team\" WHERE \"Title\" = 'asdfasdf'")->value()); |
||
1022 | } |
||
1023 | |||
1024 | public function TestHasOwnTable() { |
||
1025 | /* Test DataObject::has_own_table() returns true if the object has $has_one or $db values */ |
||
1026 | $this->assertTrue(DataObject::has_own_table("DataObjectTest_Player")); |
||
1027 | $this->assertTrue(DataObject::has_own_table("DataObjectTest_Team")); |
||
1028 | $this->assertTrue(DataObject::has_own_table("DataObjectTest_Fixture")); |
||
1029 | |||
1030 | /* Root DataObject that always have a table, even if they lack both $db and $has_one */ |
||
1031 | $this->assertTrue(DataObject::has_own_table("DataObjectTest_FieldlessTable")); |
||
1032 | |||
1033 | /* Subclasses without $db or $has_one don't have a table */ |
||
1034 | $this->assertFalse(DataObject::has_own_table("DataObjectTest_FieldlessSubTable")); |
||
1035 | |||
1036 | /* Return false if you don't pass it a subclass of DataObject */ |
||
1037 | $this->assertFalse(DataObject::has_own_table("DataObject")); |
||
1038 | $this->assertFalse(DataObject::has_own_table("ViewableData")); |
||
1039 | $this->assertFalse(DataObject::has_own_table("ThisIsntADataObject")); |
||
1040 | } |
||
1041 | |||
1042 | public function testMerge() { |
||
1043 | // test right merge of subclasses |
||
1044 | $left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1'); |
||
1045 | $right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation'); |
||
1046 | $leftOrigID = $left->ID; |
||
1047 | $left->merge($right, 'right', false, false); |
||
1048 | $this->assertEquals( |
||
1049 | $left->Title, |
||
1050 | 'Subteam 2', |
||
1051 | 'merge() with "right" priority overwrites fields with existing values on subclasses' |
||
1052 | ); |
||
1053 | $this->assertEquals( |
||
1054 | $left->ID, |
||
1055 | $leftOrigID, |
||
1056 | 'merge() with "right" priority doesnt overwrite database ID' |
||
1057 | ); |
||
1058 | |||
1059 | // test overwriteWithEmpty flag on existing left values |
||
1060 | $left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation'); |
||
1061 | $right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam3_with_empty_fields'); |
||
1062 | $left->merge($right, 'right', false, true); |
||
1063 | $this->assertEquals( |
||
1064 | $left->Title, |
||
1065 | 'Subteam 3', |
||
1066 | 'merge() with $overwriteWithEmpty overwrites non-empty fields on left object' |
||
1067 | ); |
||
1068 | |||
1069 | // test overwriteWithEmpty flag on empty left values |
||
1070 | $left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1'); |
||
1071 | // $SubclassDatabaseField is empty on here |
||
1072 | $right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation'); |
||
1073 | $left->merge($right, 'right', false, true); |
||
1074 | $this->assertEquals( |
||
1075 | $left->SubclassDatabaseField, |
||
1076 | NULL, |
||
1077 | 'merge() with $overwriteWithEmpty overwrites empty fields on left object' |
||
1078 | ); |
||
1079 | |||
1080 | // @todo test "left" priority flag |
||
1081 | // @todo test includeRelations flag |
||
1082 | // @todo test includeRelations in combination with overwriteWithEmpty |
||
1083 | // @todo test has_one relations |
||
1084 | // @todo test has_many and many_many relations |
||
1085 | } |
||
1086 | |||
1087 | public function testPopulateDefaults() { |
||
1088 | $obj = new DataObjectTest_Fixture(); |
||
1089 | $this->assertEquals( |
||
1090 | $obj->MyFieldWithDefault, |
||
1091 | 'Default Value', |
||
1092 | 'Defaults are populated for in-memory object from $defaults array' |
||
1093 | ); |
||
1094 | |||
1095 | $this->assertEquals( |
||
1096 | $obj->MyFieldWithAltDefault, |
||
1097 | 'Default Value', |
||
1098 | 'Defaults are populated from overloaded populateDefaults() method' |
||
1099 | ); |
||
1100 | } |
||
1101 | |||
1102 | protected function makeAccessible($object, $method) { |
||
1103 | $reflectionMethod = new ReflectionMethod($object, $method); |
||
1104 | $reflectionMethod->setAccessible(true); |
||
1105 | return $reflectionMethod; |
||
1106 | } |
||
1107 | |||
1108 | View Code Duplication | public function testValidateModelDefinitionsFailsWithArray() { |
|
1109 | Config::nest(); |
||
1110 | |||
1111 | $object = new DataObjectTest_Team; |
||
1112 | $method = $this->makeAccessible($object, 'validateModelDefinitions'); |
||
1113 | |||
1114 | Config::inst()->update('DataObjectTest_Team', 'has_one', array('NotValid' => array('NoArraysAllowed'))); |
||
1115 | $this->setExpectedException('LogicException'); |
||
1116 | |||
1117 | try { |
||
1118 | $method->invoke($object); |
||
1119 | } catch(Exception $e) { |
||
1120 | Config::unnest(); // Catch the exception so we can unnest config before failing the test |
||
1121 | throw $e; |
||
1122 | } |
||
1123 | } |
||
1124 | |||
1125 | View Code Duplication | public function testValidateModelDefinitionsFailsWithIntKey() { |
|
1126 | Config::nest(); |
||
1127 | |||
1128 | $object = new DataObjectTest_Team; |
||
1129 | $method = $this->makeAccessible($object, 'validateModelDefinitions'); |
||
1130 | |||
1131 | Config::inst()->update('DataObjectTest_Team', 'has_many', array(12 => 'DataObjectTest_Player')); |
||
1132 | $this->setExpectedException('LogicException'); |
||
1133 | |||
1134 | try { |
||
1135 | $method->invoke($object); |
||
1136 | } catch(Exception $e) { |
||
1137 | Config::unnest(); // Catch the exception so we can unnest config before failing the test |
||
1138 | throw $e; |
||
1139 | } |
||
1140 | } |
||
1141 | |||
1142 | View Code Duplication | public function testValidateModelDefinitionsFailsWithIntValue() { |
|
1143 | Config::nest(); |
||
1144 | |||
1145 | $object = new DataObjectTest_Team; |
||
1146 | $method = $this->makeAccessible($object, 'validateModelDefinitions'); |
||
1147 | |||
1148 | Config::inst()->update('DataObjectTest_Team', 'many_many', array('Players' => 12)); |
||
1149 | $this->setExpectedException('LogicException'); |
||
1150 | |||
1151 | try { |
||
1152 | $method->invoke($object); |
||
1153 | } catch(Exception $e) { |
||
1154 | Config::unnest(); // Catch the exception so we can unnest config before failing the test |
||
1155 | throw $e; |
||
1156 | } |
||
1157 | } |
||
1158 | |||
1159 | /** |
||
1160 | * many_many_extraFields is allowed to have an array value, so shouldn't throw an exception |
||
1161 | */ |
||
1162 | View Code Duplication | public function testValidateModelDefinitionsPassesWithExtraFields() { |
|
1163 | Config::nest(); |
||
1164 | |||
1165 | $object = new DataObjectTest_Team; |
||
1166 | $method = $this->makeAccessible($object, 'validateModelDefinitions'); |
||
1167 | |||
1168 | Config::inst()->update('DataObjectTest_Team', 'many_many_extraFields', |
||
1169 | array('Relations' => array('Price' => 'Int'))); |
||
1170 | |||
1171 | try { |
||
1172 | $method->invoke($object); |
||
1173 | } catch(Exception $e) { |
||
1174 | Config::unnest(); |
||
1175 | $this->fail('Exception should not be thrown'); |
||
1176 | throw $e; |
||
1177 | } |
||
1178 | |||
1179 | Config::unnest(); |
||
1180 | } |
||
1181 | |||
1182 | public function testNewClassInstance() { |
||
1183 | $dataObject = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
1184 | $changedDO = $dataObject->newClassInstance('DataObjectTest_SubTeam'); |
||
1185 | $changedFields = $changedDO->getChangedFields(); |
||
1186 | |||
1187 | // Don't write the record, it will reset changed fields |
||
1188 | $this->assertInstanceOf('DataObjectTest_SubTeam', $changedDO); |
||
1189 | $this->assertEquals($changedDO->ClassName, 'DataObjectTest_SubTeam'); |
||
1190 | $this->assertContains('ClassName', array_keys($changedFields)); |
||
1191 | $this->assertEquals($changedFields['ClassName']['before'], 'DataObjectTest_Team'); |
||
1192 | $this->assertEquals($changedFields['ClassName']['after'], 'DataObjectTest_SubTeam'); |
||
1193 | |||
1194 | $changedDO->write(); |
||
1195 | |||
1196 | $this->assertInstanceOf('DataObjectTest_SubTeam', $changedDO); |
||
1197 | $this->assertEquals($changedDO->ClassName, 'DataObjectTest_SubTeam'); |
||
1198 | } |
||
1199 | |||
1200 | public function testMultipleManyManyWithSameClass() { |
||
1201 | $team = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
1202 | $sponsors = $team->Sponsors(); |
||
1203 | $equipmentSuppliers = $team->EquipmentSuppliers(); |
||
1204 | |||
1205 | // Check that DataObject::many_many() works as expected |
||
1206 | list($class, $targetClass, $parentField, $childField, $joinTable) = $team->manyManyComponent('Sponsors'); |
||
1207 | $this->assertEquals('DataObjectTest_Team', $class, |
||
1208 | 'DataObject::many_many() didn\'t find the correct base class'); |
||
1209 | $this->assertEquals('DataObjectTest_EquipmentCompany', $targetClass, |
||
1210 | 'DataObject::many_many() didn\'t find the correct target class for the relation'); |
||
1211 | $this->assertEquals('DataObjectTest_EquipmentCompany_SponsoredTeams', $joinTable, |
||
1212 | 'DataObject::many_many() didn\'t find the correct relation table'); |
||
1213 | |||
1214 | // Check that ManyManyList still works |
||
1215 | $this->assertEquals(2, $sponsors->count(), 'Rows are missing from relation'); |
||
1216 | $this->assertEquals(1, $equipmentSuppliers->count(), 'Rows are missing from relation'); |
||
1217 | |||
1218 | // Check everything works when no relation is present |
||
1219 | $teamWithoutSponsor = $this->objFromFixture('DataObjectTest_Team', 'team3'); |
||
1220 | $this->assertInstanceOf('ManyManyList', $teamWithoutSponsor->Sponsors()); |
||
1221 | $this->assertEquals(0, $teamWithoutSponsor->Sponsors()->count()); |
||
1222 | |||
1223 | // Check many_many_extraFields still works |
||
1224 | $equipmentCompany = $this->objFromFixture('DataObjectTest_EquipmentCompany', 'equipmentcompany1'); |
||
1225 | $equipmentCompany->SponsoredTeams()->add($teamWithoutSponsor, array('SponsorFee' => 1000)); |
||
1226 | $sponsoredTeams = $equipmentCompany->SponsoredTeams(); |
||
1227 | $this->assertEquals(1000, $sponsoredTeams->byID($teamWithoutSponsor->ID)->SponsorFee, |
||
1228 | 'Data from many_many_extraFields was not stored/extracted correctly'); |
||
1229 | |||
1230 | // Check subclasses correctly inherit multiple many_manys |
||
1231 | $subTeam = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1'); |
||
1232 | $this->assertEquals(2, $subTeam->Sponsors()->count(), |
||
1233 | 'Child class did not inherit multiple many_manys'); |
||
1234 | $this->assertEquals(1, $subTeam->EquipmentSuppliers()->count(), |
||
1235 | 'Child class did not inherit multiple many_manys'); |
||
1236 | // Team 2 has one EquipmentCompany sponsor and one SubEquipmentCompany |
||
1237 | $team2 = $this->objFromFixture('DataObjectTest_Team', 'team2'); |
||
1238 | $this->assertEquals(2, $team2->Sponsors()->count(), |
||
1239 | 'Child class did not inherit multiple belongs_many_manys'); |
||
1240 | |||
1241 | // Check many_many_extraFields also works from the belongs_many_many side |
||
1242 | $sponsors = $team2->Sponsors(); |
||
1243 | $sponsors->add($equipmentCompany, array('SponsorFee' => 750)); |
||
1244 | $this->assertEquals(750, $sponsors->byID($equipmentCompany->ID)->SponsorFee, |
||
1245 | 'Data from many_many_extraFields was not stored/extracted correctly'); |
||
1246 | |||
1247 | $subEquipmentCompany = $this->objFromFixture('DataObjectTest_SubEquipmentCompany', 'subequipmentcompany1'); |
||
1248 | $subTeam->Sponsors()->add($subEquipmentCompany, array('SponsorFee' => 1200)); |
||
1249 | $this->assertEquals(1200, $subTeam->Sponsors()->byID($subEquipmentCompany->ID)->SponsorFee, |
||
1250 | 'Data from inherited many_many_extraFields was not stored/extracted correctly'); |
||
1251 | } |
||
1252 | |||
1253 | public function testManyManyExtraFields() { |
||
1254 | $player = $this->objFromFixture('DataObjectTest_Player', 'player1'); |
||
1255 | $team = $this->objFromFixture('DataObjectTest_Team', 'team1'); |
||
1256 | |||
1257 | // Get all extra fields |
||
1258 | $teamExtraFields = $team->manyManyExtraFields(); |
||
1259 | $this->assertEquals(array( |
||
1260 | 'Players' => array('Position' => 'Varchar(100)') |
||
1261 | ), $teamExtraFields); |
||
1262 | |||
1263 | // Ensure fields from parent classes are included |
||
1264 | $subTeam = singleton('DataObjectTest_SubTeam'); |
||
1265 | $teamExtraFields = $subTeam->manyManyExtraFields(); |
||
1266 | $this->assertEquals(array( |
||
1267 | 'Players' => array('Position' => 'Varchar(100)'), |
||
1268 | 'FormerPlayers' => array('Position' => 'Varchar(100)') |
||
1269 | ), $teamExtraFields); |
||
1270 | |||
1271 | // Extra fields are immediately available on the Team class (defined in $many_many_extraFields) |
||
1272 | $teamExtraFields = $team->manyManyExtraFieldsForComponent('Players'); |
||
1273 | $this->assertEquals($teamExtraFields, array( |
||
1274 | 'Position' => 'Varchar(100)' |
||
1275 | )); |
||
1276 | |||
1277 | // We'll have to go through the relation to get the extra fields on Player |
||
1278 | $playerExtraFields = $player->manyManyExtraFieldsForComponent('Teams'); |
||
1279 | $this->assertEquals($playerExtraFields, array( |
||
1280 | 'Position' => 'Varchar(100)' |
||
1281 | )); |
||
1282 | |||
1283 | // Iterate through a many-many relationship and confirm that extra fields are included |
||
1284 | $newTeam = new DataObjectTest_Team(); |
||
1285 | $newTeam->Title = "New team"; |
||
1286 | $newTeam->write(); |
||
1287 | $newTeamID = $newTeam->ID; |
||
1288 | |||
1289 | $newPlayer = new DataObjectTest_Player(); |
||
1290 | $newPlayer->FirstName = "Sam"; |
||
1291 | $newPlayer->Surname = "Minnee"; |
||
1292 | $newPlayer->write(); |
||
1293 | |||
1294 | // The idea of Sam as a prop is essentially humourous. |
||
1295 | $newTeam->Players()->add($newPlayer, array("Position" => "Prop")); |
||
1296 | |||
1297 | // Requery and uncache everything |
||
1298 | $newTeam->flushCache(); |
||
1299 | $newTeam = DataObject::get_by_id('DataObjectTest_Team', $newTeamID); |
||
1300 | |||
1301 | // Check that the Position many_many_extraField is extracted. |
||
1302 | $player = $newTeam->Players()->First(); |
||
1303 | $this->assertEquals('Sam', $player->FirstName); |
||
1304 | $this->assertEquals("Prop", $player->Position); |
||
1305 | |||
1306 | // Check that ordering a many-many relation by an aggregate column doesn't fail |
||
1307 | $player = $this->objFromFixture('DataObjectTest_Player', 'player2'); |
||
1308 | $player->Teams()->sort("count(DISTINCT \"DataObjectTest_Team_Players\".\"DataObjectTest_PlayerID\") DESC"); |
||
1309 | } |
||
1310 | |||
1311 | /** |
||
1312 | * Check that the queries generated for many-many relation queries can have unlimitedRowCount |
||
1313 | * called on them. |
||
1314 | */ |
||
1315 | public function testManyManyUnlimitedRowCount() { |
||
1316 | $player = $this->objFromFixture('DataObjectTest_Player', 'player2'); |
||
1317 | // TODO: What's going on here? |
||
1318 | $this->assertEquals(2, $player->Teams()->dataQuery()->query()->unlimitedRowCount()); |
||
1319 | } |
||
1320 | |||
1321 | /** |
||
1322 | * Tests that singular_name() generates sensible defaults. |
||
1323 | */ |
||
1324 | public function testSingularName() { |
||
1325 | $assertions = array( |
||
1326 | 'DataObjectTest_Player' => 'Data Object Test Player', |
||
1327 | 'DataObjectTest_Team' => 'Data Object Test Team', |
||
1328 | 'DataObjectTest_Fixture' => 'Data Object Test Fixture' |
||
1329 | ); |
||
1330 | |||
1331 | foreach($assertions as $class => $expectedSingularName) { |
||
1332 | $this->assertEquals( |
||
1333 | $expectedSingularName, |
||
1334 | singleton($class)->singular_name(), |
||
1335 | "Assert that the singular_name for '$class' is correct." |
||
1336 | ); |
||
1337 | } |
||
1338 | } |
||
1339 | |||
1340 | /** |
||
1341 | * Tests that plural_name() generates sensible defaults. |
||
1342 | */ |
||
1343 | public function testPluralName() { |
||
1344 | $assertions = array( |
||
1345 | 'DataObjectTest_Player' => 'Data Object Test Players', |
||
1346 | 'DataObjectTest_Team' => 'Data Object Test Teams', |
||
1347 | 'DataObjectTest_Fixture' => 'Data Object Test Fixtures', |
||
1348 | 'DataObjectTest_Play' => 'Data Object Test Plays', |
||
1349 | 'DataObjectTest_Bogey' => 'Data Object Test Bogeys', |
||
1350 | 'DataObjectTest_Ploy' => 'Data Object Test Ploys', |
||
1351 | ); |
||
1352 | |||
1353 | foreach($assertions as $class => $expectedPluralName) { |
||
1354 | $this->assertEquals( |
||
1355 | $expectedPluralName, |
||
1356 | singleton($class)->plural_name(), |
||
1357 | "Assert that the plural_name for '$class' is correct." |
||
1358 | ); |
||
1359 | } |
||
1360 | } |
||
1361 | |||
1362 | public function testHasDatabaseField() { |
||
1363 | $team = singleton('DataObjectTest_Team'); |
||
1364 | $subteam = singleton('DataObjectTest_SubTeam'); |
||
1365 | |||
1366 | $this->assertTrue( |
||
1367 | $team->hasDatabaseField('Title'), |
||
1368 | "hasOwnDatabaseField() works with \$db fields" |
||
1369 | ); |
||
1370 | $this->assertTrue( |
||
1371 | $team->hasDatabaseField('CaptainID'), |
||
1372 | "hasOwnDatabaseField() works with \$has_one fields" |
||
1373 | ); |
||
1374 | $this->assertFalse( |
||
1375 | $team->hasDatabaseField('NonExistentField'), |
||
1376 | "hasOwnDatabaseField() doesn't detect non-existend fields" |
||
1377 | ); |
||
1378 | $this->assertTrue( |
||
1379 | $team->hasDatabaseField('ExtendedDatabaseField'), |
||
1380 | "hasOwnDatabaseField() works with extended fields" |
||
1381 | ); |
||
1382 | $this->assertFalse( |
||
1383 | $team->hasDatabaseField('SubclassDatabaseField'), |
||
1384 | "hasOwnDatabaseField() doesn't pick up fields in subclasses on parent class" |
||
1385 | ); |
||
1386 | |||
1387 | $this->assertTrue( |
||
1388 | $subteam->hasDatabaseField('SubclassDatabaseField'), |
||
1389 | "hasOwnDatabaseField() picks up fields in subclasses" |
||
1390 | ); |
||
1391 | |||
1392 | } |
||
1393 | |||
1394 | public function testFieldTypes() { |
||
1395 | $obj = new DataObjectTest_Fixture(); |
||
1396 | $obj->DateField = '1988-01-02'; |
||
1397 | $obj->DatetimeField = '1988-03-04 06:30'; |
||
1398 | $obj->write(); |
||
1399 | $obj->flushCache(); |
||
1400 | |||
1401 | $obj = DataObject::get_by_id('DataObjectTest_Fixture', $obj->ID); |
||
1402 | $this->assertEquals('1988-01-02', $obj->DateField); |
||
1403 | $this->assertEquals('1988-03-04 06:30:00', $obj->DatetimeField); |
||
1404 | } |
||
1405 | |||
1406 | public function testTwoSubclassesWithTheSameFieldNameWork() { |
||
1407 | // Create two objects of different subclasses, setting the values of fields that are |
||
1408 | // defined separately in each subclass |
||
1409 | $obj1 = new DataObjectTest_SubTeam(); |
||
1410 | $obj1->SubclassDatabaseField = "obj1"; |
||
1411 | $obj2 = new OtherSubclassWithSameField(); |
||
1412 | $obj2->SubclassDatabaseField = "obj2"; |
||
1413 | |||
1414 | // Write them to the database |
||
1415 | $obj1->write(); |
||
1416 | $obj2->write(); |
||
1417 | |||
1418 | // Check that the values of those fields are properly read from the database |
||
1419 | $values = DataObject::get("DataObjectTest_Team", "\"DataObjectTest_Team\".\"ID\" IN |
||
1420 | ($obj1->ID, $obj2->ID)")->column("SubclassDatabaseField"); |
||
1421 | $this->assertEquals(array_intersect($values, array('obj1', 'obj2')), $values); |
||
1422 | } |
||
1423 | |||
1424 | public function testClassNameSetForNewObjects() { |
||
1428 | |||
1429 | public function testHasValue() { |
||
1430 | $team = new DataObjectTest_Team(); |
||
1431 | $this->assertFalse($team->hasValue('Title', null, false)); |
||
1432 | $this->assertFalse($team->hasValue('DatabaseField', null, false)); |
||
1433 | |||
1434 | $team->Title = 'hasValue'; |
||
1435 | $this->assertTrue($team->hasValue('Title', null, false)); |
||
1436 | $this->assertFalse($team->hasValue('DatabaseField', null, false)); |
||
1437 | |||
1438 | $team->DatabaseField = '<p></p>'; |
||
1439 | $this->assertTrue($team->hasValue('Title', null, false)); |
||
1440 | $this->assertFalse ( |
||
1441 | $team->hasValue('DatabaseField', null, false), |
||
1442 | 'Test that a blank paragraph on a HTML field is not a valid value.' |
||
1443 | ); |
||
1444 | |||
1445 | $team->Title = '<p></p>'; |
||
1446 | $this->assertTrue ( |
||
1447 | $team->hasValue('Title', null, false), |
||
1448 | 'Test that an empty paragraph is a value for non-HTML fields.' |
||
1449 | ); |
||
1450 | |||
1451 | $team->DatabaseField = 'hasValue'; |
||
1452 | $this->assertTrue($team->hasValue('Title', null, false)); |
||
1453 | $this->assertTrue($team->hasValue('DatabaseField', null, false)); |
||
1454 | } |
||
1455 | |||
1456 | public function testHasMany() { |
||
1457 | $company = new DataObjectTest_Company(); |
||
1458 | |||
1459 | $this->assertEquals ( |
||
1460 | array ( |
||
1461 | 'CurrentStaff' => 'DataObjectTest_Staff', |
||
1462 | 'PreviousStaff' => 'DataObjectTest_Staff' |
||
1463 | ), |
||
1464 | $company->hasMany(), |
||
1465 | 'has_many strips field name data by default.' |
||
1466 | ); |
||
1467 | |||
1468 | $this->assertEquals ( |
||
1469 | 'DataObjectTest_Staff', |
||
1470 | $company->hasManyComponent('CurrentStaff'), |
||
1471 | 'has_many strips field name data by default on single relationships.' |
||
1472 | ); |
||
1473 | |||
1474 | $this->assertEquals ( |
||
1475 | array ( |
||
1476 | 'CurrentStaff' => 'DataObjectTest_Staff.CurrentCompany', |
||
1489 | |||
1490 | public function testGetRemoteJoinField() { |
||
1514 | |||
1515 | public function testBelongsTo() { |
||
1548 | |||
1549 | public function testBelongsToPolymorphic() { |
||
1586 | |||
1587 | /** |
||
1588 | * @expectedException LogicException |
||
1589 | */ |
||
1590 | public function testInvalidate() { |
||
1600 | |||
1601 | public function testToMap() { |
||
1620 | |||
1621 | public function testIsEmpty() { |
||
1628 | |||
1629 | public function testRelField() { |
||
1647 | |||
1648 | public function testRelObject() { |
||
1664 | |||
1665 | public function testLateStaticBindingStyle() { |
||
1675 | |||
1676 | public function testBrokenLateStaticBindingStyle() { |
||
1682 | |||
1683 | } |
||
1684 | |||
1952 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: