Passed
Pull Request — master (#224)
by Def
02:42
created

ActiveRecordTest::testVirtualRelation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 0
dl 0
loc 10
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\ActiveRecord\Tests;
6
7
use ReflectionException;
8
use Yiisoft\ActiveRecord\ActiveQuery;
9
use Yiisoft\ActiveRecord\ActiveRecordInterface;
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\CustomerWithAlias;
14
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Dog;
15
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Item;
16
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\NoExist;
17
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\NullValues;
18
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Order;
19
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\OrderItem;
20
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\OrderItemWithNullFK;
21
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Type;
22
use Yiisoft\Db\Exception\Exception;
23
use Yiisoft\Db\Exception\InvalidArgumentException;
24
use Yiisoft\Db\Exception\InvalidCallException;
25
use Yiisoft\Db\Exception\InvalidConfigException;
26
use Yiisoft\Db\Query\Query;
27
28
abstract class ActiveRecordTest extends TestCase
29
{
30
    public function testStoreNull(): void
31
    {
32
        $this->checkFixture($this->db, 'null_values', true);
33
34
        $record = new NullValues($this->db);
35
36
        $this->assertNull($record->getAttribute('var1'));
37
        $this->assertNull($record->getAttribute('var2'));
38
        $this->assertNull($record->getAttribute('var3'));
39
        $this->assertNull($record->getAttribute('stringcol'));
40
41
        $record->setAttribute('var1', 123);
42
        $record->setAttribute('var2', 456);
43
        $record->setAttribute('var3', 789);
44
        $record->setAttribute('stringcol', 'hello!');
45
        $record->save();
46
47
        $this->assertTrue($record->refresh());
48
        $this->assertEquals(123, $record->getAttribute('var1'));
49
        $this->assertEquals(456, $record->getAttribute('var2'));
50
        $this->assertEquals(789, $record->getAttribute('var3'));
51
        $this->assertEquals('hello!', $record->getAttribute('stringcol'));
52
53
        $record->setAttribute('var1', null);
54
        $record->setAttribute('var2', null);
55
        $record->setAttribute('var3', null);
56
        $record->setAttribute('stringcol', null);
57
        $record->save();
58
59
        $this->assertTrue($record->refresh());
60
        $this->assertNull($record->getAttribute('var1'));
61
        $this->assertNull($record->getAttribute('var2'));
62
        $this->assertNull($record->getAttribute('var3'));
63
        $this->assertNull($record->getAttribute('>stringcol'));
64
65
        $record->setAttribute('var1', 0);
66
        $record->setAttribute('var2', 0);
67
        $record->setAttribute('var3', 0);
68
        $record->setAttribute('stringcol', '');
69
        $record->save();
70
71
        $this->assertTrue($record->refresh());
72
        $this->assertEquals(0, $record->getAttribute('var1'));
73
        $this->assertEquals(0, $record->getAttribute('var2'));
74
        $this->assertEquals(0, $record->getAttribute('var3'));
75
        $this->assertEquals('', $record->getAttribute('stringcol'));
76
    }
77
78
    public function testStoreEmpty(): void
79
    {
80
        $this->checkFixture($this->db, 'null_values');
81
82
        $record = new NullValues($this->db);
83
84
        /** this is to simulate empty html form submission */
85
        $record->var1 = '';
0 ignored issues
show
Documentation Bug introduced by
The property $var1 was declared of type integer, but '' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
86
        $record->var2 = '';
0 ignored issues
show
Documentation Bug introduced by
The property $var2 was declared of type integer, but '' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
87
        $record->var3 = '';
0 ignored issues
show
Documentation Bug introduced by
The property $var3 was declared of type integer, but '' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
88
        $record->stringcol = '';
89
        $record->save();
90
91
        $this->assertTrue($record->refresh());
92
93
        /** {@see https://github.com/yiisoft/yii2/commit/34945b0b69011bc7cab684c7f7095d837892a0d4#commitcomment-4458225} */
94
        $this->assertSame($record->var1, $record->var2);
95
        $this->assertSame($record->var2, $record->var3);
96
    }
97
98
    public function testIsPrimaryKey(): void
99
    {
100
        $this->checkFixture($this->db, 'customer');
101
102
        $customer = new Customer($this->db);
103
        $orderItem = new OrderItem($this->db);
104
105
        $this->assertTrue($customer->isPrimaryKey(['id']));
106
        $this->assertFalse($customer->isPrimaryKey([]));
107
        $this->assertFalse($customer->isPrimaryKey(['id', 'name']));
108
        $this->assertFalse($customer->isPrimaryKey(['name']));
109
        $this->assertFalse($customer->isPrimaryKey(['name', 'email']));
110
111
        $this->assertTrue($orderItem->isPrimaryKey(['order_id', 'item_id']));
112
        $this->assertFalse($orderItem->isPrimaryKey([]));
113
        $this->assertFalse($orderItem->isPrimaryKey(['order_id']));
114
        $this->assertFalse($orderItem->isPrimaryKey(['item_id']));
115
        $this->assertFalse($orderItem->isPrimaryKey(['quantity']));
116
        $this->assertFalse($orderItem->isPrimaryKey(['quantity', 'subtotal']));
117
        $this->assertFalse($orderItem->isPrimaryKey(['order_id', 'item_id', 'quantity']));
118
    }
119
120
    public function testOutdatedRelationsAreResetForNewRecords(): void
121
    {
122
        $this->checkFixture($this->db, 'order_item');
123
124
        $orderItem = new OrderItem($this->db);
125
126
        $orderItem->order_id = 1;
127
        $orderItem->item_id = 3;
128
        $this->assertEquals(1, $orderItem->order->id);
0 ignored issues
show
Bug Best Practice introduced by
The property order does not exist on Yiisoft\ActiveRecord\Tes...\ActiveRecord\OrderItem. Since you implemented __get, consider adding a @property annotation.
Loading history...
129
        $this->assertEquals(3, $orderItem->item->id);
0 ignored issues
show
Bug Best Practice introduced by
The property item does not exist on Yiisoft\ActiveRecord\Tes...\ActiveRecord\OrderItem. Since you implemented __get, consider adding a @property annotation.
Loading history...
130
131
        /** test `__set()`. */
132
        $orderItem->order_id = 2;
133
        $orderItem->item_id = 1;
134
        $this->assertEquals(2, $orderItem->order->id);
135
        $this->assertEquals(1, $orderItem->item->id);
136
137
        /** test `setAttribute()`. */
138
        $orderItem->setAttribute('order_id', 2);
139
        $orderItem->setAttribute('item_id', 2);
140
        $this->assertEquals(2, $orderItem->order->id);
141
        $this->assertEquals(2, $orderItem->item->id);
142
    }
143
144
    public function testDefaultValues(): void
145
    {
146
        $this->checkFixture($this->db, 'type');
147
148
        $arClass = new Type($this->db);
149
150
        $arClass->loadDefaultValues();
151
152
        $this->assertEquals(1, $arClass->int_col2);
153
        $this->assertEquals('something', $arClass->char_col2);
154
        $this->assertEquals(1.23, $arClass->float_col2);
155
        $this->assertEquals(33.22, $arClass->numeric_col);
156
        $this->assertEquals(true, $arClass->bool_col2);
157
        $this->assertEquals('2002-01-01 00:00:00', $arClass->time);
158
159
        $arClass = new Type($this->db);
160
        $arClass->char_col2 = 'not something';
161
162
        $arClass->loadDefaultValues();
163
        $this->assertEquals('not something', $arClass->char_col2);
164
165
        $arClass = new Type($this->db);
166
        $arClass->char_col2 = 'not something';
167
168
        $arClass->loadDefaultValues(false);
169
        $this->assertEquals('something', $arClass->char_col2);
170
    }
171
172
    public function testCastValues(): void
173
    {
174
        $this->checkFixture($this->db, 'type');
175
176
        $arClass = new Type($this->db);
177
178
        $arClass->int_col = 123;
179
        $arClass->int_col2 = 456;
180
        $arClass->smallint_col = 42;
181
        $arClass->char_col = '1337';
182
        $arClass->char_col2 = 'test';
183
        $arClass->char_col3 = 'test123';
184
        $arClass->float_col = 3.742;
185
        $arClass->float_col2 = 42.1337;
186
        $arClass->bool_col = true;
187
        $arClass->bool_col2 = false;
188
189
        $arClass->save();
190
191
        /** @var $model Type */
192
        $aqClass = new ActiveQuery(Type::class, $this->db);
193
        $query = $aqClass->one();
194
195
        $this->assertSame(123, $query->int_col);
196
        $this->assertSame(456, $query->int_col2);
197
        $this->assertSame(42, $query->smallint_col);
198
        $this->assertSame('1337', trim($query->char_col));
199
        $this->assertSame('test', $query->char_col2);
200
        $this->assertSame('test123', $query->char_col3);
201
    }
202
203
    public function testPopulateRecordCallWhenQueryingOnParentClass(): void
204
    {
205
        $this->checkFixture($this->db, 'cat');
206
207
        $cat = new Cat($this->db);
208
        $cat->save();
209
210
        $dog = new Dog($this->db);
211
        $dog->save();
212
213
        $animal = new ActiveQuery(Animal::class, $this->db);
214
215
        $animals = $animal->where(['type' => Dog::class])->one();
216
        $this->assertEquals('bark', $animals->getDoes());
217
218
        $animals = $animal->where(['type' => Cat::class])->one();
219
        $this->assertEquals('meow', $animals->getDoes());
220
    }
221
222
    public function testSaveEmpty(): void
223
    {
224
        $this->checkFixture($this->db, 'null_values');
225
226
        $this->loadFixture($this->db);
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 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 = $this->invokeMethod($activeQuery, 'findByCondition', [$validFilter]);
298
299
300
        $this->db->getQueryBuilder()->build($query);
301
302
        $this->assertTrue(true);
303
    }
304
305
    public 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 = $this->invokeMethod(
351
            $query,
352
            'findByCondition',
353
            $filterWithInjection
354
        );
355
356
        $this->db->getQueryBuilder()->build($query);
357
    }
358
359
    public function testRefreshQuerySetAliasFindRecord(): void
360
    {
361
        $this->checkFixture($this->db, 'customer');
362
363
        $customer = new CustomerWithAlias($this->db);
364
365
        $customer->id = 1;
366
        $customer->refresh();
367
368
        $this->assertEquals(1, $customer->id);
369
    }
370
371
    public function testResetNotSavedRelation(): void
372
    {
373
        $this->checkFixture($this->db, 'order');
374
375
        $order = new Order($this->db);
376
377
        $order->customer_id = 1;
378
        $order->created_at = 1_325_502_201;
0 ignored issues
show
Bug introduced by
The constant Yiisoft\ActiveRecord\Tests\1_325_502_201 was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
379
        $order->total = 0;
380
381
        $orderItem = new OrderItem($this->db);
382
383
        $order->orderItems;
0 ignored issues
show
Bug Best Practice introduced by
The property orderItems does not exist on Yiisoft\ActiveRecord\Tes...tubs\ActiveRecord\Order. Since you implemented __get, consider adding a @property annotation.
Loading history...
384
385
        $order->populateRelation('orderItems', [$orderItem]);
386
387
        $order->save();
388
389
        $this->assertCount(1, $order->orderItems);
390
    }
391
392
    public function testIssetException(): void
393
    {
394
        $this->checkFixture($this->db, 'cat');
395
396
        $cat = new Cat($this->db);
397
398
        $this->assertFalse(isset($cat->exception));
0 ignored issues
show
Bug Best Practice introduced by
The property exception does not exist on Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Cat. Since you implemented __get, consider adding a @property annotation.
Loading history...
399
    }
400
401
    public function testIssetThrowable(): void
402
    {
403
        $this->checkFixture($this->db, 'cat');
404
405
        $cat = new Cat($this->db);
406
407
        $this->assertFalse(isset($cat->throwable));
0 ignored issues
show
Bug Best Practice introduced by
The property throwable does not exist on Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Cat. Since you implemented __get, consider adding a @property annotation.
Loading history...
408
    }
409
410
    public function testSetAttributes(): void
411
    {
412
        $attributes = [];
413
        $this->checkFixture($this->db, 'customer');
414
415
        $attributes['email'] = '[email protected]';
416
        $attributes['name'] = 'samdark';
417
        $attributes['address'] = 'rusia';
418
        $attributes['status'] = 1;
419
420
        if ($this->driverName === 'pgsql') {
421
            $attributes['bool_status'] = true;
422
        }
423
424
        $attributes['profile_id'] = null;
425
426
        $customer = new Customer($this->db);
427
428
        $customer->setAttributes($attributes);
429
430
        $this->assertTrue($customer->save());
431
    }
432
433
    public function testSetAttributeNoExist(): void
434
    {
435
        $this->checkFixture($this->db, 'cat');
436
437
        $cat = new Cat($this->db);
438
439
        $this->expectException(InvalidArgumentException::class);
440
        $this->expectExceptionMessage(
441
            'Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Cat has no attribute named "noExist"'
442
        );
443
444
        $cat->setAttribute('noExist', 1);
445
    }
446
447
    public function testSetOldAttribute(): void
448
    {
449
        $this->checkFixture($this->db, 'customer');
450
451
        $customer = new Customer($this->db);
452
453
        $this->assertEmpty($customer->getOldAttribute('name'));
454
455
        $customer->setOldAttribute('name', 'samdark');
456
457
        $this->assertEquals('samdark', $customer->getOldAttribute('name'));
458
    }
459
460
    public function testSetOldAttributeException(): void
461
    {
462
        $this->checkFixture($this->db, 'customer');
463
464
        $customer = new Customer($this->db);
465
466
        $this->assertEmpty($customer->getOldAttribute('name'));
467
468
        $this->expectException(InvalidArgumentException::class);
469
        $this->expectExceptionMessage(
470
            'Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer has no attribute named "noExist"'
471
        );
472
        $customer->setOldAttribute('noExist', 'samdark');
473
    }
474
475
    public function testIsAttributeChangedNotChanged(): void
476
    {
477
        $this->checkFixture($this->db, 'customer');
478
479
        $customer = new Customer($this->db);
480
481
        $this->assertEmpty($customer->getAttribute('name'));
482
        $this->assertEmpty($customer->getOldAttribute('name'));
483
        $this->assertFalse($customer->isAttributeChanged('name', false));
484
    }
485
486
    public function testTableSchemaException(): void
487
    {
488
        $noExist = new NoExist($this->db);
489
490
        $this->expectException(InvalidConfigException::class);
491
        $this->expectExceptionMessage('The table does not exist: NoExist');
492
        $noExist->getTableSchema();
493
    }
494
495
    public function testInsert(): void
496
    {
497
        $this->checkFixture($this->db, 'customer');
498
499
        $customer = new Customer($this->db);
500
501
        $customer->email = '[email protected]';
502
        $customer->name = 'user4';
503
        $customer->address = 'address4';
504
505
        $this->assertNull($customer->id);
506
        $this->assertTrue($customer->isNewRecord);
507
508
        $customer->save();
509
510
        $this->assertNotNull($customer->id);
511
        $this->assertFalse($customer->isNewRecord);
512
    }
513
514
    /**
515
     * Some PDO implementations (e.g. cubrid) do not support boolean values.
516
     *
517
     * Make sure this does not affect AR layer.
518
     */
519
    public function testBooleanAttribute(): void
520
    {
521
        $this->checkFixture($this->db, 'customer');
522
523
        $this->loadFixture($this->db);
524
525
        $customer = new Customer($this->db);
526
527
        $customer->name = 'boolean customer';
528
        $customer->email = '[email protected]';
529
        $customer->status = true;
0 ignored issues
show
Documentation Bug introduced by
The property $status was declared of type integer, but true is of type true. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
530
531
        $customer->save();
532
        $customer->refresh();
533
        $this->assertEquals(1, $customer->status);
534
535
        $customer->status = false;
536
        $customer->save();
537
538
        $customer->refresh();
539
        $this->assertEquals(0, $customer->status);
540
541
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
542
        $customers = $customerQuery->where(['status' => true])->all();
543
        $this->assertCount(2, $customers);
544
545
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
546
        $customers = $customerQuery->where(['status' => false])->all();
547
        $this->assertCount(1, $customers);
548
    }
549
550
    public function testAttributeAccess(): void
551
    {
552
        $this->checkFixture($this->db, 'customer');
553
554
        $arClass = new Customer($this->db);
555
556
        $this->assertTrue($arClass->canSetProperty('name'));
557
        $this->assertTrue($arClass->canGetProperty('name'));
558
        $this->assertFalse($arClass->canSetProperty('unExistingColumn'));
559
        $this->assertFalse(isset($arClass->name));
560
561
        $arClass->name = 'foo';
562
        $this->assertTrue(isset($arClass->name));
563
564
        unset($arClass->name);
565
        $this->assertNull($arClass->name);
566
567
        /** {@see https://github.com/yiisoft/yii2-gii/issues/190} */
568
        $baseModel = new Customer($this->db);
569
        $this->assertFalse($baseModel->hasProperty('unExistingColumn'));
570
571
        $customer = new Customer($this->db);
572
        $this->assertInstanceOf(Customer::class, $customer);
573
        $this->assertTrue($customer->canGetProperty('id'));
574
        $this->assertTrue($customer->canSetProperty('id'));
575
576
        /** tests that we really can get and set this property */
577
        $this->assertNull($customer->id);
578
        $customer->id = 10;
579
        $this->assertNotNull($customer->id);
580
581
        /** Let's test relations */
582
        $this->assertTrue($customer->canGetProperty('orderItems'));
583
        $this->assertFalse($customer->canSetProperty('orderItems'));
584
585
        /** Newly created model must have empty relation */
586
        $this->assertSame([], $customer->orderItems);
0 ignored issues
show
Bug Best Practice introduced by
The property orderItems does not exist on Yiisoft\ActiveRecord\Tes...s\ActiveRecord\Customer. Since you implemented __get, consider adding a @property annotation.
Loading history...
587
588
        /** does it still work after accessing the relation? */
589
        $this->assertTrue($customer->canGetProperty('orderItems'));
590
        $this->assertFalse($customer->canSetProperty('orderItems'));
591
592
        try {
593
            /** @var $itemClass ActiveRecordInterface */
594
            $customer->orderItems = [new Item($this->db)];
0 ignored issues
show
Bug Best Practice introduced by
The property orderItems does not exist on Yiisoft\ActiveRecord\Tes...s\ActiveRecord\Customer. Since you implemented __set, consider adding a @property annotation.
Loading history...
595
            $this->fail('setter call above MUST throw Exception');
596
        } catch (Exception $e) {
597
            /** catch exception "Setting read-only property" */
598
            $this->assertInstanceOf(InvalidCallException::class, $e);
599
        }
600
601
        /** related attribute $customer->orderItems didn't change cause it's read-only */
602
        $this->assertSame([], $customer->orderItems);
603
        $this->assertFalse($customer->canGetProperty('non_existing_property'));
604
        $this->assertFalse($customer->canSetProperty('non_existing_property'));
605
    }
606
607
    public function testHasAttribute(): void
608
    {
609
        $this->checkFixture($this->db, 'customer');
610
611
        $customer = new Customer($this->db);
612
613
        $this->assertTrue($customer->hasAttribute('id'));
614
        $this->assertTrue($customer->hasAttribute('email'));
615
        $this->assertFalse($customer->hasAttribute(0));
616
        $this->assertFalse($customer->hasAttribute(null));
617
        $this->assertFalse($customer->hasAttribute(42));
618
619
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
620
        $customer = $customerQuery->findOne(1);
621
        $this->assertTrue($customer->hasAttribute('id'));
622
        $this->assertTrue($customer->hasAttribute('email'));
623
        $this->assertFalse($customer->hasAttribute(0));
624
        $this->assertFalse($customer->hasAttribute(null));
625
        $this->assertFalse($customer->hasAttribute(42));
626
    }
627
628
    public function testRefresh(): void
629
    {
630
        $this->checkFixture($this->db, 'customer');
631
632
        $customer = new Customer($this->db);
633
634
        $this->assertFalse($customer->refresh());
635
636
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
637
        $customer = $customerQuery->findOne(1);
638
        $customer->name = 'to be refreshed';
639
640
        $this->assertTrue($customer->refresh());
641
        $this->assertEquals('user1', $customer->name);
642
    }
643
644
    public function testEquals(): void
645
    {
646
        $this->checkFixture($this->db, 'customer');
647
648
        $customerA = new Customer($this->db);
649
        $customerB = new Customer($this->db);
650
        $this->assertFalse($customerA->equals($customerB));
651
652
        $customerA = new Customer($this->db);
653
        $customerB = new Item($this->db);
654
        $this->assertFalse($customerA->equals($customerB));
655
    }
656
657
    public function providerForUnlinkDelete()
658
    {
659
        return [
660
            'with delete' => [true, 0],
661
            'without delete' => [false, 1],
662
        ];
663
    }
664
665
    /**
666
     * @dataProvider providerForUnlinkDelete
667
     *
668
     * @see https://github.com/yiisoft/yii2/issues/17174
669
     */
670
    public function testUnlinkWithViaOnCondition($delete, $count)
671
    {
672
        $this->checkFixture($this->db, 'order', true);
673
        $this->checkFixture($this->db, 'order_item_with_null_fk', true);
674
675
        $orderQuery = new ActiveQuery(Order::class, $this->db);
676
        $order = $orderQuery->findOne(2);
677
678
        $this->assertCount(1, $order->itemsFor8);
679
        $order->unlink('itemsFor8', $order->itemsFor8[0], $delete);
680
681
        $order = $orderQuery->findOne(2);
682
        $this->assertCount(0, $order->itemsFor8);
683
        $this->assertCount(2, $order->orderItemsWithNullFK);
684
685
        $orderItemQuery = new ActiveQuery(OrderItemWithNullFK::class, $this->db);
686
        $this->assertCount(1, $orderItemQuery->findAll([
687
            'order_id' => 2,
688
            'item_id' => 5,
689
        ]));
690
        $this->assertCount($count, $orderItemQuery->findAll([
691
            'order_id' => null,
692
            'item_id' => null,
693
        ]));
694
    }
695
696
    public function testVirtualRelation()
697
    {
698
        $this->checkFixture($this->db, 'order', true);
699
700
        $orderQuery = new ActiveQuery(Order::class, $this->db);
701
        /** @var Order $order */
702
        $order = $orderQuery->findOne(2);
703
704
        $order->setVirtualCustomerId($order->customer_id);
705
        $this->assertNotNull($order->getVirtualCustomer());
706
    }
707
}
708