Passed
Pull Request — master (#268)
by
unknown
11:39
created

ActiveRecordTest::testIsPrimaryKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 15
nc 1
nop 0
dl 0
loc 20
rs 9.7666
c 1
b 0
f 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\ActiveRecord\Tests;
6
7
use DateTimeImmutable;
8
use ReflectionException;
9
use Yiisoft\ActiveRecord\ActiveQuery;
10
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Animal;
11
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Cat;
12
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer;
13
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\CustomerClosureField;
14
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\CustomerWithAlias;
15
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Dog;
16
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Item;
17
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\NoExist;
18
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\NullValues;
19
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Order;
20
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\OrderItem;
21
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\OrderItemWithNullFK;
22
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Type;
23
use Yiisoft\ActiveRecord\Tests\Support\Assert;
24
use Yiisoft\Db\Exception\Exception;
25
use Yiisoft\Db\Exception\InvalidArgumentException;
26
use Yiisoft\Db\Exception\InvalidCallException;
27
use Yiisoft\Db\Exception\InvalidConfigException;
28
use Yiisoft\Db\Query\Query;
29
30
abstract class ActiveRecordTest extends TestCase
31
{
32
    public function testStoreNull(): void
33
    {
34
        $this->checkFixture($this->db, 'null_values', true);
35
36
        $record = new NullValues($this->db);
37
38
        $this->assertNull($record->getAttribute('var1'));
39
        $this->assertNull($record->getAttribute('var2'));
40
        $this->assertNull($record->getAttribute('var3'));
41
        $this->assertNull($record->getAttribute('stringcol'));
42
43
        $record->setAttribute('var1', 123);
44
        $record->setAttribute('var2', 456);
45
        $record->setAttribute('var3', 789);
46
        $record->setAttribute('stringcol', 'hello!');
47
        $record->save();
48
49
        $this->assertTrue($record->refresh());
50
        $this->assertEquals(123, $record->getAttribute('var1'));
51
        $this->assertEquals(456, $record->getAttribute('var2'));
52
        $this->assertEquals(789, $record->getAttribute('var3'));
53
        $this->assertEquals('hello!', $record->getAttribute('stringcol'));
54
55
        $record->setAttribute('var1', null);
56
        $record->setAttribute('var2', null);
57
        $record->setAttribute('var3', null);
58
        $record->setAttribute('stringcol', null);
59
        $record->save();
60
61
        $this->assertTrue($record->refresh());
62
        $this->assertNull($record->getAttribute('var1'));
63
        $this->assertNull($record->getAttribute('var2'));
64
        $this->assertNull($record->getAttribute('var3'));
65
        $this->assertNull($record->getAttribute('>stringcol'));
66
67
        $record->setAttribute('var1', 0);
68
        $record->setAttribute('var2', 0);
69
        $record->setAttribute('var3', 0);
70
        $record->setAttribute('stringcol', '');
71
        $record->save();
72
73
        $this->assertTrue($record->refresh());
74
        $this->assertEquals(0, $record->getAttribute('var1'));
75
        $this->assertEquals(0, $record->getAttribute('var2'));
76
        $this->assertEquals(0, $record->getAttribute('var3'));
77
        $this->assertEquals('', $record->getAttribute('stringcol'));
78
    }
79
80
    public function testStoreEmpty(): void
81
    {
82
        $this->checkFixture($this->db, 'null_values');
83
84
        $record = new NullValues($this->db);
85
86
        /** this is to simulate empty html form submission */
87
        $record->var1 = '';
88
        $record->var2 = '';
89
        $record->var3 = '';
90
        $record->stringcol = '';
91
        $record->save();
92
93
        $this->assertTrue($record->refresh());
94
95
        /** {@see https://github.com/yiisoft/yii2/commit/34945b0b69011bc7cab684c7f7095d837892a0d4#commitcomment-4458225} */
96
        $this->assertSame($record->var1, $record->var2);
97
        $this->assertSame($record->var2, $record->var3);
98
    }
99
100
    public function testIsPrimaryKey(): void
101
    {
102
        $this->checkFixture($this->db, 'customer');
103
104
        $customer = new Customer($this->db);
105
        $orderItem = new OrderItem($this->db);
106
107
        $this->assertTrue($customer->isPrimaryKey(['id']));
108
        $this->assertFalse($customer->isPrimaryKey([]));
109
        $this->assertFalse($customer->isPrimaryKey(['id', 'name']));
110
        $this->assertFalse($customer->isPrimaryKey(['name']));
111
        $this->assertFalse($customer->isPrimaryKey(['name', 'email']));
112
113
        $this->assertTrue($orderItem->isPrimaryKey(['order_id', 'item_id']));
114
        $this->assertFalse($orderItem->isPrimaryKey([]));
115
        $this->assertFalse($orderItem->isPrimaryKey(['order_id']));
116
        $this->assertFalse($orderItem->isPrimaryKey(['item_id']));
117
        $this->assertFalse($orderItem->isPrimaryKey(['quantity']));
118
        $this->assertFalse($orderItem->isPrimaryKey(['quantity', 'subtotal']));
119
        $this->assertFalse($orderItem->isPrimaryKey(['order_id', 'item_id', 'quantity']));
120
    }
121
122
    public function testOutdatedRelationsAreResetForNewRecords(): void
123
    {
124
        $this->checkFixture($this->db, 'order_item');
125
126
        $orderItem = new OrderItem($this->db);
127
128
        $orderItem->order_id = 1;
129
        $orderItem->item_id = 3;
130
        $this->assertEquals(1, $orderItem->order->id);
131
        $this->assertEquals(3, $orderItem->item->id);
132
133
        /** test `__set()`. */
134
        $orderItem->order_id = 2;
135
        $orderItem->item_id = 1;
136
        $this->assertEquals(2, $orderItem->order->id);
137
        $this->assertEquals(1, $orderItem->item->id);
138
139
        /** test `setAttribute()`. */
140
        $orderItem->setAttribute('order_id', 2);
141
        $orderItem->setAttribute('item_id', 2);
142
        $this->assertEquals(2, $orderItem->order->id);
143
        $this->assertEquals(2, $orderItem->item->id);
144
    }
145
146
    public function testDefaultValues(): void
147
    {
148
        $this->checkFixture($this->db, 'type');
149
150
        $arClass = new Type($this->db);
151
152
        $arClass->loadDefaultValues();
153
154
        $this->assertEquals(1, $arClass->int_col2);
155
        $this->assertEquals('something', $arClass->char_col2);
156
        $this->assertEquals(1.23, $arClass->float_col2);
157
        $this->assertEquals(33.22, $arClass->numeric_col);
158
        $this->assertEquals(true, $arClass->bool_col2);
159
        $this->assertEquals(new DateTimeImmutable('2002-01-01 00:00:00'), $arClass->time);
160
161
        $arClass = new Type($this->db);
162
        $arClass->char_col2 = 'not something';
163
164
        $arClass->loadDefaultValues();
165
        $this->assertEquals('not something', $arClass->char_col2);
166
167
        $arClass = new Type($this->db);
168
        $arClass->char_col2 = 'not something';
169
170
        $arClass->loadDefaultValues(false);
171
        $this->assertEquals('something', $arClass->char_col2);
172
    }
173
174
    public function testCastValues(): void
175
    {
176
        $this->checkFixture($this->db, 'type');
177
178
        $arClass = new Type($this->db);
179
180
        $arClass->int_col = 123;
181
        $arClass->int_col2 = 456;
182
        $arClass->smallint_col = 42;
183
        $arClass->char_col = '1337';
184
        $arClass->char_col2 = 'test';
185
        $arClass->char_col3 = 'test123';
186
        $arClass->float_col = 3.742;
187
        $arClass->float_col2 = 42.1337;
188
        $arClass->bool_col = true;
189
        $arClass->bool_col2 = false;
190
191
        $arClass->save();
192
193
        /** @var $model Type */
194
        $aqClass = new ActiveQuery(Type::class, $this->db);
195
        $query = $aqClass->onePopulate();
196
197
        $this->assertSame(123, $query->int_col);
198
        $this->assertSame(456, $query->int_col2);
199
        $this->assertSame(42, $query->smallint_col);
200
        $this->assertSame('1337', trim($query->char_col));
201
        $this->assertSame('test', $query->char_col2);
202
        $this->assertSame('test123', $query->char_col3);
203
    }
204
205
    public function testPopulateRecordCallWhenQueryingOnParentClass(): void
206
    {
207
        $this->checkFixture($this->db, 'cat');
208
209
        $cat = new Cat($this->db);
210
        $cat->save();
211
212
        $dog = new Dog($this->db);
213
        $dog->save();
214
215
        $animal = new ActiveQuery(Animal::class, $this->db);
216
217
        $animals = $animal->where(['type' => Dog::class])->onePopulate();
218
        $this->assertEquals('bark', $animals->getDoes());
219
220
        $animals = $animal->where(['type' => Cat::class])->onePopulate();
221
        $this->assertEquals('meow', $animals->getDoes());
222
    }
223
224
    public function testSaveEmpty(): void
225
    {
226
        $this->checkFixture($this->db, 'null_values', true);
227
228
        $record = new NullValues($this->db);
229
230
        $this->assertTrue($record->save());
231
        $this->assertEquals(1, $record->id);
232
    }
233
234
    /**
235
     * Verify that {{}} are not going to be replaced in parameters.
236
     */
237
    public function testNoTablenameReplacement(): void
238
    {
239
        $this->checkFixture($this->db, 'customer');
240
241
        $customer = new Customer($this->db);
242
243
        $customer->name = 'Some {{weird}} name';
244
        $customer->email = '[email protected]';
245
        $customer->address = 'Some {{%weird}} address';
246
        $customer->insert();
247
        $customer->refresh();
248
249
        $this->assertEquals('Some {{weird}} name', $customer->name);
250
        $this->assertEquals('Some {{%weird}} address', $customer->address);
251
252
        $customer->name = 'Some {{updated}} name';
253
        $customer->address = 'Some {{%updated}} address';
254
        $customer->update();
255
256
        $this->assertEquals('Some {{updated}} name', $customer->name);
257
        $this->assertEquals('Some {{%updated}} address', $customer->address);
258
    }
259
260
    public static function legalValuesForFindByCondition(): array
261
    {
262
        return [
263
            [Customer::class, ['id' => 1]],
264
            [Customer::class, ['customer.id' => 1]],
265
            [Customer::class, ['[[id]]' => 1]],
266
            [Customer::class, ['{{customer}}.[[id]]' => 1]],
267
            [Customer::class, ['{{%customer}}.[[id]]' => 1]],
268
            [CustomerWithAlias::class, ['id' => 1]],
269
            [CustomerWithAlias::class, ['customer.id' => 1]],
270
            [CustomerWithAlias::class, ['[[id]]' => 1]],
271
            [CustomerWithAlias::class, ['{{customer}}.[[id]]' => 1]],
272
            [CustomerWithAlias::class, ['{{%customer}}.[[id]]' => 1]],
273
            [CustomerWithAlias::class, ['csr.id' => 1], 'csr'],
274
            [CustomerWithAlias::class, ['{{csr}}.[[id]]' => 1], 'csr'],
275
        ];
276
    }
277
278
    /**
279
     * @dataProvider legalValuesForFindByCondition
280
     *
281
     * @throws ReflectionException
282
     */
283
    public function testLegalValuesForFindByCondition(
284
        string $modelClassName,
285
        array $validFilter,
286
        ?string $alias = null
287
    ): void {
288
        $this->checkFixture($this->db, 'customer');
289
290
        $activeQuery = new ActiveQuery($modelClassName, $this->db);
291
292
        if ($alias !== null) {
293
            $activeQuery->alias('csr');
294
        }
295
296
        /** @var Query $query */
297
        $query = Assert::invokeMethod($activeQuery, 'findByCondition', [$validFilter]);
298
299
300
        $this->db->getQueryBuilder()->build($query);
301
302
        $this->assertTrue(true);
303
    }
304
305
    public static function illegalValuesForFindByCondition(): array
306
    {
307
        return [
308
            [Customer::class, [['`id`=`id` and 1' => 1]]],
309
            [Customer::class, [[
310
                'legal' => 1,
311
                '`id`=`id` and 1' => 1,
312
            ]]],
313
            [Customer::class, [[
314
                'nested_illegal' => [
315
                    'false or 1=' => 1,
316
                ],
317
            ]]],
318
            [Customer::class, [['true--' => 1]]],
319
320
            [CustomerWithAlias::class, [['`csr`.`id`=`csr`.`id` and 1' => 1]]],
321
            [CustomerWithAlias::class, [[
322
                'legal' => 1,
323
                '`csr`.`id`=`csr`.`id` and 1' => 1,
324
            ]]],
325
            [CustomerWithAlias::class, [[
326
                'nested_illegal' => [
327
                    'false or 1=' => 1,
328
                ],
329
            ]]],
330
            [CustomerWithAlias::class, [['true--' => 1]]],
331
        ];
332
    }
333
334
    /**
335
     * @dataProvider illegalValuesForFindByCondition
336
     *
337
     * @throws ReflectionException
338
     */
339
    public function testValueEscapingInFindByCondition(string $modelClassName, array $filterWithInjection): void
340
    {
341
        $this->checkFixture($this->db, 'customer');
342
        $this->expectException(InvalidArgumentException::class);
343
        $this->expectExceptionMessageMatches(
344
            '/^Key "(.+)?" is not a column name and can not be used as a filter$/'
345
        );
346
347
        $query = new ActiveQuery($modelClassName, $this->db);
348
349
        /** @var Query $query */
350
        $query = Assert::invokeMethod($query, 'findByCondition', $filterWithInjection);
351
352
        $this->db->getQueryBuilder()->build($query);
353
    }
354
355
    public function testRefreshQuerySetAliasFindRecord(): void
356
    {
357
        $this->checkFixture($this->db, 'customer');
358
359
        $customer = new CustomerWithAlias($this->db);
360
361
        $customer->id = 1;
362
        $customer->refresh();
363
364
        $this->assertEquals(1, $customer->id);
365
    }
366
367
    public function testResetNotSavedRelation(): void
368
    {
369
        $this->checkFixture($this->db, 'order');
370
371
        $order = new Order($this->db);
372
373
        $order->customer_id = 1;
374
        $order->created_at = 1_325_502_201;
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING on line 374 at column 30
Loading history...
375
        $order->total = 0;
376
377
        $orderItem = new OrderItem($this->db);
378
379
        $order->orderItems;
380
381
        $order->populateRelation('orderItems', [$orderItem]);
382
383
        $order->save();
384
385
        $this->assertCount(1, $order->orderItems);
386
    }
387
388
    public function testIssetException(): void
389
    {
390
        $this->checkFixture($this->db, 'cat');
391
392
        $cat = new Cat($this->db);
393
394
        $this->assertFalse(isset($cat->exception));
395
    }
396
397
    public function testIssetThrowable(): void
398
    {
399
        $this->checkFixture($this->db, 'cat');
400
401
        $cat = new Cat($this->db);
402
403
        $this->assertFalse(isset($cat->throwable));
404
    }
405
406
    public function testSetAttributes(): void
407
    {
408
        $attributes = [];
409
        $this->checkFixture($this->db, 'customer');
410
411
        $attributes['email'] = '[email protected]';
412
        $attributes['name'] = 'samdark';
413
        $attributes['address'] = 'rusia';
414
        $attributes['status'] = 1;
415
416
        if ($this->db->getDriverName() === 'pgsql') {
417
            $attributes['bool_status'] = true;
418
        }
419
420
        $attributes['profile_id'] = null;
421
422
        $customer = new Customer($this->db);
423
424
        $customer->setAttributes($attributes);
425
426
        $this->assertTrue($customer->save());
427
    }
428
429
    public function testSetAttributeNoExist(): void
430
    {
431
        $this->checkFixture($this->db, 'cat');
432
433
        $cat = new Cat($this->db);
434
435
        $this->expectException(InvalidArgumentException::class);
436
        $this->expectExceptionMessage(
437
            'Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Cat has no attribute named "noExist"'
438
        );
439
440
        $cat->setAttribute('noExist', 1);
441
    }
442
443
    public function testSetOldAttribute(): void
444
    {
445
        $this->checkFixture($this->db, 'customer');
446
447
        $customer = new Customer($this->db);
448
449
        $this->assertEmpty($customer->getOldAttribute('name'));
450
451
        $customer->setOldAttribute('name', 'samdark');
452
453
        $this->assertEquals('samdark', $customer->getOldAttribute('name'));
454
    }
455
456
    public function testSetOldAttributeException(): void
457
    {
458
        $this->checkFixture($this->db, 'customer');
459
460
        $customer = new Customer($this->db);
461
462
        $this->assertEmpty($customer->getOldAttribute('name'));
463
464
        $this->expectException(InvalidArgumentException::class);
465
        $this->expectExceptionMessage(
466
            'Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer has no attribute named "noExist"'
467
        );
468
        $customer->setOldAttribute('noExist', 'samdark');
469
    }
470
471
    public function testIsAttributeChangedNotChanged(): void
472
    {
473
        $this->checkFixture($this->db, 'customer');
474
475
        $customer = new Customer($this->db);
476
477
        $this->assertEmpty($customer->getAttribute('name'));
478
        $this->assertEmpty($customer->getOldAttribute('name'));
479
        $this->assertFalse($customer->isAttributeChanged('name', false));
480
    }
481
482
    public function testTableSchemaException(): void
483
    {
484
        $noExist = new NoExist($this->db);
485
486
        $this->expectException(InvalidConfigException::class);
487
        $this->expectExceptionMessage('The table does not exist: NoExist');
488
        $noExist->getTableSchema();
489
    }
490
491
    public function testInsert(): void
492
    {
493
        $this->checkFixture($this->db, 'customer');
494
495
        $customer = new Customer($this->db);
496
497
        $customer->email = '[email protected]';
498
        $customer->name = 'user4';
499
        $customer->address = 'address4';
500
501
        $this->assertNull($customer->id);
502
        $this->assertTrue($customer->isNewRecord);
503
504
        $customer->save();
505
506
        $this->assertNotNull($customer->id);
507
        $this->assertFalse($customer->isNewRecord);
508
    }
509
510
    /**
511
     * Some PDO implementations (e.g. cubrid) do not support boolean values.
512
     *
513
     * Make sure this does not affect AR layer.
514
     */
515
    public function testBooleanAttribute(): void
516
    {
517
        $this->checkFixture($this->db, 'customer', true);
518
519
        $customer = new Customer($this->db);
520
521
        $customer->name = 'boolean customer';
522
        $customer->email = '[email protected]';
523
        $customer->status = true;
524
525
        $customer->save();
526
        $customer->refresh();
527
        $this->assertEquals(1, $customer->status);
528
529
        $customer->status = false;
530
        $customer->save();
531
532
        $customer->refresh();
533
        $this->assertEquals(0, $customer->status);
534
535
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
536
        $customers = $customerQuery->where(['status' => true])->all();
537
        $this->assertCount(2, $customers);
538
539
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
540
        $customers = $customerQuery->where(['status' => false])->all();
541
        $this->assertCount(1, $customers);
542
    }
543
544
    public function testAttributeAccess(): void
545
    {
546
        $this->checkFixture($this->db, 'customer');
547
548
        $arClass = new Customer($this->db);
549
550
        $this->assertTrue($arClass->canSetProperty('name'));
551
        $this->assertTrue($arClass->canGetProperty('name'));
552
        $this->assertFalse($arClass->canSetProperty('unExistingColumn'));
553
        $this->assertFalse(isset($arClass->name));
554
555
        $arClass->name = 'foo';
556
        $this->assertTrue(isset($arClass->name));
557
558
        unset($arClass->name);
559
        $this->assertNull($arClass->name);
560
561
        /** {@see https://github.com/yiisoft/yii2-gii/issues/190} */
562
        $baseModel = new Customer($this->db);
563
        $this->assertFalse($baseModel->hasProperty('unExistingColumn'));
564
565
        $customer = new Customer($this->db);
566
        $this->assertInstanceOf(Customer::class, $customer);
567
        $this->assertTrue($customer->canGetProperty('id'));
568
        $this->assertTrue($customer->canSetProperty('id'));
569
570
        /** tests that we really can get and set this property */
571
        $this->assertNull($customer->id);
572
        $customer->id = 10;
573
        $this->assertNotNull($customer->id);
574
575
        /** Let's test relations */
576
        $this->assertTrue($customer->canGetProperty('orderItems'));
577
        $this->assertFalse($customer->canSetProperty('orderItems'));
578
579
        /** Newly created model must have empty relation */
580
        $this->assertSame([], $customer->orderItems);
581
582
        /** does it still work after accessing the relation? */
583
        $this->assertTrue($customer->canGetProperty('orderItems'));
584
        $this->assertFalse($customer->canSetProperty('orderItems'));
585
586
        try {
587
            /** @var $itemClass ActiveRecordInterface */
588
            $customer->orderItems = [new Item($this->db)];
589
            $this->fail('setter call above MUST throw Exception');
590
        } catch (Exception $e) {
591
            /** catch exception "Setting read-only property" */
592
            $this->assertInstanceOf(InvalidCallException::class, $e);
593
        }
594
595
        /** related attribute $customer->orderItems didn't change cause it's read-only */
596
        $this->assertSame([], $customer->orderItems);
597
        $this->assertFalse($customer->canGetProperty('non_existing_property'));
598
        $this->assertFalse($customer->canSetProperty('non_existing_property'));
599
    }
600
601
    public function testHasAttribute(): void
602
    {
603
        $this->checkFixture($this->db, 'customer');
604
605
        $customer = new Customer($this->db);
606
607
        $this->assertTrue($customer->hasAttribute('id'));
608
        $this->assertTrue($customer->hasAttribute('email'));
609
        $this->assertFalse($customer->hasAttribute(0));
610
        $this->assertFalse($customer->hasAttribute(null));
611
        $this->assertFalse($customer->hasAttribute(42));
612
613
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
614
        $customer = $customerQuery->findOne(1);
615
        $this->assertTrue($customer->hasAttribute('id'));
616
        $this->assertTrue($customer->hasAttribute('email'));
617
        $this->assertFalse($customer->hasAttribute(0));
618
        $this->assertFalse($customer->hasAttribute(null));
619
        $this->assertFalse($customer->hasAttribute(42));
620
    }
621
622
    public function testRefresh(): void
623
    {
624
        $this->checkFixture($this->db, 'customer');
625
626
        $customer = new Customer($this->db);
627
628
        $this->assertFalse($customer->refresh());
629
630
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
631
        $customer = $customerQuery->findOne(1);
632
        $customer->name = 'to be refreshed';
633
634
        $this->assertTrue($customer->refresh());
635
        $this->assertEquals('user1', $customer->name);
636
    }
637
638
    public function testEquals(): void
639
    {
640
        $this->checkFixture($this->db, 'customer');
641
642
        $customerA = new Customer($this->db);
643
        $customerB = new Customer($this->db);
644
        $this->assertFalse($customerA->equals($customerB));
645
646
        $customerA = new Customer($this->db);
647
        $customerB = new Item($this->db);
648
        $this->assertFalse($customerA->equals($customerB));
649
    }
650
651
    public static function providerForUnlinkDelete()
652
    {
653
        return [
654
            'with delete' => [true, 0],
655
            'without delete' => [false, 1],
656
        ];
657
    }
658
659
    /**
660
     * @dataProvider providerForUnlinkDelete
661
     *
662
     * @see https://github.com/yiisoft/yii2/issues/17174
663
     */
664
    public function testUnlinkWithViaOnCondition($delete, $count)
665
    {
666
        $this->checkFixture($this->db, 'order', true);
667
        $this->checkFixture($this->db, 'order_item_with_null_fk', true);
668
669
        $orderQuery = new ActiveQuery(Order::class, $this->db);
670
        $order = $orderQuery->findOne(2);
671
672
        $this->assertCount(1, $order->itemsFor8);
673
        $order->unlink('itemsFor8', $order->itemsFor8[0], $delete);
674
675
        $order = $orderQuery->findOne(2);
676
        $this->assertCount(0, $order->itemsFor8);
677
        $this->assertCount(2, $order->orderItemsWithNullFK);
678
679
        $orderItemQuery = new ActiveQuery(OrderItemWithNullFK::class, $this->db);
680
        $this->assertCount(1, $orderItemQuery->findAll([
681
            'order_id' => 2,
682
            'item_id' => 5,
683
        ]));
684
        $this->assertCount($count, $orderItemQuery->findAll([
685
            'order_id' => null,
686
            'item_id' => null,
687
        ]));
688
    }
689
690
    public function testVirtualRelation()
691
    {
692
        $this->checkFixture($this->db, 'order', true);
693
694
        $orderQuery = new ActiveQuery(Order::class, $this->db);
695
        /** @var Order $order */
696
        $order = $orderQuery->findOne(2);
697
698
        $order->setVirtualCustomerId($order->customer_id);
699
        $this->assertNotNull($order->getVirtualCustomer());
700
    }
701
702
    /**
703
     * Test joinWith eager loads via relation
704
     *
705
     * @see https://github.com/yiisoft/yii2/issues/19507
706
     */
707
    public function testJoinWithEager()
708
    {
709
        $this->checkFixture($this->db, 'customer', true);
710
711
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
712
        $eagerCustomers = $customerQuery->joinWith(['items2'])->all();
713
        $eagerItemsCount = 0;
714
        foreach ($eagerCustomers as $customer) {
715
            $eagerItemsCount += is_countable($customer->items2) ? count($customer->items2) : 0;
716
        }
717
718
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
719
        $lazyCustomers = $customerQuery->all();
720
        $lazyItemsCount = 0;
721
        foreach ($lazyCustomers as $customer) {
722
            $lazyItemsCount += is_countable($customer->items2) ? count($customer->items2) : 0;
723
        }
724
725
        $this->assertEquals($eagerItemsCount, $lazyItemsCount);
726
    }
727
728
    public function testToArray(): void
729
    {
730
        $this->checkFixture($this->db, 'customer', true);
731
732
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
733
        $customer = $customerQuery->findOne(1);
734
735
        $this->assertSame(
736
            [
737
                'id' => 1,
738
                'email' => '[email protected]',
739
                'name' => 'user1',
740
                'address' => 'address1',
741
                'status' => 1,
742
                'profile_id' => 1,
743
            ],
744
            $customer->toArray(),
745
        );
746
    }
747
748
    public function testToArrayWithClosure(): void
749
    {
750
        $this->checkFixture($this->db, 'customer', true);
751
752
        $customerQuery = new ActiveQuery(CustomerClosureField::class, $this->db);
753
        $customer = $customerQuery->findOne(1);
754
755
        $this->assertSame(
756
            [
757
                'id' => 1,
758
                'email' => '[email protected]',
759
                'name' => 'user1',
760
                'address' => 'address1',
761
                'status' => 'active',
762
                'profile_id' => 1,
763
            ],
764
            $customer->toArray(),
765
        );
766
    }
767
}
768