Completed
Push — 3.7 ( b1adf2...8f9bb9 )
by
unknown
24:14 queued 10:05
created

testWriteManipulationWithNonScalarValuesDisallowed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @package framework
5
 * @subpackage tests
6
 */
7
class ManyManyListTest extends SapphireTest {
8
9
	protected static $fixture_file = 'DataObjectTest.yml';
10
11
	protected $extraDataObjects = array(
12
		'DataObjectTest_Team',
13
		'DataObjectTest_SubTeam',
14
		'DataObjectTest_Fan',
15
		'DataObjectTest_Sortable',
16
		'DataObjectTest_EquipmentCompany',
17
		'DataObjectTest_SubEquipmentCompany',
18
		'DataObjectTest_Player',
19
		'DataObjectTest_Company',
20
		'DataObjectTest_TeamComment',
21
		'ManyManyListTest_ExtraFields',
22
		'ManyManyListTest_Product',
23
		'ManyManyListTest_Category',
24
        'MockDynamicAssignmentDataObject',
25
	);
26
27
28
	public function testAddCompositedExtraFields() {
29
		$obj = new ManyManyListTest_ExtraFields();
30
		$obj->write();
31
32
		$money = new Money();
33
		$money->setAmount(100);
34
		$money->setCurrency('USD');
35
36
		// the actual test is that this does not generate an error in the sql.
37
		$obj->Clients()->add($obj, array(
38
			'Worth' => $money,
39
			'Reference' => 'Foo'
40
		));
41
42
		$check = $obj->Clients()->First();
43
44
		$this->assertEquals('Foo', $check->Reference, 'Basic scalar fields should exist');
45
		$this->assertInstanceOf('Money', $check->Worth, 'Composite fields should exist on the record');
46
		$this->assertEquals(100, $check->Worth->getAmount());
47
	}
48
49
	/**
50
	 * This test targets a bug where appending many_many_extraFields to a query would
51
	 * result in erroneous queries for sort orders that rely on _SortColumn0
52
	 */
53
	public function testAddCompositedExtraFieldsWithSortColumn0() {
54
		$obj = new ManyManyListTest_ExtraFields();
55
		$obj->write();
56
57
		$product = new ManyManyListTest_Product();
58
		$product->Title = 'Test Product';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<ManyManyListTest_Product>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
59
		$product->write();
60
61
		// the actual test is that this does not generate an error in the sql.
62
		$obj->Products()->add($product, array(
63
			'Reference' => 'Foo'
64
		));
65
66
		$result = $obj->Products()->First();
67
		$this->assertEquals('Foo', $result->Reference, 'Basic scalar fields should exist');
68
		$this->assertEquals('Test Product', $result->Title);
69
	}
70
71
	public function testCreateList() {
72
		$list = ManyManyList::create('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID',
73
			'DataObjectTest_PlayerID');
74
		$this->assertEquals(2, $list->count());
75
	}
76
77
78
	public function testRelationshipEmptyOnNewRecords() {
79
		// Relies on the fact that (unrelated) teams exist in the fixture file already
80
		$newPlayer = new DataObjectTest_Player(); // many_many Teams
81
		$this->assertEquals(array(), $newPlayer->Teams()->column('ID'));
82
	}
83
84
	public function testAddingSingleDataObjectByReference() {
85
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
86
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
87
		$player1->Teams()->add($team1);
88
		$player1->flushCache();
89
90
		$compareTeams = new ManyManyList('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID',
91
			'DataObjectTest_PlayerID');
92
		$compareTeams = $compareTeams->forForeignID($player1->ID);
93
		$this->assertEquals($player1->Teams()->column('ID'),$compareTeams->column('ID'),
94
			"Adding single record as DataObject to many_many");
95
	}
96
97
	public function testRemovingSingleDataObjectByReference() {
98
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
99
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
100
		$player1->Teams()->remove($team1);
101
		$player1->flushCache();
102
		$compareTeams = new ManyManyList('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID',
103
			'DataObjectTest_PlayerID');
104
		$compareTeams = $compareTeams->forForeignID($player1->ID);
105
		$this->assertEquals($player1->Teams()->column('ID'),$compareTeams->column('ID'),
106
			"Removing single record as DataObject from many_many");
107
	}
108
109
	public function testAddingSingleDataObjectByID() {
110
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
111
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
112
		$player1->Teams()->add($team1->ID);
113
		$player1->flushCache();
114
		$compareTeams = new ManyManyList('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID',
115
			'DataObjectTest_PlayerID');
116
		$compareTeams = $compareTeams->forForeignID($player1->ID);
117
		$this->assertEquals($player1->Teams()->column('ID'), $compareTeams->column('ID'),
118
			"Adding single record as ID to many_many");
119
	}
120
121
	public function testRemoveByID() {
122
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
123
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
124
		$player1->Teams()->removeByID($team1->ID);
125
		$player1->flushCache();
126
		$compareTeams = new ManyManyList('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID',
127
			'DataObjectTest_PlayerID');
128
		$compareTeams = $compareTeams->forForeignID($player1->ID);
129
		$this->assertEquals($player1->Teams()->column('ID'), $compareTeams->column('ID'),
130
			"Removing single record as ID from many_many");
131
	}
132
133
	public function testSetByIdList() {
134
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
135
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
136
		$team2 = $this->objFromFixture('DataObjectTest_Team', 'team2');
137
		$player1->Teams()->setByIdList(array($team1->ID, $team2->ID));
138
		$this->assertEquals(array($team1->ID, $team2->ID), $player1->Teams()->sort('Title')->column());
139
		$player1->Teams()->setByIdList(array($team1->ID));
140
		$this->assertEquals(array($team1->ID), $player1->Teams()->sort('Title')->column());
141
		$player1->Teams()->setByIdList(array($team2->ID));
142
		$this->assertEquals(array($team2->ID), $player1->Teams()->sort('Title')->column());
143
	}
144
145
	public function testAddingWithMultipleForeignKeys() {
146
		$newPlayer = new DataObjectTest_Player();
147
		$newPlayer->write();
148
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
149
		$team2 = $this->objFromFixture('DataObjectTest_Team', 'team2');
150
151
		$playersTeam1Team2 = DataObjectTest_Team::get()->relation('Players')
152
			->forForeignID(array($team1->ID, $team2->ID));
153
		$playersTeam1Team2->add($newPlayer);
154
		$this->assertEquals(
155
			array($team1->ID, $team2->ID),
156
			$newPlayer->Teams()->sort('Title')->column('ID')
157
		);
158
	}
159
160
	public function testAddingExistingDoesntRemoveExtraFields() {
161
		$player = new DataObjectTest_Player();
162
		$player->write();
163
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
164
165
		$team1->Players()->add($player, array('Position' => 'Captain'));
166
		$this->assertEquals(
167
			array('Position' => 'Captain'),
168
			$team1->Players()->getExtraData('Teams', $player->ID),
169
			'Writes extrafields'
170
		);
171
172
		$team1->Players()->add($player);
173
		$this->assertEquals(
174
			array('Position' => 'Captain'),
175
			$team1->Players()->getExtraData('Teams', $player->ID),
176
			'Retains extrafields on subsequent adds with NULL fields'
177
		);
178
179
		$team1->Players()->add($player, array('Position' => 'Defense'));
180
		$this->assertEquals(
181
			array('Position' => 'Defense'),
182
			$team1->Players()->getExtraData('Teams', $player->ID),
183
			'Updates extrafields on subsequent adds with fields'
184
		);
185
186
		$team1->Players()->add($player, array('Position' => null));
187
		$this->assertEquals(
188
			array('Position' => null),
189
			$team1->Players()->getExtraData('Teams', $player->ID),
190
			'Allows clearing of extrafields on subsequent adds'
191
		);
192
	}
193
194
	public function testSubtractOnAManyManyList() {
195
		$allList = ManyManyList::create('DataObjectTest_Player', 'DataObjectTest_Team_Players',
196
			'DataObjectTest_PlayerID', 'DataObjectTest_TeamID');
197
		$this->assertEquals(3, $allList->count(),
198
			'Precondition; we have all 3 players connected to a team in the list');
199
200
		$teamOneID = $this->idFromFixture('DataObjectTest_Team', 'team1');
201
		$teamTwoID = $this->idFromFixture('DataObjectTest_Team', 'team2');
202
203
		// Captain 1 belongs to one team; team1
204
		$captain1 = $this->objFromFixture('DataObjectTest_Player', 'captain1');
205
		$this->assertEquals(array($teamOneID),$captain1->Teams()->column("ID"),
206
			'Precondition; player2 belongs to team1');
207
208
		// Player 2 belongs to both teams: team1, team2
209
		$player2 = $this->objFromFixture('DataObjectTest_Player', 'player2');
210
		$this->assertEquals(array($teamOneID,$teamTwoID), $player2->Teams()->sort('Title')->column('ID'),
211
			'Precondition; player2 belongs to team1 and team2');
212
213
		// We want to find the teams for player2 where the captain does not belong to
214
		$teamsWithoutTheCaptain = $player2->Teams()->subtract($captain1->Teams());
215
216
		// Assertions
217
		$this->assertEquals(1,$teamsWithoutTheCaptain->count(),
218
			'The ManyManyList should onlu contain one team');
219
		$this->assertEquals($teamTwoID, $teamsWithoutTheCaptain->first()->ID,
220
			'The ManyManyList contains the wrong team');
221
	}
222
223
	public function testRemoveAll() {
224
		$first = new DataObjectTest_Team();
225
		$first->write();
226
227
		$second = new DataObjectTest_Team();
228
		$second->write();
229
230
		$firstPlayers = $first->Players();
231
		$secondPlayers = $second->Players();
232
233
		$a = new DataObjectTest_Player();
234
		$a->ShirtNumber = 'a';
0 ignored issues
show
Documentation introduced by
The property ShirtNumber does not exist on object<DataObjectTest_Player>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
235
		$a->write();
236
237
		$b = new DataObjectTest_Player();
238
		$b->ShirtNumber = 'b';
0 ignored issues
show
Documentation introduced by
The property ShirtNumber does not exist on object<DataObjectTest_Player>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
239
		$b->write();
240
241
		$firstPlayers->add($a);
242
		$firstPlayers->add($b);
243
244
		$secondPlayers->add($a);
245
		$secondPlayers->add($b);
246
247
		$this->assertEquals(array('a', 'b'), $firstPlayers->sort('ShirtNumber')->column('ShirtNumber'));
248
		$this->assertEquals(array('a', 'b'), $secondPlayers->sort('ShirtNumber')->column('ShirtNumber'));
249
250
		$firstPlayers->removeAll();
251
252
		$this->assertEquals(0, count($firstPlayers));
253
		$this->assertEquals(2, count($secondPlayers));
254
255
		$firstPlayers->removeAll();
256
257
		$firstPlayers->add($a);
258
		$firstPlayers->add($b);
259
260
		$this->assertEquals(array('a', 'b'), $firstPlayers->sort('ShirtNumber')->column('ShirtNumber'));
261
262
		$firstPlayers->filter('ShirtNumber', 'b')->removeAll();
263
264
		$this->assertEquals(array('a'), $firstPlayers->column('ShirtNumber'));
265
		$this->assertEquals(array('a', 'b'), $secondPlayers->sort('ShirtNumber')->column('ShirtNumber'));
266
267
		$this->assertNotNull(DataObjectTest_Player::get()->byID($a->ID));
268
		$this->assertNotNull(DataObjectTest_Player::get()->byID($b->ID));
269
	}
270
271
	public function testAppendExtraFieldsToQuery() {
272
		$list = new ManyManyList(
273
			'ManyManyListTest_ExtraFields',
274
			'ManyManyListTest_ExtraFields_Clients',
275
			'ManyManyListTest_ExtraFieldsID',
276
			'ChildID', array(
277
				'Worth' => 'Money',
278
				'Reference' => 'Varchar'
279
			)
280
		);
281
282
		// ensure that ManyManyListTest_ExtraFields_Clients.ValueCurrency is
283
		// selected.
284
		$db = DB::get_conn();
285
		$expected = 'SELECT DISTINCT "ManyManyListTest_ExtraFields_Clients"."WorthCurrency",'
286
			.' "ManyManyListTest_ExtraFields_Clients"."WorthAmount", "ManyManyListTest_ExtraFields_Clients"."Reference",'
287
			.' "ManyManyListTest_ExtraFields"."ClassName", "ManyManyListTest_ExtraFields"."LastEdited",'
288
			.' "ManyManyListTest_ExtraFields"."Created", "ManyManyListTest_ExtraFields"."ID",'
289
			.' CASE WHEN "ManyManyListTest_ExtraFields"."ClassName" IS NOT NULL THEN'
290
			.' "ManyManyListTest_ExtraFields"."ClassName" ELSE '. Convert::raw2sql('ManyManyListTest_ExtraFields', true)
291
			.' END AS "RecordClassName" FROM "ManyManyListTest_ExtraFields" INNER JOIN'
292
			.' "ManyManyListTest_ExtraFields_Clients" ON'
293
			.' "ManyManyListTest_ExtraFields_Clients"."ManyManyListTest_ExtraFieldsID" ='
294
			.' "ManyManyListTest_ExtraFields"."ID"';
295
296
		$this->assertSQLEquals($expected, $list->sql($parameters));
297
	}
298
299
	public function testFilteringOnPreviouslyJoinedTable() {
300
301
		/** @var ManyManyListTest_Category $category */
302
		$category = $this->objFromFixture('ManyManyListTest_Category', 'categorya');
303
304
		/** @var ManyManyList $productsRelatedToProductB */
305
		$productsRelatedToProductB = $category->Products()->filter('RelatedProducts.Title', 'Product B');
306
307
		$this->assertEquals(1, $productsRelatedToProductB->count());
308
	}
309
310
    public function testWriteManipulationWithNonScalarValuesAllowed()
311
    {
312
        $left = MockDynamicAssignmentDataObject::create();
313
        $left->write();
314
        $right = MockDynamicAssignmentDataObject::create();
315
        $right->write();
316
        $left->MockManyMany()->add($right, array(
0 ignored issues
show
Bug introduced by
The method MockManyMany() does not exist on MockDynamicAssignmentDataObject. Did you maybe mean manyMany()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
317
            'ManyManyStaticScalarOnlyField' => true,
318
            'ManyManyDynamicScalarOnlyField' => false,
319
            'ManyManyDynamicField' => true
320
        ));
321
        $pivot = $left->MockManyMany()->first();
0 ignored issues
show
Bug introduced by
The method MockManyMany() does not exist on MockDynamicAssignmentDataObject. Did you maybe mean manyMany()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
322
        $this->assertEquals(1, $pivot->ManyManyStaticScalarOnlyField);
323
        $this->assertEquals(0, $pivot->ManyManyDynamicScalarOnlyField);
324
        $this->assertEquals(1, $pivot->ManyManyDynamicField);
325
    }
326
327
    /**
328
     * @expectedException PHPUnit_Framework_Error
329
     * @expectedExceptionMessageRegExp /parameterised field assignments are disallowed/
330
     */
331
    public function testWriteManipulationWithNonScalarValuesDisallowed()
332
    {
333
        $left = MockDynamicAssignmentDataObject::create();
334
        $left->write();
335
        $right = MockDynamicAssignmentDataObject::create();
336
        $right->write();
337
338
        $left->MockManyMany()->add($right, array(
0 ignored issues
show
Bug introduced by
The method MockManyMany() does not exist on MockDynamicAssignmentDataObject. Did you maybe mean manyMany()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
339
            'ManyManyStaticScalarOnlyField' => false,
340
            'ManyManyDynamicScalarOnlyField' => true,
341
            'ManyManyDynamicField' => false
342
        ));
343
    }
344
}
345
346
/**
347
 * @package framework
348
 * @subpackage tests
349
 */
350
class ManyManyListTest_ExtraFields extends DataObject implements TestOnly {
351
352
	private static $many_many = array(
353
		'Clients' => 'ManyManyListTest_ExtraFields',
354
		'Products' => 'ManyManyListTest_Product'
355
	);
356
357
	private static $belongs_many_many = array(
358
		'WorksWith' => 'ManyManyListTest_ExtraFields'
359
	);
360
361
	private static $many_many_extraFields = array(
362
		'Clients' => array(
363
			'Reference' => 'Varchar',
364
			'Worth' => 'Money'
365
		),
366
		'Products' => array(
367
			'Reference' => 'Varchar'
368
		)
369
	);
370
}
371
372
class ManyManyListTest_Product extends DataObject implements TestOnly {
373
374
	private static $db = array(
375
		'Title' => 'Varchar'
376
	);
377
378
	private static $many_many = array(
379
		'RelatedProducts' => 'ManyManyListTest_Product'
380
	);
381
382
	private static $belongs_many_many = array(
383
		'RelatedTo' => 'ManyManyListTest_Product',
384
		'Categories' => 'ManyManyListTest_Category'
385
	);
386
387
	private static $default_sort = '"Title" IS NOT NULL ASC, "Title" ASC';
388
389
}
390
391
class ManyManyListTest_Category extends DataObject implements TestOnly {
392
393
	private static $db = array(
394
		'Title' => 'Varchar'
395
	);
396
397
	private static $many_many = array(
398
		'Products' => 'ManyManyListTest_Product'
399
	);
400
401
}
402
403