EagerLoaderTest::testReformatContain()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 2
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 6 and the first side effect is on line 2.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
require_once App::pluginPath('EagerLoader') . 'Test' . DS . 'bootstrap.php';
3
4
App::uses('EagerLoader', 'EagerLoader.Model');
5
6
class EagerLoaderTest extends CakeTestCase {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
7
8
/**
9
 * autoFixtures property
10
 *
11
 * @var bool
12
 */
13
	public $autoFixtures = false;
14
15
/**
16
 * Fixtures
17
 *
18
 * @var array
19
 */
20
	public $fixtures = array(
21
		'core.user',
22
		'core.article',
23
		'core.comment',
24
		'core.attachment',
25
		'core.tag',
26
		'core.articles_tag',
27
		'core.apple',
28
		'core.category',
29
	);
30
31
/**
32
 * setUp method
33
 *
34
 * @return void
35
 */
36
	public function setUp() {
37
		parent::setUp();
38
		$this->EagerLoader = new EagerLoader();
39
	}
40
41
/**
42
 * Tests reformatContain method
43
 *
44
 * @param array|string $contain Value of `contain` option
45
 * @param array $expected Expected
46
 * @return void
47
 *
48
 * @dataProvider dataProviderForTestReformatContain
49
 */
50
	public function testReformatContain($contain, $expected) {
51
		$method = new ReflectionMethod($this->EagerLoader, 'reformatContain');
52
		$method->setAccessible(true);
53
		$result = $method->invoke($this->EagerLoader, $contain);
54
		$this->assertEquals($expected, $result);
55
	}
56
57
/**
58
 * Data provider for testReformatContain
59
 *
60
 * @return array
61
 */
62
	public function dataProviderForTestReformatContain() {
63
		return array(
64
			array(
65
				// {{{ #0
66
				'User',
67
				array(
68
					'options' => array(),
69
					'contain' => array(
70
						'User' => array('options' => array(), 'contain' => array()),
71
					),
72
				),
73
				// }}}
74
			),
75
			array(
76
				// {{{ #1
77
				'User.Profile',
78
				array(
79
					'options' => array(),
80
					'contain' => array(
81
						'User' => array(
82
							'options' => array(),
83
							'contain' => array(
84
								'Profile' => array('options' => array(), 'contain' => array()),
85
							),
86
						),
87
					),
88
				),
89
				// }}}
90
			),
91
			array(
92
				// {{{ #2
93
				array(
94
					'Comment' => array(
95
						'User' => array(),
96
						'limit' => 3,
97
						'order' => array('id' => 'desc'),
98
						'conditions' => array('published' => 'Y'),
99
					),
100
				),
101
				array(
102
					'options' => array(),
103
					'contain' => array(
104
						'Comment' => array(
105
							'options' => array(
106
								'limit' => 3,
107
								'order' => array('id' => 'desc'),
108
								'conditions' => array('published' => 'Y'),
109
							),
110
							'contain' => array(
111
								'User' => array('options' => array(), 'contain' => array()),
112
							),
113
						),
114
					),
115
				),
116
				// }}}
117
			),
118
			array(
119
				// {{{ #3
120
				array(
121
					'User' => array('fields' => array('name')),
122
					'User.Profile' => array('fields' => array('address')),
123
					'Comment.User.Profile',
124
				),
125
				array(
126
					'options' => array(),
127
					'contain' => array(
128
						'User' => array(
129
							'contain' => array(
130
								'Profile' => array('options' => array('fields' => array('address')), 'contain' => array()),
131
							),
132
							'options' => array('fields' => array('name')),
133
						),
134
						'Comment' => array(
135
							'options' => array(),
136
							'contain' => array(
137
								'User' => array(
138
									'options' => array(),
139
									'contain' => array(
140
										'Profile' => array('options' => array(), 'contain' => array()),
141
									),
142
								),
143
							),
144
						),
145
					),
146
				),
147
				// }}}
148
			),
149
		);
150
	}
151
152
/**
153
 * Tests buildJoinQuery method
154
 *
155
 * @return void
156
 */
157
	public function testBuildJoinQuery() {
158
		$this->loadFixtures('User');
159
		$User = ClassRegistry::init('User');
160
161
		$db = $User->getDataSource();
0 ignored issues
show
Unused Code introduced by
$db is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
162
163
		$method = new ReflectionMethod($this->EagerLoader, 'buildJoinQuery');
164
		$method->setAccessible(true);
165
		$result = $method->invokeArgs($this->EagerLoader, array(
166
			$User,
167
			array('fields' => array()),
168
			'INNER',
169
			array('Article.user_id' => 'User.id'),
170
			array(
171
				'conditions' => array(
172
					array('Article.user_id' => array(1, 2, 3)),
173
				)
174
			)
175
		));
176
177
		$expected = array(
178
			// {{{
179
			'fields' => array(
180
				'User.id',
181
				'User.user',
182
				'User.password',
183
				'User.created',
184
				'User.updated',
185
				'Article.user_id',
186
			),
187
			'joins' => array(
188
				array(
189
					'type' => 'INNER',
190
					'table' => $User,
191
					'alias' => 'User',
192
					'conditions' => array(
193
						array('Article.user_id' => array(1, 2, 3)),
194
						array('Article.user_id' => (object)array('type' => 'identifier', 'value' => 'User.id')),
195
					),
196
				),
197
			),
198
			'conditions' => array(),
199
			'order' => array(),
200
			// }}}
201
		);
202
203
		$this->assertEquals($expected, $result);
204
	}
205
206
/**
207
 * Tests perseContain method
208
 *
209
 * @param string $model Parent model of the contained model
210
 * @param string $alias Alias of the contained model
211
 * @param array $contain Reformatted `contain` option for the deep associations
212
 * @param array $expected Expected
213
 * @return void
214
 *
215
 * @dataProvider dataProviderForTestParseContain
216
 */
217
	public function testParseContain($model, $alias, $contain, $expected) {
218
		$this->loadFixtures('ArticlesTag', 'Apple');
219
220
		$model = ClassRegistry::init($model);
221
222
		$method = new ReflectionMethod($this->EagerLoader, 'parseContain');
223
		$method->setAccessible(true);
224
225
		$result = $method->invokeArgs($this->EagerLoader, array($model, $alias, $contain));
226
227
		// Remove something
228
		$result = Hash::remove($result, '{s}.{n}.target');
229
		$result = Hash::remove($result, '{s}.{n}.parent');
230
		$result = Hash::remove($result, '{s}.{n}.habtm');
231
		$result = Hash::remove($result, '{s}.{n}.finderQuery');
232
233
		$this->assertEquals($expected, $result);
234
	}
235
236
/**
237
 * Data provider for testParseContain
238
 *
239
 * @return array
240
 */
241
	public function dataProviderForTestParseContain() {
242
		return array(
243
			array(
244
				// {{{ #0 normal
245
				'Comment',
246
				'Article',
247
				array(
248
					'options' => array(),
249
					'contain' => array(
250
						'User' => array(
251
							'options' => array(),
252
							'contain' => array(),
253
						),
254
					),
255
				),
256
				array(
257
					'Comment' => array(
258
						array(
259
							'alias' => 'Article',
260
							'parentAlias' => 'Comment',
261
							'parentKey' => 'article_id',
262
							'targetKey' => 'id',
263
							'aliasPath' => 'Comment.Article',
264
							'propertyPath' => 'Article',
265
							'options' => array(),
266
							'has' => false,
267
							'belong' => true,
268
							'many' => false,
269
							'external' => false,
270
						),
271
						array(
272
							'alias' => 'User',
273
							'parentAlias' => 'Article',
274
							'parentKey' => 'user_id',
275
							'targetKey' => 'id',
276
							'aliasPath' => 'Comment.Article.User',
277
							'propertyPath' => 'Article.User',
278
							'options' => array(),
279
							'has' => false,
280
							'belong' => true,
281
							'many' => false,
282
							'external' => false,
283
							'eager' => true,
284
						),
285
					),
286
				),
287
				// }}}
288
			),
289
			array(
290
				// {{{ #1 complex
291
				'User',
292
				'Article',
293
				array(
294
					'options' => array(),
295
					'contain' => array(
296
						'Comment' => array(
297
							'options' => array('limit' => 3),
298
							'contain' => array(
299
								'User' => array('options' => array(), 'contain' => array()),
300
								'Attachment' => array('options' => array(), 'contain' => array()),
301
							),
302
						),
303
						'Tag' => array('options' => array(), 'contain' => array())
304
					),
305
				),
306
				array(
307
					'User' => array(
308
						array(
309
							'alias' => 'Article',
310
							'parentAlias' => 'User',
311
							'parentKey' => 'id',
312
							'targetKey' => 'user_id',
313
							'aliasPath' => 'User.Article',
314
							'propertyPath' => 'Article',
315
							'options' => array(),
316
							'has' => true,
317
							'belong' => false,
318
							'many' => true,
319
							'external' => true,
320
						),
321
					),
322
					'User.Article' => array(
323
						array(
324
							'alias' => 'Comment',
325
							'parentAlias' => 'Article',
326
							'parentKey' => 'id',
327
							'targetKey' => 'article_id',
328
							'aliasPath' => 'User.Article.Comment',
329
							'propertyPath' => 'Article.Comment',
330
							'options' => array('limit' => 3),
331
							'has' => true,
332
							'belong' => false,
333
							'many' => true,
334
							'external' => true,
335
						),
336
						array(
337
							'alias' => 'Tag',
338
							'parentAlias' => 'Article',
339
							'parentKey' => 'id',
340
							'targetKey' => 'id',
341
							'aliasPath' => 'User.Article.Tag',
342
							'propertyPath' => 'Article.Tag',
343
							'options' => array(),
344
							'has' => true,
345
							'belong' => true,
346
							'many' => true,
347
							'habtmAlias' => 'ArticlesTag',
348
							'habtmParentKey' => 'article_id',
349
							'habtmTargetKey' => 'tag_id',
350
							'external' => true,
351
						),
352
					),
353
					'User.Article.Comment' => array(
354
						array(
355
							'alias' => 'User',
356
							'parentAlias' => 'Comment',
357
							'parentKey' => 'user_id',
358
							'targetKey' => 'id',
359
							'aliasPath' => 'User.Article.Comment.User',
360
							'propertyPath' => 'Comment.User',
361
							'options' => array(),
362
							'has' => false,
363
							'belong' => true,
364
							'many' => false,
365
							'external' => false,
366
						),
367
						array(
368
							'alias' => 'Attachment',
369
							'parentAlias' => 'Comment',
370
							'parentKey' => 'id',
371
							'targetKey' => 'comment_id',
372
							'aliasPath' => 'User.Article.Comment.Attachment',
373
							'propertyPath' => 'Comment.Attachment',
374
							'options' => array(),
375
							'has' => true,
376
							'belong' => false,
377
							'many' => false,
378
							'external' => false,
379
						),
380
					),
381
				),
382
				// }}}
383
			),
384
			array(
385
				// {{{ #2 HABTM
386
				'Article',
387
				'Tag',
388
				array(
389
					'options' => array(),
390
					'contain' => array(
391
						'Article' => array('options' => array(), 'contain' => array()),
392
					),
393
				),
394
				array(
395
					'Article' => array(
396
						array(
397
							'alias' => 'Tag',
398
							'parentAlias' => 'Article',
399
							'parentKey' => 'id',
400
							'targetKey' => 'id',
401
							'aliasPath' => 'Article.Tag',
402
							'propertyPath' => 'Tag',
403
							'options' => array(),
404
							'has' => true,
405
							'belong' => true,
406
							'many' => true,
407
							'habtmAlias' => 'ArticlesTag',
408
							'habtmParentKey' => 'article_id',
409
							'habtmTargetKey' => 'tag_id',
410
							'external' => true,
411
						),
412
					),
413
					'Article.Tag' => array(
414
						array(
415
							'alias' => 'Article',
416
							'parentAlias' => 'Tag',
417
							'parentKey' => 'id',
418
							'targetKey' => 'id',
419
							'aliasPath' => 'Article.Tag.Article',
420
							'propertyPath' => 'Tag.Article',
421
							'options' => array(),
422
							'has' => true,
423
							'belong' => true,
424
							'many' => true,
425
							'habtmAlias' => 'ArticlesTag',
426
							'habtmParentKey' => 'tag_id',
427
							'habtmTargetKey' => 'article_id',
428
							'external' => true,
429
						),
430
					),
431
				),
432
				// }}}
433
			),
434
			array(
435
				// {{{ #3 association (external)
436
				'Article',
437
				'SecondComment',
438
				array('options' => array(), 'contain' => array()),
439
				array(
440
					'Article' => array(
441
						array(
442
							'alias' => 'SecondComment',
443
							'parentAlias' => 'Article',
444
							'parentKey' => 'id',
445
							'targetKey' => 'article_id',
446
							'aliasPath' => 'Article.SecondComment',
447
							'propertyPath' => 'SecondComment',
448
							'options' => array(
449
								'order' => 'SecondComment.id',
450
								'limit' => 1,
451
								'offset' => 1,
452
							),
453
							'has' => true,
454
							'belong' => false,
455
							'many' => false,
456
							'external' => true,
457
						),
458
					),
459
				),
460
				// }}}
461
			),
462
			array(
463
				// {{{ #4 association (finderQuery)
464
				'Apple',
465
				'NextApple',
466
				array(
467
					'options' => array(),
468
					'contain' => array(
469
						'ParentApple' => array('options' => array(), 'contain' => array()),
470
					)
471
				),
472
				array(
473
					'Apple' => array(
474
						array(
475
							'alias' => 'NextApple',
476
							'parentAlias' => 'Apple',
477
							'parentKey' => 'id',
478
							'targetKey' => 'apple_id',
479
							'aliasPath' => 'Apple.NextApple',
480
							'propertyPath' => 'NextApple',
481
							'options' => array(),
482
							'has' => true,
483
							'belong' => false,
484
							'many' => false,
485
							'external' => true,
486
						),
487
					),
488
					'Apple.NextApple' => array(
489
						array(
490
							'alias' => 'ParentApple',
491
							'parentAlias' => 'NextApple',
492
							'parentKey' => 'apple_id',
493
							'targetKey' => 'id',
494
							'aliasPath' => 'Apple.NextApple.ParentApple',
495
							'propertyPath' => 'NextApple.ParentApple',
496
							'options' => array(),
497
							'has' => false,
498
							'belong' => true,
499
							'many' => false,
500
							'external' => true,
501
						),
502
					),
503
				),
504
				// }}}
505
			),
506
			array(
507
				// {{{ #5 duplication
508
				'Attachment',
509
				'Comment',
510
				array(
511
					'options' => array(),
512
					'contain' => array(
513
						'Article' => array(
514
							'options' => array(),
515
							'contain' => array(
516
								'User' => array('options' => array(), 'contain' => array()),
517
							),
518
						),
519
						'User' => array('options' => array(), 'contain' => array()),
520
					)
521
				),
522
				array(
523
					'Attachment' => array(
524
						array(
525
							'alias' => 'Comment',
526
							'parentAlias' => 'Attachment',
527
							'parentKey' => 'comment_id',
528
							'targetKey' => 'id',
529
							'aliasPath' => 'Attachment.Comment',
530
							'propertyPath' => 'Comment',
531
							'options' => array(),
532
							'has' => false,
533
							'belong' => true,
534
							'many' => false,
535
							'external' => false,
536
						),
537
						array(
538
							'alias' => 'Article',
539
							'parentAlias' => 'Comment',
540
							'parentKey' => 'article_id',
541
							'targetKey' => 'id',
542
							'aliasPath' => 'Attachment.Comment.Article',
543
							'propertyPath' => 'Comment.Article',
544
							'options' => array(),
545
							'has' => false,
546
							'belong' => true,
547
							'many' => false,
548
							'external' => false,
549
							'eager' => true,
550
						),
551
						array(
552
							'alias' => 'User',
553
							'parentAlias' => 'Article',
554
							'parentKey' => 'user_id',
555
							'targetKey' => 'id',
556
							'aliasPath' => 'Attachment.Comment.Article.User',
557
							'propertyPath' => 'Comment.Article.User',
558
							'options' => array(),
559
							'has' => false,
560
							'belong' => true,
561
							'many' => false,
562
							'external' => false,
563
							'eager' => true,
564
						),
565
					),
566
					'Attachment.Comment' => array(
567
						array(
568
							'alias' => 'User',
569
							'parentAlias' => 'Comment',
570
							'parentKey' => 'user_id',
571
							'targetKey' => 'id',
572
							'aliasPath' => 'Attachment.Comment.User',
573
							'propertyPath' => 'Comment.User',
574
							'options' => array(),
575
							'has' => false,
576
							'belong' => true,
577
							'many' => false,
578
							'external' => true,
579
						),
580
					),
581
				),
582
				// }}}
583
			),
584
			array(
585
				// {{{ #6 self
586
				'Apple',
587
				'ParentApple',
588
				array(
589
					'options' => array(),
590
					'contain' => array(
591
						'ParentApple' => array('options' => array(), 'contain' => array()),
592
					)
593
				),
594
				array(
595
					'Apple' => array(
596
						array(
597
							'alias' => 'ParentApple',
598
							'parentAlias' => 'Apple',
599
							'parentKey' => 'apple_id',
600
							'targetKey' => 'id',
601
							'aliasPath' => 'Apple.ParentApple',
602
							'propertyPath' => 'ParentApple',
603
							'options' => array(),
604
							'has' => false,
605
							'belong' => true,
606
							'many' => false,
607
							'external' => false,
608
						),
609
					),
610
					'Apple.ParentApple' => array(
611
						array(
612
							'alias' => 'ParentApple',
613
							'parentAlias' => 'ParentApple',
614
							'parentKey' => 'apple_id',
615
							'targetKey' => 'id',
616
							'aliasPath' => 'Apple.ParentApple.ParentApple',
617
							'propertyPath' => 'ParentApple.ParentApple',
618
							'options' => array(),
619
							'has' => false,
620
							'belong' => true,
621
							'many' => false,
622
							'external' => true,
623
						),
624
					),
625
				),
626
				// }}}
627
			),
628
629
		);
630
	}
631
632
/**
633
 * Tests that parseContain method throws an exception,
634
 * if the parent model is not associated with the specified model.
635
 *
636
 * @return void
637
 *
638
 * @expectedException InvalidArgumentException
639
 * @expectedExceptionMessage Model "User" is not associated with model "Something"
640
 */
641
	public function testParseContainThrowsException() {
642
		$User = ClassRegistry::init('User');
643
644
		$method = new ReflectionMethod($this->EagerLoader, 'parseContain');
645
		$method->setAccessible(true);
646
		$method->invokeArgs($this->EagerLoader, array(
647
			$User,
648
			'Something',
649
			array('options' => array(), 'contain' => array()),
650
			array(
651
				'root' => 'User',
652
				'aliasPath' => 'User',
653
				'propertyPath' => '',
654
			)
655
		));
656
	}
657
658
/**
659
 * Tests normalizeQuery method
660
 *
661
 * @param string $model Model
662
 * @param array $query Query
663
 * @param array $expected Expected
664
 * @return void
665
 *
666
 * @dataProvider dataProviderForTestNormalizeQuery
667
 */
668
	public function testNormalizeQuery($model, $query, $expected) {
669
		$this->loadFixtures($model);
670
671
		$model = ClassRegistry::init($model);
672
673
		$db = $model->getDataSource();
674
		$startQuote = $db->startQuote;
675
		$endQuote = $db->endQuote;
676
		$db->startQuote = '';
677
		$db->endQuote = '';
678
679
		$method = new ReflectionMethod($this->EagerLoader, 'normalizeQuery');
680
		$method->setAccessible(true);
681
		$result = $method->invokeArgs($this->EagerLoader, array($model, $query));
682
683
		$db->startQuote = $startQuote;
684
		$db->endQuote = $endQuote;
685
686
		$this->assertEquals($expected, $result);
687
	}
688
689
/**
690
 * Data provider for testNormalizeQuery
691
 *
692
 * @return array
693
 */
694
	public function dataProviderForTestNormalizeQuery() {
695
		return array(
696
			array(
697
				// {{{ #0
698
				'User',
699
				array(),
700
				array(
701
					'fields' => array(
702
						'User.id',
703
						'User.user',
704
						'User.password',
705
						'User.created',
706
						'User.updated',
707
					),
708
					'conditions' => array(),
709
					'order' => array(),
710
				),
711
				// }}}
712
			),
713
			array(
714
				// {{{ #1
715
				'User',
716
				array(
717
					'fields' => 'User.id',
718
					'conditions' => '1 = 1',
719
				),
720
				array(
721
					'fields' => array('User.id'),
722
					'conditions' => array('1 = 1'),
723
					'order' => array(),
724
				),
725
				// }}}
726
			),
727
			array(
728
				// {{{ #2
729
				'User',
730
				array(
731
					'fields' => array(
732
						'User.id',
733
						'user',
734
						'password',
735
					),
736
					'conditions' => array(
737
						'user' => 'larry',
738
					),
739
				),
740
				array(
741
					'fields' => array(
742
						'User.id',
743
						'User.user',
744
						'User.password',
745
					),
746
					'conditions' => array(
747
						array('User.user' => 'larry'),
748
					),
749
					'order' => array(),
750
				),
751
				// }}}
752
			),
753
			array(
754
				// {{{ #3
755
				'User',
756
				array(
757
					'fields' => 'id',
758
					'conditions' => array(),
759
					'order' => array(
760
						'id' => 'ASC',
761
						'User.id' => 'DESC',
762
						'created' => 'DESC',
763
						'updated',
764
					),
765
				),
766
				array(
767
					'fields' => array(
768
						'User.id',
769
					),
770
					'conditions' => array(),
771
					'order' => array(
772
						'User.id' => 'ASC',
773
						'User.created' => 'DESC',
774
						'User.updated',
775
					),
776
				),
777
				// }}}
778
			),
779
			array(
780
				// {{{ #4 Virtual fields
781
				'Category',
782
				array(
783
					'fields' => 'is_root',
784
					'conditions' => array('is_root' => 0),
785
					'order' => array(
786
						'is_root',
787
					),
788
				),
789
				array(
790
					'fields' => array(
791
						'(CASE WHEN Category.parent_id = 0 THEN 1 ELSE 0 END) AS  Category__is_root',
792
					),
793
					'conditions' => array(
794
						(object)array(
795
							'type' => 'expression',
796
							'value' => '(CASE WHEN Category.parent_id = 0 THEN 1 ELSE 0 END) = 0',
797
						)
798
					),
799
					'order' => array(
800
						'(CASE WHEN Category.parent_id = 0 THEN 1 ELSE 0 END)',
801
					),
802
				),
803
				// }}}
804
			),
805
		);
806
	}
807
808
/**
809
 * Tests mergeExternalExternal and mergeInternalExternal
810
 *
811
 * @param string $parent Name of the parent model
812
 * @param string $target Name of the target model
813
 * @param array $meta Meta data to be used for eager loading
814
 * @param array $results Results
815
 * @param array $fixtures Fixtures to be used
816
 * @param array $expectedArgument Expected argument for loadExternal method
817
 * @param array $expectedResults Expected results
818
 * @return void
819
 *
820
 * @dataProvider dataProviderForTestMergeExternal
821
 */
822
	public function testMergeExternal($parent, $target, $meta, $results, $fixtures, $expectedArgument, $expectedResults) {
823
		call_user_func_array(array($this, 'loadFixtures'), $fixtures);
824
825
		$parent = ClassRegistry::init($parent);
826
		$target = $parent->$target;
827
828
		$meta += array(
829
			'parent' => $parent,
830
			'target' => $target,
831
			'alias' => $target->alias,
832
			'parentAlias' => $parent->alias,
833
			'aliasPath' => $parent->alias . '.' . $target->alias,
834
			'propertyPath' => $parent->alias . '.' . $target->alias,
835
		);
836
837
		if (isset($meta['habtmAlias'])) {
838
			$meta['habtm'] = $parent->{$meta['habtmAlias']};
839
		}
840
841
		if ($target->alias === 'NextApple') {
842
			$meta['finderQuery'] = $target->getNextAppleFinderQuery();
843
		}
844
845
		$EagerLoader = $this->getMock('EagerLoader', array('loadExternal'));
846
		$EagerLoader->expects($this->once())
847
			->method('loadExternal')
848
			->with($target, $meta['aliasPath'], $expectedArgument)
849
			->will($this->returnArgument(2));
850
851
		$method = new ReflectionMethod($EagerLoader, ($meta['external'] ? 'mergeExternalExternal' : 'mergeInternalExternal'));
852
		$method->setAccessible(true);
853
		$merged = $method->invokeArgs($EagerLoader, array($target, $results, $meta));
854
855
		$this->assertEquals($expectedResults, $merged);
856
	}
857
858
/**
859
 * Data provider for testMergeExternal method
860
 *
861
 * @return array
862
 */
863
	public function dataProviderForTestMergeExternal() {
864
		return array(
865
			array(
866
				// {{{ #0 hasMany
867
				'User',
868
				'Comment',
869
				// $meta
870
				array(
871
					'parentKey' => 'id',
872
					'targetKey' => 'user_id',
873
					'options' => array('fields' => 'id'),
874
					'has' => true,
875
					'belong' => false,
876
					'many' => true,
877
					'external' => true,
878
				),
879
				// $results
880
				array(
881
					array(
882
						'User' => array(
883
							'id' => '2',
884
						),
885
					),
886
					array(
887
						'User' => array(
888
							'id' => '4',
889
						),
890
					),
891
				),
892
				// $frixtures
893
				array('User', 'Comment'),
894
				// $expectedArgument
895
				array(
896
					array(
897
						'Comment' => array(
898
							'id' => '1',
899
							'user_id' => '2',
900
						),
901
						'EagerLoaderModel' => array(
902
							'assoc_id' => '2',
903
						),
904
					),
905
					array(
906
						'Comment' => array(
907
							'id' => '2',
908
							'user_id' => '4',
909
						),
910
						'EagerLoaderModel' => array(
911
							'assoc_id' => '4',
912
						),
913
					),
914
					array(
915
						'Comment' => array(
916
							'id' => '6',
917
							'user_id' => '2',
918
						),
919
						'EagerLoaderModel' => array(
920
							'assoc_id' => '2',
921
						),
922
					),
923
				),
924
				// $expectedResults
925
				array(
926
					array(
927
						'User' => array(
928
							'id' => '2',
929
							'Comment' => array(
930
								array(
931
									'id' => '1',
932
									'user_id' => '2',
933
								),
934
								array(
935
									'id' => '6',
936
									'user_id' => '2',
937
								),
938
							),
939
						),
940
					),
941
					array(
942
						'User' => array(
943
							'id' => '4',
944
							'Comment' => array(
945
								array(
946
									'id' => '2',
947
									'user_id' => '4',
948
								),
949
							),
950
						),
951
					)
952
				),
953
				// }}}
954
			),
955
			array(
956
				// {{{ #1 hasMany (limited)
957
				'User',
958
				'Comment',
959
				// $meta
960
				array(
961
					'parentKey' => 'id',
962
					'targetKey' => 'user_id',
963
					'options' => array('fields' => 'id', 'limit' => 1),
964
					'has' => true,
965
					'belong' => false,
966
					'many' => true,
967
					'external' => true,
968
				),
969
				// $results
970
				array(
971
					array(
972
						'User' => array(
973
							'id' => '2',
974
						),
975
					),
976
					array(
977
						'User' => array(
978
							'id' => '4',
979
						),
980
					),
981
				),
982
				// $frixtures
983
				array('User', 'Comment'),
984
				// $expectedArgument
985
				array(
986
					array(
987
						'Comment' => array(
988
							'id' => '1',
989
							'user_id' => '2',
990
						),
991
						'EagerLoaderModel' => array(
992
							'assoc_id' => '2',
993
						),
994
					),
995
					array(
996
						'Comment' => array(
997
							'id' => '2',
998
							'user_id' => '4',
999
						),
1000
						'EagerLoaderModel' => array(
1001
							'assoc_id' => '4',
1002
						),
1003
					),
1004
				),
1005
				// $expectedResults
1006
				array(
1007
					array(
1008
						'User' => array(
1009
							'id' => '2',
1010
							'Comment' => array(
1011
								array(
1012
									'id' => '1',
1013
									'user_id' => '2',
1014
								),
1015
							),
1016
						),
1017
					),
1018
					array(
1019
						'User' => array(
1020
							'id' => '4',
1021
							'Comment' => array(
1022
								array(
1023
									'id' => '2',
1024
									'user_id' => '4',
1025
								),
1026
							),
1027
						),
1028
					)
1029
				),
1030
				// }}}
1031
			),
1032
			array(
1033
				// {{{ #2 hasOne (external)
1034
				'Comment',
1035
				'Attachment',
1036
				// $meta
1037
				array(
1038
					'parentKey' => 'id',
1039
					'targetKey' => 'comment_id',
1040
					'options' => array('fields' => 'id'),
1041
					'has' => true,
1042
					'belong' => false,
1043
					'many' => false,
1044
					'external' => true,
1045
				),
1046
				// $results
1047
				array(
1048
					array(
1049
						'Comment' => array(
1050
							'id' => '5',
1051
						),
1052
					),
1053
				),
1054
				// $frixtures
1055
				array('Comment', 'Attachment'),
1056
				// $expectedArgument
1057
				array(
1058
					array(
1059
						'Attachment' => array(
1060
							'id' => '1',
1061
							'comment_id' => '5',
1062
						),
1063
						'EagerLoaderModel' => array(
1064
							'assoc_id' => '5',
1065
						),
1066
					),
1067
				),
1068
				// $expectedResults
1069
				array(
1070
					array(
1071
						'Comment' => array(
1072
							'id' => '5',
1073
							'Attachment' => array(
1074
								'id' => '1',
1075
								'comment_id' => '5',
1076
							),
1077
						),
1078
					),
1079
				),
1080
				// }}}
1081
			),
1082
			array(
1083
				// {{{ #3 hasAndBelongsToMany
1084
				'Article',
1085
				'Tag',
1086
				// $meta
1087
				array(
1088
					'parentKey' => 'id',
1089
					'targetKey' => 'id',
1090
					'options' => array('fields' => 'id', 'order' => array('ArticlesTag.article_id')),
1091
					'habtmAlias' => 'ArticlesTag',
1092
					'habtmParentKey' => 'article_id',
1093
					'habtmTargetKey' => 'tag_id',
1094
					'has' => true,
1095
					'belong' => true,
1096
					'many' => true,
1097
					'external' => true,
1098
				),
1099
				// $results
1100
				array(
1101
					array(
1102
						'Article' => array(
1103
							'id' => '1',
1104
						),
1105
					),
1106
					array(
1107
						'Article' => array(
1108
							'id' => '2',
1109
						),
1110
					),
1111
				),
1112
				// $frixtures
1113
				array('Article', 'Tag', 'ArticlesTag'),
1114
				// $expectedArgument
1115
				array(
1116
					array(
1117
						'Tag' => array(
1118
							'id' => '1',
1119
						),
1120
						'ArticlesTag' => array(
1121
							'article_id' => '1',
1122
							'tag_id' => '1',
1123
						),
1124
						'EagerLoaderModel' => array(
1125
							'assoc_id' => '1',
1126
						),
1127
					),
1128
					array(
1129
						'Tag' => array(
1130
							'id' => '2',
1131
						),
1132
						'ArticlesTag' => array(
1133
							'article_id' => '1',
1134
							'tag_id' => '2',
1135
						),
1136
						'EagerLoaderModel' => array(
1137
							'assoc_id' => '1',
1138
						),
1139
					),
1140
					array(
1141
						'Tag' => array(
1142
							'id' => '1',
1143
						),
1144
						'ArticlesTag' => array(
1145
							'article_id' => '2',
1146
							'tag_id' => '1',
1147
						),
1148
						'EagerLoaderModel' => array(
1149
							'assoc_id' => '2',
1150
						),
1151
					),
1152
					array(
1153
						'Tag' => array(
1154
							'id' => '3',
1155
						),
1156
						'ArticlesTag' => array(
1157
							'article_id' => '2',
1158
							'tag_id' => '3',
1159
						),
1160
						'EagerLoaderModel' => array(
1161
							'assoc_id' => '2',
1162
						),
1163
					),
1164
				),
1165
				// $expectedResults
1166
				array(
1167
					array(
1168
						'Article' => array(
1169
							'id' => '1',
1170
							'Tag' => array(
1171
								array(
1172
									'id' => '1',
1173
									'ArticlesTag' => array(
1174
										'article_id' => '1',
1175
										'tag_id' => '1',
1176
									),
1177
								),
1178
								array(
1179
									'id' => '2',
1180
									'ArticlesTag' => array(
1181
										'article_id' => '1',
1182
										'tag_id' => '2',
1183
									),
1184
								),
1185
							),
1186
						),
1187
					),
1188
					array(
1189
						'Article' => array(
1190
							'id' => '2',
1191
							'Tag' => array(
1192
								array(
1193
									'id' => '1',
1194
									'ArticlesTag' => array(
1195
										'article_id' => '2',
1196
										'tag_id' => '1',
1197
									),
1198
								),
1199
								array(
1200
									'id' => '3',
1201
									'ArticlesTag' => array(
1202
										'article_id' => '2',
1203
										'tag_id' => '3',
1204
									),
1205
								),
1206
							),
1207
						),
1208
					),
1209
				),
1210
				// }}}
1211
			),
1212
			array(
1213
				// {{{ #4 hasOne (finderQuery)
1214
				'Apple',
1215
				'NextApple',
1216
				// $meta
1217
				array(
1218
					'parentAlias' => 'Apple',
1219
					'parentKey' => 'id',
1220
					'targetKey' => 'apple_id',
1221
					'aliasPath' => 'Apple.NextApple',
1222
					'propertyPath' => 'Apple.NextApple',
1223
					'options' => array(),
1224
					'has' => true,
1225
					'belong' => false,
1226
					'many' => false,
1227
					'external' => true,
1228
				),
1229
				// $results
1230
				array(
1231
					array(
1232
						'Apple' => array(
1233
							'id' => '1',
1234
						),
1235
					),
1236
					array(
1237
						'Apple' => array(
1238
							'id' => '5',
1239
						),
1240
					),
1241
				),
1242
				// $fixtures
1243
				array('Apple'),
1244
				// $expectedArgument
1245
				array(
1246
					array(
1247
						'NextApple' => array(
1248
							'id' => '2',
1249
							'apple_id' => '1',
1250
							'color' => 'Bright Red 1',
1251
							'name' => 'Bright Red Apple',
1252
							'created' => '2006-11-22 10:43:13',
1253
							'modified' => '2006-11-30 18:38:10',
1254
						),
1255
						'EagerLoaderModel' => array(
1256
							'assoc_id' => '1',
1257
						),
1258
					),
1259
					array(
1260
						'NextApple' => array(
1261
							'id' => '6',
1262
							'apple_id' => 4,
1263
							'color' => 'My new appleOrange',
1264
							'name' => 'My new apple',
1265
							'created' => '2006-12-25 05:29:39',
1266
							'modified' => '2006-12-25 05:29:39',
1267
						),
1268
						'EagerLoaderModel' => array(
1269
							'assoc_id' => '5',
1270
						),
1271
					),
1272
				),
1273
				// $expectedResults
1274
				array(
1275
					array(
1276
						'Apple' => array(
1277
							'id' => '1',
1278
							'NextApple' => array(
1279
								'id' => '2',
1280
								'apple_id' => 1,
1281
								'color' => 'Bright Red 1',
1282
								'name' => 'Bright Red Apple',
1283
								'created' => '2006-11-22 10:43:13',
1284
								'modified' => '2006-11-30 18:38:10',
1285
							),
1286
						),
1287
					),
1288
					array(
1289
						'Apple' => array(
1290
							'id' => '5',
1291
							'NextApple' => array(
1292
								'id' => '6',
1293
								'apple_id' => 4,
1294
								'color' => 'My new appleOrange',
1295
								'name' => 'My new apple',
1296
								'created' => '2006-12-25 05:29:39',
1297
								'modified' => '2006-12-25 05:29:39',
1298
							),
1299
						),
1300
					),
1301
				),
1302
				// }}}
1303
			),
1304
			array(
1305
				// {{{ #5 belongsTo
1306
				'Article',
1307
				'User',
1308
				// $meta
1309
				array(
1310
					'parentAlias' => 'Article',
1311
					'parentKey' => 'user_id',
1312
					'targetKey' => 'id',
1313
					'aliasPath' => 'Article.User',
1314
					'propertyPath' => 'Article.User',
1315
					'options' => array(
1316
						'fields' => 'id',
1317
					),
1318
					'has' => false,
1319
					'belong' => true,
1320
					'many' => false,
1321
					'external' => false,
1322
				),
1323
				// $results
1324
				array(
1325
					array(
1326
						'Article' => array(
1327
							'id' => '1',
1328
							'user_id' => '1',
1329
						),
1330
						'User' => array(
1331
							'id' => '1',
1332
							'dummy' => '1',
1333
						),
1334
					),
1335
					array(
1336
						'Article' => array(
1337
							'id' => '3',
1338
							'user_id' => '1',
1339
						),
1340
						'User' => array(
1341
							'id' => '1',
1342
							'dummy' => '2',
1343
						),
1344
					)
1345
				),
1346
				// $fixtures
1347
				array('Article', 'User'),
1348
				// $expectedArgument
1349
				array(
1350
					array(
1351
						'User' => array(
1352
							'id' => '1',
1353
							'dummy' => '1',
1354
						),
1355
					),
1356
					array(
1357
						'User' => array(
1358
							'id' => '1',
1359
							'dummy' => '2',
1360
						),
1361
					),
1362
				),
1363
				// $expectedResults
1364
				array(
1365
					array(
1366
						'Article' => array(
1367
							'id' => '1',
1368
							'user_id' => '1',
1369
							'User' => array(
1370
								'id' => '1',
1371
								'dummy' => '1',
1372
							),
1373
						),
1374
					),
1375
					array(
1376
						'Article' => array(
1377
							'id' => '3',
1378
							'user_id' => '1',
1379
							'User' => array(
1380
								'id' => '1',
1381
								'dummy' => '2',
1382
							),
1383
						),
1384
					),
1385
				),
1386
				// }}}
1387
			),
1388
			array(
1389
				// {{{ #6 hasOne
1390
				'Comment',
1391
				'Attachment',
1392
				// $meta
1393
				array(
1394
					'parentAlias' => 'Comment',
1395
					'parentKey' => 'id',
1396
					'targetKey' => 'comment_id',
1397
					'aliasPath' => 'Comment.Attachment',
1398
					'propertyPath' => 'Comment.Attachment',
1399
					'options' => array(
1400
						'fields' => 'id',
1401
					),
1402
					'has' => true,
1403
					'belong' => false,
1404
					'many' => false,
1405
					'external' => false,
1406
				),
1407
				// $results
1408
				array(
1409
					array(
1410
						'Comment' => array(
1411
							'id' => '1',
1412
						),
1413
						'Attachment' => array(
1414
							'id' => null,
1415
							'comment_id' => null,
1416
						),
1417
					),
1418
					array(
1419
						'Comment' => array(
1420
							'id' => '5',
1421
						),
1422
						'Attachment' => array(
1423
							'id' => '1',
1424
							'comment_id' => '5',
1425
						),
1426
					)
1427
				),
1428
				// $fixtures
1429
				array('Comment', 'Attachment'),
1430
				// $expectedArgument
1431
				array(
1432
					array(
1433
						'Attachment' => array(),
1434
					),
1435
					array(
1436
						'Attachment' => array(
1437
							'id' => '1',
1438
							'comment_id' => '5',
1439
						),
1440
					),
1441
				),
1442
				// $expectedResults
1443
				array(
1444
					array(
1445
						'Comment' => array(
1446
							'id' => '1',
1447
							'Attachment' => array(),
1448
						),
1449
					),
1450
					array(
1451
						'Comment' => array(
1452
							'id' => '5',
1453
							'Attachment' => array(
1454
								'id' => '1',
1455
								'comment_id' => '5',
1456
							),
1457
						),
1458
					),
1459
				),
1460
				// }}}
1461
			),
1462
		);
1463
	}
1464
1465
/**
1466
 * Tests that no memory leak occurs
1467
 *
1468
 * @return void
1469
 */
1470
	public function testGarbageCollection() {
1471
		$this->loadFixtures('User', 'Article');
1472
		$User = ClassRegistry::init('User');
1473
1474
		for ($i = 0; $i < 1100; ++$i) {
1475
			EagerLoader::handleBeforeFind($User, array('contain' => 'Article'));
1476
		}
1477
1478
		$prop = new ReflectionProperty($this->EagerLoader, 'handlers');
1479
		$prop->setAccessible(true);
1480
		$handlers = $prop->getValue(null);
1481
		$this->assertEquals(1000, count($handlers));
1482
	}
1483
1484
/**
1485
 * Tests that an exception occurs if invalid ID specified
1486
 *
1487
 * @return void
1488
 *
1489
 * @expectedException UnexpectedValueException
1490
 * @expectedExceptionMessage EagerLoader "foo" is not found
1491
 */
1492
	public function testNotFound() {
1493
		$User = ClassRegistry::init('User');
1494
		EagerLoader::handleAfterFind($User, array(array('EagerLoaderModel' => array('id' => 'foo'))));
1495
	}
1496
}
1497