Passed
Branch add-rector-actions (5668b1)
by Wilmer
03:26
created

ActiveQueryTest::testQueryScalar()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 6
rs 10
c 2
b 0
f 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\ActiveRecord\Tests;
6
7
use ReflectionException;
8
use Throwable;
9
use Yiisoft\ActiveRecord\ActiveQuery;
10
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\BitValues;
11
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Category;
12
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer;
13
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\CustomerQuery;
14
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Document;
15
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Dossier;
16
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Item;
17
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Order;
18
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\OrderItem;
19
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\OrderItemWithNullFK;
20
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\OrderWithNullFK;
21
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Profile;
22
use Yiisoft\Arrays\ArrayHelper;
23
use Yiisoft\Db\Command\Command;
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\Exception\StaleObjectException;
29
use Yiisoft\Db\Exception\UnknownPropertyException;
30
use Yiisoft\Db\Query\QueryInterface;
31
32
use function sort;
33
use function ucfirst;
34
35
abstract class ActiveQueryTest extends TestCase
36
{
37
    public function testOptions(): void
38
    {
39
        $this->checkFixture($this->db, 'customer', true);
40
41
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
42
43
        $query = $customerQuery->on(['a' => 'b'])->joinWith('profile');
44
        $this->assertEquals($query->getARClass(), Customer::class);
45
        $this->assertEquals($query->getOn(), ['a' => 'b']);
46
        $this->assertEquals($query->getJoinWith(), [[['profile'], true, 'LEFT JOIN']]);
47
    }
48
49
    public function testPrepare(): void
50
    {
51
        $this->checkFixture($this->db, 'customer');
52
53
        $query = new ActiveQuery(Customer::class, $this->db);
54
        $this->assertInstanceOf(QueryInterface::class, $query->prepare($this->db->getQueryBuilder()));
55
    }
56
57
    public function testPopulateEmptyRows(): void
58
    {
59
        $this->checkFixture($this->db, 'customer');
60
61
        $query = new ActiveQuery(Customer::class, $this->db);
62
        $this->assertEquals([], $query->populate([]));
63
    }
64
65
    public function testPopulateFilledRows(): void
66
    {
67
        $this->checkFixture($this->db, 'customer');
68
69
        $query = new ActiveQuery(Customer::class, $this->db);
70
        $rows = $query->all();
71
        $result = $query->populate($rows);
72
        $this->assertEquals($rows, $result);
73
    }
74
75
    public function testOne(): void
76
    {
77
        $this->checkFixture($this->db, 'customer');
78
79
        $query = new ActiveQuery(Customer::class, $this->db);
80
        $this->assertInstanceOf(Customer::class, $query->one());
81
    }
82
83
    public function testCreateCommand(): void
84
    {
85
        $this->checkFixture($this->db, 'customer');
86
87
        $query = new ActiveQuery(Customer::class, $this->db);
88
        $this->assertInstanceOf(Command::class, $query->createCommand());
89
    }
90
91
    public function testQueryScalar(): void
92
    {
93
        $this->checkFixture($this->db, 'customer');
94
95
        $query = new ActiveQuery(Customer::class, $this->db);
96
        $this->assertEquals('user1', $this->invokeMethod($query, 'queryScalar', ['name']));
97
    }
98
99
    public function testGetJoinWith(): void
100
    {
101
        $this->checkFixture($this->db, 'customer');
102
103
        $query = new ActiveQuery(Customer::class, $this->db);
104
        $query->joinWith('profile');
105
        $this->assertEquals([[['profile'], true, 'LEFT JOIN']], $query->getJoinWith());
106
    }
107
108
    public function testInnerJoinWith(): void
109
    {
110
        $this->checkFixture($this->db, 'customer');
111
112
        $query = new ActiveQuery(Customer::class, $this->db);
113
        $query->innerJoinWith('profile');
114
        $this->assertEquals([[['profile'], true, 'INNER JOIN']], $query->getJoinWith());
115
    }
116
117
    public function testBuildJoinWithRemoveDuplicateJoinByTableName(): void
118
    {
119
        $this->checkFixture($this->db, 'customer');
120
121
        $query = new ActiveQuery(Customer::class, $this->db);
122
        $query->innerJoinWith('orders')->joinWith('orders.orderItems');
123
        $this->invokeMethod($query, 'buildJoinWith');
124
        $this->assertEquals([
125
            [
126
                'INNER JOIN',
127
                'order',
128
                '{{customer}}.[[id]] = {{order}}.[[customer_id]]',
129
            ],
130
            [
131
                'LEFT JOIN',
132
                'order_item',
133
                '{{order}}.[[id]] = {{order_item}}.[[order_id]]',
134
            ],
135
        ], $query->getJoin());
136
    }
137
138
    public function testGetQueryTableNameFromNotSet(): void
139
    {
140
        $this->checkFixture($this->db, 'customer');
141
142
        $query = new ActiveQuery(Customer::class, $this->db);
143
        $this->assertEquals(['customer', 'customer'], $this->invokeMethod($query, 'getTableNameAndAlias'));
144
    }
145
146
    public function testGetQueryTableNameFromSet(): void
147
    {
148
        $this->checkFixture($this->db, 'customer');
149
150
        $query = new ActiveQuery(Customer::class, $this->db);
151
        $query->from(['alias' => 'customer']);
152
        $this->assertEquals(['customer', 'alias'], $this->invokeMethod($query, 'getTableNameAndAlias'));
153
    }
154
155
    public function testOnCondition(): void
156
    {
157
        $this->checkFixture($this->db, 'customer');
158
159
        $on = ['active' => true];
160
        $params = ['a' => 'b'];
161
162
        $query = new ActiveQuery(Customer::class, $this->db);
163
        $query->onCondition($on, $params);
164
        $this->assertEquals($on, $query->getOn());
165
        $this->assertEquals($params, $query->getParams());
166
    }
167
168
    public function testAndOnConditionOnNotSet(): void
169
    {
170
        $this->checkFixture($this->db, 'customer');
171
172
        $on = ['active' => true];
173
        $params = ['a' => 'b'];
174
        $query = new ActiveQuery(Customer::class, $this->db);
175
        $query->andOnCondition($on, $params);
176
        $this->assertEquals($on, $query->getOn());
177
        $this->assertEquals($params, $query->getParams());
178
    }
179
180
    public function testAndOnConditionOnSet(): void
181
    {
182
        $this->checkFixture($this->db, 'customer');
183
184
        $onOld = ['active' => true];
185
        $on = ['active' => true];
186
        $params = ['a' => 'b'];
187
188
        $query = new ActiveQuery(Customer::class, $this->db);
189
190
        $query->on($onOld)->andOnCondition($on, $params);
191
192
        $this->assertEquals(['and', $onOld, $on], $query->getOn());
193
        $this->assertEquals($params, $query->getParams());
194
    }
195
196
    public function testOrOnConditionOnNotSet(): void
197
    {
198
        $this->checkFixture($this->db, 'customer');
199
200
        $on = ['active' => true];
201
        $params = ['a' => 'b'];
202
203
        $query = new ActiveQuery(Customer::class, $this->db);
204
205
        $query->orOnCondition($on, $params);
206
207
        $this->assertEquals($on, $query->getOn());
208
        $this->assertEquals($params, $query->getParams());
209
    }
210
211
    public function testOrOnConditionOnSet(): void
212
    {
213
        $this->checkFixture($this->db, 'customer');
214
215
        $onOld = ['active' => true];
216
        $on = ['active' => true];
217
        $params = ['a' => 'b'];
218
219
        $query = new ActiveQuery(Customer::class, $this->db);
220
221
        $query->on($onOld)->orOnCondition($on, $params);
222
223
        $this->assertEquals(['or', $onOld, $on], $query->getOn());
224
        $this->assertEquals($params, $query->getParams());
225
    }
226
227
    public function testViaTable(): void
228
    {
229
        $this->checkFixture($this->db, 'customer');
230
231
        $order = new Order($this->db);
232
233
        $query = new ActiveQuery(Customer::class, $this->db);
234
235
        $query->primaryModel($order)->viaTable(Profile::class, ['id' => 'item_id']);
236
237
        $this->assertInstanceOf(ActiveQuery::class, $query);
238
        $this->assertInstanceOf(ActiveQuery::class, $query->getVia());
239
    }
240
241
    public function testAliasNotSet(): void
242
    {
243
        $this->checkFixture($this->db, 'customer');
244
245
        $query = new ActiveQuery(Customer::class, $this->db);
246
247
        $query->alias('alias');
248
249
        $this->assertInstanceOf(ActiveQuery::class, $query);
250
        $this->assertEquals(['alias' => 'customer'], $query->getFrom());
251
    }
252
253
    public function testAliasYetSet(): void
254
    {
255
        $this->checkFixture($this->db, 'customer');
256
257
        $aliasOld = ['old'];
258
259
        $query = new ActiveQuery(Customer::class, $this->db);
260
261
        $query->from($aliasOld)->alias('alias');
262
263
        $this->assertInstanceOf(ActiveQuery::class, $query);
264
        $this->assertEquals(['alias' => 'old'], $query->getFrom());
265
    }
266
267
    public function testGetTableNamesNotFilledFrom(): void
268
    {
269
        $this->checkFixture($this->db, 'profile');
270
271
        $query = new ActiveQuery(Profile::class, $this->db);
272
273
        $tableName = Profile::tableName();
274
275
        $this->assertEquals(
276
            [
277
                '{{' . $tableName . '}}' => '{{' . $tableName . '}}',
278
            ],
279
            $query->getTablesUsedInFrom()
280
        );
281
    }
282
283
    public function testGetTableNamesWontFillFrom(): void
284
    {
285
        $this->checkFixture($this->db, 'profile');
286
287
        $query = new ActiveQuery(Profile::class, $this->db);
288
289
        $this->assertEquals($query->getFrom(), null);
290
291
        $query->getTablesUsedInFrom();
292
293
        $this->assertEquals($query->getFrom(), null);
294
    }
295
296
    /**
297
     * {@see https://github.com/yiisoft/yii2/issues/5341}
298
     *
299
     * Issue: Plan 1 -- * Account * -- * User
300
     * Our Tests: Category 1 -- * Item * -- * Order
301
     */
302
    public function testDeeplyNestedTableRelationWith(): void
303
    {
304
        $this->checkFixture($this->db, 'category');
305
306
        $this->loadFixture($this->db);
307
308
        /** @var $category Category */
309
        $categoriesQuery = new ActiveQuery(Category::class, $this->db);
310
311
        $categories = $categoriesQuery->with('orders')->indexBy('id')->all();
312
        $category = $categories[1];
313
        $this->assertNotNull($category);
314
315
        $orders = $category->orders;
0 ignored issues
show
Bug introduced by
Accessing orders on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
316
        $this->assertCount(2, $orders);
317
        $this->assertInstanceOf(Order::class, $orders[0]);
318
        $this->assertInstanceOf(Order::class, $orders[1]);
319
320
        $ids = [$orders[0]->id, $orders[1]->id];
321
        sort($ids);
322
        $this->assertEquals([1, 3], $ids);
323
324
        $category = $categories[2];
325
        $this->assertNotNull($category);
326
327
        $orders = $category->orders;
328
        $this->assertCount(1, $orders);
329
        $this->assertInstanceOf(Order::class, $orders[0]);
330
        $this->assertEquals(2, $orders[0]->id);
331
    }
332
333
    public function testGetSql(): void
334
    {
335
        $this->checkFixture($this->db, 'customer');
336
337
        $query = new ActiveQuery(Customer::class, $this->db);
338
339
        $query->sql('SELECT * FROM {{customer}} ORDER BY [[id]] DESC');
340
341
        $this->assertEquals('SELECT * FROM {{customer}} ORDER BY [[id]] DESC', $query->getSql());
342
    }
343
344
    public function testCustomColumns(): void
345
    {
346
        $this->checkFixture($this->db, 'customer');
347
348
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
349
350
        /** find custom column */
351
        if ($this->driverName === 'oci') {
352
            $customers = $customerQuery
353
                ->select(['{{customer}}.*', '([[status]]*2) AS [[status2]]'])
354
                ->where(['name' => 'user3'])->one();
355
        } else {
356
            $customers = $customerQuery
357
                ->select(['*', '([[status]]*2) AS [[status2]]'])
358
                ->where(['name' => 'user3'])->one();
359
        }
360
361
        $this->assertEquals(3, $customers->getAttribute('id'));
362
        $this->assertEquals(4, $customers->status2);
363
    }
364
365
    public function testCallFind(): void
366
    {
367
        $this->checkFixture($this->db, 'customer');
368
369
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
370
371
        /** find count, sum, average, min, max, scalar */
372
        $this->assertEquals(3, $customerQuery->count());
373
        $this->assertEquals(6, $customerQuery->sum('[[id]]'));
374
        $this->assertEquals(2, $customerQuery->average('[[id]]'));
375
        $this->assertEquals(1, $customerQuery->min('[[id]]'));
376
        $this->assertEquals(3, $customerQuery->max('[[id]]'));
377
        $this->assertEquals(3, $customerQuery->select('COUNT(*)')->scalar());
378
        $this->assertEquals(2, $customerQuery->where('[[id]]=1 OR [[id]]=2')->count());
379
    }
380
381
    /**
382
     * {@see https://github.com/yiisoft/yii2/issues/8593}
383
     */
384
    public function testCountWithFindBySql(): void
385
    {
386
        $this->checkFixture($this->db, 'customer');
387
388
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
389
390
        $query = $customerQuery->findBySql('SELECT * FROM {{customer}}');
391
        $this->assertEquals(3, $query->count());
392
393
        $query = $customerQuery->findBySql('SELECT * FROM {{customer}} WHERE  [[id]]=:id', [':id' => 2]);
394
        $this->assertEquals(1, $query->count());
395
    }
396
397
    public function testDeeplyNestedTableRelation(): void
398
    {
399
        $this->checkFixture($this->db, 'customer');
400
401
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
402
403
        $customers = $customerQuery->findOne(1);
404
        $this->assertNotNull($customerQuery);
405
406
        $items = $customers->orderItems;
0 ignored issues
show
Bug introduced by
Accessing orderItems on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
407
408
        $this->assertCount(2, $items);
409
        $this->assertEquals(1, $items[0]->getAttribute('id'));
410
        $this->assertEquals(2, $items[1]->getAttribute('id'));
411
        $this->assertInstanceOf(Item::class, $items[0]);
412
        $this->assertInstanceOf(Item::class, $items[1]);
413
    }
414
415
    /**
416
     * {@see https://github.com/yiisoft/yii2/issues/5341}
417
     *
418
     * Issue: Plan 1 -- * Account * -- * User
419
     * Our Tests: Category 1 -- * Item * -- * Order
420
     */
421
    public function testDeeplyNestedTableRelation2(): void
422
    {
423
        $this->checkFixture($this->db, 'category');
424
425
        $categoryQuery = new ActiveQuery(Category::class, $this->db);
426
427
        $categories = $categoryQuery->where(['id' => 1])->one();
428
        $this->assertNotNull($categories);
429
430
        $orders = $categories->orders;
431
        $this->assertCount(2, $orders);
432
        $this->assertInstanceOf(Order::class, $orders[0]);
433
        $this->assertInstanceOf(Order::class, $orders[1]);
434
435
        $ids = [$orders[0]->id, $orders[1]->getAttribute('id')];
436
        sort($ids);
437
        $this->assertEquals([1, 3], $ids);
438
439
        $categories = $categoryQuery->where(['id' => 2])->one();
440
        $this->assertNotNull($categories);
441
442
        $orders = $categories->orders;
443
        $this->assertCount(1, $orders);
444
        $this->assertEquals(2, $orders[0]->getAttribute('id'));
445
        $this->assertInstanceOf(Order::class, $orders[0]);
446
    }
447
448
    public function testJoinWith(): void
449
    {
450
        $this->checkFixture($this->db, 'order');
451
452
        /** left join and eager loading */
453
        $orderQuery = new ActiveQuery(Order::class, $this->db);
454
        $orders = $orderQuery->joinWith('customer')->orderBy('customer.id DESC, order.id')->all();
455
        $this->assertCount(3, $orders);
456
        $this->assertEquals(2, $orders[0]->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
457
        $this->assertEquals(3, $orders[1]->id);
458
        $this->assertEquals(1, $orders[2]->id);
459
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
0 ignored issues
show
Bug introduced by
The method isRelationPopulated() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Yiisoft\ActiveRecord\ActiveRecordInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

459
        $this->assertTrue($orders[0]->/** @scrutinizer ignore-call */ isRelationPopulated('customer'));
Loading history...
460
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
461
        $this->assertTrue($orders[2]->isRelationPopulated('customer'));
462
463
        /** inner join filtering and eager loading */
464
        $orderQuery = new ActiveQuery(Order::class, $this->db);
465
        $orders = $orderQuery->innerJoinWith(
466
            [
467
                'customer' => function ($query) {
468
                    $query->where('{{customer}}.[[id]]=2');
469
                },
470
            ]
471
        )->orderBy('order.id')->all();
472
        $this->assertCount(2, $orders);
473
        $this->assertEquals(2, $orders[0]->id);
474
        $this->assertEquals(3, $orders[1]->id);
475
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
476
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
477
478
        /** inner join filtering, eager loading, conditions on both primary and relation */
479
        $orderQuery = new ActiveQuery(Order::class, $this->db);
480
        $orders = $orderQuery->innerJoinWith(
481
            [
482
                'customer' => function ($query) {
483
                    $query->where(['customer.id' => 2]);
484
                },
485
            ]
486
        )->where(['order.id' => [1, 2]])->orderBy('order.id')->all();
487
        $this->assertCount(1, $orders);
488
        $this->assertEquals(2, $orders[0]->id);
489
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
490
491
        /** inner join filtering without eager loading */
492
        $orderQuery = new ActiveQuery(Order::class, $this->db);
493
        $orders = $orderQuery->innerJoinWith(
494
            [
495
                'customer' => static function ($query) {
496
                    $query->where('{{customer}}.[[id]]=2');
497
                },
498
            ],
499
            false
500
        )->orderBy('order.id')->all();
501
        $this->assertCount(2, $orders);
502
        $this->assertEquals(2, $orders[0]->id);
503
        $this->assertEquals(3, $orders[1]->id);
504
        $this->assertFalse($orders[0]->isRelationPopulated('customer'));
505
        $this->assertFalse($orders[1]->isRelationPopulated('customer'));
506
507
        /** inner join filtering without eager loading, conditions on both primary and relation */
508
        $orderQuery = new ActiveQuery(Order::class, $this->db);
509
        $orders = $orderQuery->innerJoinWith(
510
            [
511
                'customer' => static function ($query) {
512
                    $query->where(['customer.id' => 2]);
513
                },
514
            ],
515
            false
516
        )->where(['order.id' => [1, 2]])->orderBy('order.id')->all();
517
        $this->assertCount(1, $orders);
518
        $this->assertEquals(2, $orders[0]->id);
519
        $this->assertFalse($orders[0]->isRelationPopulated('customer'));
520
521
        /** join with via-relation */
522
        $orderQuery = new ActiveQuery(Order::class, $this->db);
523
        $orders = $orderQuery->innerJoinWith('books')->orderBy('order.id')->all();
524
        $this->assertCount(2, $orders);
525
        $this->assertCount(2, $orders[0]->books);
0 ignored issues
show
Bug introduced by
Accessing books on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
526
        $this->assertCount(1, $orders[1]->books);
527
        $this->assertEquals(1, $orders[0]->id);
528
        $this->assertEquals(3, $orders[1]->id);
529
        $this->assertTrue($orders[0]->isRelationPopulated('books'));
530
        $this->assertTrue($orders[1]->isRelationPopulated('books'));
531
532
        /** join with sub-relation */
533
        $orderQuery = new ActiveQuery(Order::class, $this->db);
534
        $orders = $orderQuery->innerJoinWith(
535
            [
536
                'items' => function ($q) {
537
                    $q->orderBy('item.id');
538
                },
539
                'items.category' => function ($q) {
540
                    $q->where('{{category}}.[[id]] = 2');
541
                },
542
            ]
543
        )->orderBy('order.id')->all();
544
        $this->assertCount(1, $orders);
545
        $this->assertCount(3, $orders[0]->items);
0 ignored issues
show
Bug introduced by
Accessing items on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
546
        $this->assertEquals(2, $orders[0]->id);
547
        $this->assertEquals(2, $orders[0]->items[0]->category->id);
548
        $this->assertTrue($orders[0]->isRelationPopulated('items'));
549
        $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category'));
550
551
        /** join with table alias */
552
        $orderQuery = new ActiveQuery(Order::class, $this->db);
553
        $orders = $orderQuery->joinWith(
554
            [
555
                'customer' => function ($q) {
556
                    $q->from('customer c');
557
                },
558
            ]
559
        )->orderBy('c.id DESC, order.id')->all();
560
        $this->assertCount(3, $orders);
561
        $this->assertEquals(2, $orders[0]->id);
562
        $this->assertEquals(3, $orders[1]->id);
563
        $this->assertEquals(1, $orders[2]->id);
564
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
565
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
566
        $this->assertTrue($orders[2]->isRelationPopulated('customer'));
567
568
        /** join with table alias */
569
        $orderQuery = new ActiveQuery(Order::class, $this->db);
570
        $orders = $orderQuery->joinWith('customer as c')->orderBy('c.id DESC, order.id')->all();
571
        $this->assertCount(3, $orders);
572
        $this->assertEquals(2, $orders[0]->id);
573
        $this->assertEquals(3, $orders[1]->id);
574
        $this->assertEquals(1, $orders[2]->id);
575
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
576
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
577
        $this->assertTrue($orders[2]->isRelationPopulated('customer'));
578
579
        /** join with table alias sub-relation */
580
        $orderQuery = new ActiveQuery(Order::class, $this->db);
581
        $orders = $orderQuery->innerJoinWith(
582
            [
583
                'items as t' => function ($q) {
584
                    $q->orderBy('t.id');
585
                },
586
                'items.category as c' => function ($q) {
587
                    $q->where('{{c}}.[[id]] = 2');
588
                },
589
            ]
590
        )->orderBy('order.id')->all();
591
        $this->assertCount(1, $orders);
592
        $this->assertCount(3, $orders[0]->items);
593
        $this->assertEquals(2, $orders[0]->id);
594
        $this->assertEquals(2, $orders[0]->items[0]->category->id);
595
        $this->assertTrue($orders[0]->isRelationPopulated('items'));
596
        $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category'));
597
598
        /** join with ON condition */
599
        $orderQuery = new ActiveQuery(Order::class, $this->db);
600
        $orders = $orderQuery->joinWith('books2')->orderBy('order.id')->all();
601
        $this->assertCount(3, $orders);
602
        $this->assertCount(2, $orders[0]->books2);
0 ignored issues
show
Bug introduced by
Accessing books2 on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
603
        $this->assertCount(0, $orders[1]->books2);
604
        $this->assertCount(1, $orders[2]->books2);
605
        $this->assertEquals(1, $orders[0]->id);
606
        $this->assertEquals(2, $orders[1]->id);
607
        $this->assertEquals(3, $orders[2]->id);
608
        $this->assertTrue($orders[0]->isRelationPopulated('books2'));
609
        $this->assertTrue($orders[1]->isRelationPopulated('books2'));
610
        $this->assertTrue($orders[2]->isRelationPopulated('books2'));
611
612
        /** lazy loading with ON condition */
613
        $orderQuery = new ActiveQuery(Order::class, $this->db);
614
        $order = $orderQuery->findOne(1);
615
        $this->assertCount(2, $order->books2);
616
617
        $orderQuery = new ActiveQuery(Order::class, $this->db);
618
        $order = $orderQuery->findOne(2);
619
        $this->assertCount(0, $order->books2);
620
621
        $order = new ActiveQuery(Order::class, $this->db);
622
        $order = $order->findOne(3);
623
        $this->assertCount(1, $order->books2);
624
625
        /** eager loading with ON condition */
626
        $orderQuery = new ActiveQuery(Order::class, $this->db);
627
        $orders = $orderQuery->with('books2')->all();
628
        $this->assertCount(3, $orders);
629
        $this->assertCount(2, $orders[0]->books2);
630
        $this->assertCount(0, $orders[1]->books2);
631
        $this->assertCount(1, $orders[2]->books2);
632
        $this->assertEquals(1, $orders[0]->id);
633
        $this->assertEquals(2, $orders[1]->id);
634
        $this->assertEquals(3, $orders[2]->id);
635
        $this->assertTrue($orders[0]->isRelationPopulated('books2'));
636
        $this->assertTrue($orders[1]->isRelationPopulated('books2'));
637
        $this->assertTrue($orders[2]->isRelationPopulated('books2'));
638
639
        /** join with count and query */
640
        $orderQuery = new ActiveQuery(Order::class, $this->db);
641
        $query = $orderQuery->joinWith('customer');
642
        $count = $query->count();
643
        $this->assertEquals(3, $count);
644
645
        $orders = $query->all();
646
        $this->assertCount(3, $orders);
647
648
        /** {@see https://github.com/yiisoft/yii2/issues/2880} */
649
        $orderQuery = new ActiveQuery(Order::class, $this->db);
650
        $query = $orderQuery->findOne(1);
651
        $customer = $query->getCustomer()->joinWith(
0 ignored issues
show
Bug introduced by
The method getCustomer() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. It seems like you code against a sub-type of Yiisoft\ActiveRecord\ActiveRecordInterface such as Yiisoft\ActiveRecord\Tes...tubs\ActiveRecord\Order. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

651
        $customer = $query->/** @scrutinizer ignore-call */ getCustomer()->joinWith(
Loading history...
652
            [
653
                'orders' => static function ($q) {
654
                    $q->orderBy([]);
655
                },
656
            ]
657
        )->one();
658
        $this->assertEquals(1, $customer->id);
659
660
        $orderQuery = new ActiveQuery(Order::class, $this->db);
661
        $order = $orderQuery->joinWith(
0 ignored issues
show
Unused Code introduced by
The assignment to $order is dead and can be removed.
Loading history...
662
            [
663
                'items' => static function ($q) {
664
                    $q->from(['items' => 'item'])->orderBy('items.id');
665
                },
666
            ]
667
        )->orderBy('order.id')->one();
668
669
        /** join with sub-relation called inside Closure */
670
        $orderQuery = new ActiveQuery(Order::class, $this->db);
671
        $orders = $orderQuery->joinWith(
672
            [
673
                'items' => static function ($q) {
674
                    $q->orderBy('item.id');
675
                    $q->joinWith([
676
                        'category' => static function ($q) {
677
                            $q->where('{{category}}.[[id]] = 2');
678
                        },
679
                    ]);
680
                },
681
            ]
682
        )->orderBy('order.id')->all();
683
        $this->assertCount(1, $orders);
684
        $this->assertCount(3, $orders[0]->items);
685
        $this->assertEquals(2, $orders[0]->id);
686
        $this->assertEquals(2, $orders[0]->items[0]->category->id);
687
        $this->assertTrue($orders[0]->isRelationPopulated('items'));
688
        $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category'));
689
    }
690
691
    /**
692
     * @depends testJoinWith
693
     */
694
    public function testJoinWithAndScope(): void
695
    {
696
        $this->checkFixture($this->db, 'customer');
697
698
        /**  hasOne inner join */
699
        $customer = new CustomerQuery(Customer::class, $this->db);
700
        $customers = $customer->active()->innerJoinWith('profile')->orderBy('customer.id')->all();
701
        $this->assertCount(1, $customers);
702
        $this->assertEquals(1, $customers[0]->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
703
        $this->assertTrue($customers[0]->isRelationPopulated('profile'));
704
705
        /** hasOne outer join */
706
        $customer = new CustomerQuery(Customer::class, $this->db);
707
        $customers = $customer->active()->joinWith('profile')->orderBy('customer.id')->all();
708
        $this->assertCount(2, $customers);
709
        $this->assertEquals(1, $customers[0]->id);
710
        $this->assertEquals(2, $customers[1]->id);
711
        $this->assertInstanceOf(Profile::class, $customers[0]->profile);
0 ignored issues
show
Bug introduced by
Accessing profile on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
712
        $this->assertNull($customers[1]->profile);
713
        $this->assertTrue($customers[0]->isRelationPopulated('profile'));
714
        $this->assertTrue($customers[1]->isRelationPopulated('profile'));
715
716
        /** hasMany */
717
        $customer = new CustomerQuery(Customer::class, $this->db);
718
        $customers = $customer->active()->joinWith(
719
            [
720
                'orders' => static function ($q) {
721
                    $q->orderBy('order.id');
722
                },
723
            ]
724
        )->orderBy('customer.id DESC, order.id')->all();
725
        $this->assertCount(2, $customers);
726
        $this->assertEquals(2, $customers[0]->id);
727
        $this->assertEquals(1, $customers[1]->id);
728
        $this->assertTrue($customers[0]->isRelationPopulated('orders'));
729
        $this->assertTrue($customers[1]->isRelationPopulated('orders'));
730
    }
731
732
    /**
733
     * @depends testJoinWith
734
     *
735
     * This query will do the same join twice, ensure duplicated JOIN gets removed.
736
     *
737
     * {@see https://github.com/yiisoft/yii2/pull/2650}
738
     */
739
    public function testJoinWithVia(): void
740
    {
741
        $this->checkFixture($this->db, 'order');
742
743
        $orderQuery = new ActiveQuery(Order::class, $this->db);
744
745
        $this->db->getQueryBuilder()->setSeparator("\n");
746
747
        $rows = $orderQuery->joinWith('itemsInOrder1')->joinWith(
748
            [
749
                'items' => static function ($q) {
750
                    $q->orderBy('item.id');
751
                },
752
            ]
753
        )->all();
754
        $this->assertNotEmpty($rows);
755
    }
756
757
    public function aliasMethodProvider(): array
758
    {
759
        return [
760
            ['explicit'],
761
        ];
762
    }
763
764
    /**
765
     * @depends testJoinWith
766
     *
767
     * Tests the alias syntax for joinWith: 'alias' => 'relation'.
768
     *
769
     * @dataProvider aliasMethodProvider
770
     *
771
     * @param string $aliasMethod whether alias is specified explicitly or using the query syntax {{@tablename}}
772
     *
773
     * @throws Exception|InvalidConfigException|Throwable
774
     */
775
    public function testJoinWithAlias(string $aliasMethod): void
776
    {
777
        $orders = [];
778
        $this->checkFixture($this->db, 'order');
779
780
        /** left join and eager loading */
781
        $orderQuery = new ActiveQuery(Order::class, $this->db);
782
        $query = $orderQuery->joinWith(['customer c']);
783
784
        if ($aliasMethod === 'explicit') {
785
            $orders = $query->orderBy('c.id DESC, order.id')->all();
786
        } elseif ($aliasMethod === 'querysyntax') {
787
            $orders = $query->orderBy('{{@customer}}.id DESC, {{@order}}.id')->all();
788
        } elseif ($aliasMethod === 'applyAlias') {
789
            $orders = $query->orderBy(
790
                $query->applyAlias('customer', 'id') . ' DESC,' . $query->applyAlias('order', 'id')
0 ignored issues
show
Bug introduced by
The method applyAlias() does not exist on Yiisoft\ActiveRecord\ActiveQuery. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

790
                $query->/** @scrutinizer ignore-call */ 
791
                        applyAlias('customer', 'id') . ' DESC,' . $query->applyAlias('order', 'id')

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
791
            )->all();
792
        }
793
794
        $this->assertCount(3, $orders);
795
        $this->assertEquals(2, $orders[0]->id);
796
        $this->assertEquals(3, $orders[1]->id);
797
        $this->assertEquals(1, $orders[2]->id);
798
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
799
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
800
        $this->assertTrue($orders[2]->isRelationPopulated('customer'));
801
802
        /** inner join filtering and eager loading */
803
        $orderQuery = new ActiveQuery(Order::class, $this->db);
804
        $query = $orderQuery->innerJoinWith(['customer c']);
805
806
        if ($aliasMethod === 'explicit') {
807
            $orders = $query->where('{{c}}.[[id]]=2')->orderBy('order.id')->all();
808
        } elseif ($aliasMethod === 'querysyntax') {
809
            $orders = $query->where('{{@customer}}.[[id]]=2')->orderBy('{{@order}}.id')->all();
810
        } elseif ($aliasMethod === 'applyAlias') {
811
            $orders = $query->where(
812
                [$query->applyAlias('customer', 'id') => 2]
813
            )->orderBy($query->applyAlias('order', 'id'))->all();
814
        }
815
816
        $this->assertCount(2, $orders);
817
        $this->assertEquals(2, $orders[0]->id);
818
        $this->assertEquals(3, $orders[1]->id);
819
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
820
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
821
822
        /** inner join filtering without eager loading */
823
        $orderQuery = new ActiveQuery(Order::class, $this->db);
824
        $query = $orderQuery->innerJoinWith(['customer c'], false);
825
826
        if ($aliasMethod === 'explicit') {
827
            $orders = $query->where('{{c}}.[[id]]=2')->orderBy('order.id')->all();
828
        } elseif ($aliasMethod === 'querysyntax') {
829
            $orders = $query->where('{{@customer}}.[[id]]=2')->orderBy('{{@order}}.id')->all();
830
        } elseif ($aliasMethod === 'applyAlias') {
831
            $orders = $query->where(
832
                [$query->applyAlias('customer', 'id') => 2]
833
            )->orderBy($query->applyAlias('order', 'id'))->all();
834
        }
835
836
        $this->assertCount(2, $orders);
837
        $this->assertEquals(2, $orders[0]->id);
838
        $this->assertEquals(3, $orders[1]->id);
839
        $this->assertFalse($orders[0]->isRelationPopulated('customer'));
840
        $this->assertFalse($orders[1]->isRelationPopulated('customer'));
841
842
        /** join with via-relation */
843
        $orderQuery = new ActiveQuery(Order::class, $this->db);
844
        $query = $orderQuery->innerJoinWith(['books b']);
845
846
        if ($aliasMethod === 'explicit') {
847
            $orders = $query->where(
848
                ['b.name' => 'Yii 1.1 Application Development Cookbook']
849
            )->orderBy('order.id')->all();
850
        } elseif ($aliasMethod === 'querysyntax') {
851
            $orders = $query->where(
852
                ['{{@item}}.name' => 'Yii 1.1 Application Development Cookbook']
853
            )->orderBy('{{@order}}.id')->all();
854
        } elseif ($aliasMethod === 'applyAlias') {
855
            $orders = $query->where(
856
                [$query->applyAlias('book', 'name') => 'Yii 1.1 Application Development Cookbook']
857
            )->orderBy($query->applyAlias('order', 'id'))->all();
858
        }
859
860
        $this->assertCount(2, $orders);
861
        $this->assertCount(2, $orders[0]->books);
862
        $this->assertCount(1, $orders[1]->books);
863
        $this->assertEquals(1, $orders[0]->id);
864
        $this->assertEquals(3, $orders[1]->id);
865
        $this->assertTrue($orders[0]->isRelationPopulated('books'));
866
        $this->assertTrue($orders[1]->isRelationPopulated('books'));
867
868
        /** joining sub relations */
869
        $orderQuery = new ActiveQuery(Order::class, $this->db);
870
        $query = $orderQuery->innerJoinWith(
871
            [
872
                'items i' => static function ($q) use ($aliasMethod) {
873
                    /** @var $q ActiveQuery */
874
                    if ($aliasMethod === 'explicit') {
875
                        $q->orderBy('{{i}}.id');
876
                    } elseif ($aliasMethod === 'querysyntax') {
877
                        $q->orderBy('{{@item}}.id');
878
                    } elseif ($aliasMethod === 'applyAlias') {
879
                        $q->orderBy($q->applyAlias('item', 'id'));
880
                    }
881
                },
882
                'items.category c' => static function ($q) use ($aliasMethod) {
883
                    /**  @var $q ActiveQuery */
884
                    if ($aliasMethod === 'explicit') {
885
                        $q->where('{{c}}.[[id]] = 2');
886
                    } elseif ($aliasMethod === 'querysyntax') {
887
                        $q->where('{{@category}}.[[id]] = 2');
888
                    } elseif ($aliasMethod === 'applyAlias') {
889
                        $q->where([$q->applyAlias('category', 'id') => 2]);
890
                    }
891
                },
892
            ]
893
        );
894
895
        if ($aliasMethod === 'explicit') {
896
            $orders = $query->orderBy('{{i}}.id')->all();
897
        } elseif ($aliasMethod === 'querysyntax') {
898
            $orders = $query->orderBy('{{@item}}.id')->all();
899
        } elseif ($aliasMethod === 'applyAlias') {
900
            $orders = $query->orderBy($query->applyAlias('item', 'id'))->all();
901
        }
902
903
        $this->assertCount(1, $orders);
904
        $this->assertCount(3, $orders[0]->items);
905
        $this->assertEquals(2, $orders[0]->id);
906
        $this->assertEquals(2, $orders[0]->items[0]->category->id);
907
        $this->assertTrue($orders[0]->isRelationPopulated('items'));
908
        $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category'));
909
910
        /** join with ON condition */
911
        if ($aliasMethod === 'explicit' || $aliasMethod === 'querysyntax') {
912
            $relationName = 'books' . ucfirst($aliasMethod);
913
914
            $orderQuery = new ActiveQuery(Order::class, $this->db);
915
            $orders = $orderQuery->joinWith(["$relationName b"])->orderBy('order.id')->all();
916
917
            $this->assertCount(3, $orders);
918
            $this->assertCount(2, $orders[0]->$relationName);
919
            $this->assertCount(0, $orders[1]->$relationName);
920
            $this->assertCount(1, $orders[2]->$relationName);
921
            $this->assertEquals(1, $orders[0]->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
922
            $this->assertEquals(2, $orders[1]->id);
923
            $this->assertEquals(3, $orders[2]->id);
924
            $this->assertTrue($orders[0]->isRelationPopulated($relationName));
925
            $this->assertTrue($orders[1]->isRelationPopulated($relationName));
926
            $this->assertTrue($orders[2]->isRelationPopulated($relationName));
927
        }
928
929
        /** join with ON condition and alias in relation definition */
930
        if ($aliasMethod === 'explicit' || $aliasMethod === 'querysyntax') {
931
            $relationName = 'books' . ucfirst($aliasMethod) . 'A';
932
933
            $orderQuery = new ActiveQuery(Order::class, $this->db);
934
            $orders = $orderQuery->joinWith([(string)$relationName])->orderBy('order.id')->all();
935
936
            $this->assertCount(3, $orders);
937
            $this->assertCount(2, $orders[0]->$relationName);
938
            $this->assertCount(0, $orders[1]->$relationName);
939
            $this->assertCount(1, $orders[2]->$relationName);
940
            $this->assertEquals(1, $orders[0]->id);
941
            $this->assertEquals(2, $orders[1]->id);
942
            $this->assertEquals(3, $orders[2]->id);
943
            $this->assertTrue($orders[0]->isRelationPopulated($relationName));
944
            $this->assertTrue($orders[1]->isRelationPopulated($relationName));
945
            $this->assertTrue($orders[2]->isRelationPopulated($relationName));
946
        }
947
948
        /** join with count and query */
949
        $orderQuery = new ActiveQuery(Order::class, $this->db);
950
        $query = $orderQuery->joinWith(['customer c']);
951
952
        if ($aliasMethod === 'explicit') {
953
            $count = $query->count('c.id');
954
        } elseif ($aliasMethod === 'querysyntax') {
955
            $count = $query->count('{{@customer}}.id');
956
        } elseif ($aliasMethod === 'applyAlias') {
957
            $count = $query->count($query->applyAlias('customer', 'id'));
958
        }
959
960
        $this->assertEquals(3, $count);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $count does not seem to be defined for all execution paths leading up to this point.
Loading history...
961
962
        $orders = $query->all();
963
        $this->assertCount(3, $orders);
964
965
        /** relational query */
966
        $orderQuery = new ActiveQuery(Order::class, $this->db);
967
        $order = $orderQuery->findOne(1);
968
969
        $customerQuery = $order->getCustomer()->innerJoinWith(['orders o'], false);
970
971
        if ($aliasMethod === 'explicit') {
972
            $customer = $customerQuery->where(['o.id' => 1])->one();
973
        } elseif ($aliasMethod === 'querysyntax') {
974
            $customer = $customerQuery->where(['{{@order}}.id' => 1])->one();
975
        } elseif ($aliasMethod === 'applyAlias') {
976
            $customer = $customerQuery->where([$query->applyAlias('order', 'id') => 1])->one();
977
        }
978
979
        $this->assertEquals(1, $customer->id);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $customer does not seem to be defined for all execution paths leading up to this point.
Loading history...
980
        $this->assertNotNull($customer);
981
982
        /** join with sub-relation called inside Closure */
983
        $orderQuery = new ActiveQuery(Order::class, $this->db);
984
        $orders = $orderQuery->joinWith(
985
            [
986
                'items' => static function ($q) use ($aliasMethod) {
987
                    /** @var $q ActiveQuery */
988
                    $q->orderBy('item.id');
989
                    $q->joinWith(['category c']);
990
991
                    if ($aliasMethod === 'explicit') {
992
                        $q->where('{{c}}.[[id]] = 2');
993
                    } elseif ($aliasMethod === 'querysyntax') {
994
                        $q->where('{{@category}}.[[id]] = 2');
995
                    } elseif ($aliasMethod === 'applyAlias') {
996
                        $q->where([$q->applyAlias('category', 'id') => 2]);
997
                    }
998
                },
999
            ]
1000
        )->orderBy('order.id')->all();
1001
1002
        $this->assertCount(1, $orders);
1003
        $this->assertCount(3, $orders[0]->items);
0 ignored issues
show
Bug introduced by
Accessing items on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1004
        $this->assertEquals(2, $orders[0]->id);
1005
        $this->assertEquals(2, $orders[0]->items[0]->category->id);
1006
        $this->assertTrue($orders[0]->isRelationPopulated('items'));
1007
        $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category'));
1008
    }
1009
1010
    /**
1011
     * @depends testJoinWith
1012
     */
1013
    public function testJoinWithSameTable(): void
1014
    {
1015
        $this->checkFixture($this->db, 'order');
1016
1017
        /**
1018
         * join with the same table but different aliases alias is defined in the relation definition without eager
1019
         * loading
1020
         */
1021
        $query = new ActiveQuery(Order::class, $this->db);
1022
        $query->joinWith('bookItems', false)->joinWith('movieItems', false)->where(['movies.name' => 'Toy Story']);
1023
        $orders = $query->all();
1024
        $this->assertCount(
1025
            1,
1026
            $orders,
1027
            $query->createCommand()->getRawSql() . print_r($orders, true)
0 ignored issues
show
Bug introduced by
Are you sure print_r($orders, true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1027
            $query->createCommand()->getRawSql() . /** @scrutinizer ignore-type */ print_r($orders, true)
Loading history...
1028
        );
1029
        $this->assertEquals(2, $orders[0]->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1030
        $this->assertFalse($orders[0]->isRelationPopulated('bookItems'));
1031
        $this->assertFalse($orders[0]->isRelationPopulated('movieItems'));
1032
1033
        /** with eager loading */
1034
        $query = new ActiveQuery(Order::class, $this->db);
1035
        $query->joinWith('bookItems', true)->joinWith('movieItems', true)->where(['movies.name' => 'Toy Story']);
1036
        $orders = $query->all();
1037
        $this->assertCount(
1038
            1,
1039
            $orders,
1040
            $query->createCommand()->getRawSql() . print_r($orders, true)
1041
        );
1042
        $this->assertCount(0, $orders[0]->bookItems);
0 ignored issues
show
Bug introduced by
Accessing bookItems on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1043
        $this->assertCount(3, $orders[0]->movieItems);
0 ignored issues
show
Bug introduced by
Accessing movieItems on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1044
        $this->assertEquals(2, $orders[0]->id);
1045
        $this->assertTrue($orders[0]->isRelationPopulated('bookItems'));
1046
        $this->assertTrue($orders[0]->isRelationPopulated('movieItems'));
1047
1048
        /**
1049
         * join with the same table but different aliases alias is defined in the call to joinWith() without eager
1050
         * loading
1051
         */
1052
        $query = new ActiveQuery(Order::class, $this->db);
1053
        $query
1054
            ->joinWith(
1055
                [
1056
                    'itemsIndexed books' => static function ($q) {
1057
                        $q->onCondition('books.category_id = 1');
1058
                    },
1059
                ],
1060
                false
1061
            )->joinWith(
1062
                [
1063
                    'itemsIndexed movies' => static function ($q) {
1064
                        $q->onCondition('movies.category_id = 2');
1065
                    },
1066
                ],
1067
                false
1068
            )->where(['movies.name' => 'Toy Story']);
1069
        $orders = $query->all();
1070
        $this->assertCount(
1071
            1,
1072
            $orders,
1073
            $query->createCommand()->getRawSql() . print_r($orders, true)
1074
        );
1075
        $this->assertEquals(2, $orders[0]->id);
1076
        $this->assertFalse($orders[0]->isRelationPopulated('itemsIndexed'));
1077
1078
        /** with eager loading, only for one relation as it would be overwritten otherwise. */
1079
        $query = new ActiveQuery(Order::class, $this->db);
1080
        $query
1081
            ->joinWith(
1082
                [
1083
                    'itemsIndexed books' => static function ($q) {
1084
                        $q->onCondition('books.category_id = 1');
1085
                    },
1086
                ],
1087
                false
1088
            )
1089
            ->joinWith(
1090
                [
1091
                    'itemsIndexed movies' => static function ($q) {
1092
                        $q->onCondition('movies.category_id = 2');
1093
                    },
1094
                ],
1095
                true
1096
            )->where(['movies.name' => 'Toy Story']);
1097
        $orders = $query->all();
1098
        $this->assertCount(1, $orders, $query->createCommand()->getRawSql() . print_r($orders, true));
1099
        $this->assertCount(3, $orders[0]->itemsIndexed);
0 ignored issues
show
Bug introduced by
Accessing itemsIndexed on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1100
        $this->assertEquals(2, $orders[0]->id);
1101
        $this->assertTrue($orders[0]->isRelationPopulated('itemsIndexed'));
1102
1103
        /** with eager loading, and the other relation */
1104
        $query = new ActiveQuery(Order::class, $this->db);
1105
        $query
1106
            ->joinWith(
1107
                [
1108
                    'itemsIndexed books' => static function ($q) {
1109
                        $q->onCondition('books.category_id = 1');
1110
                    },
1111
                ],
1112
                true
1113
            )
1114
            ->joinWith(
1115
                [
1116
                    'itemsIndexed movies' => static function ($q) {
1117
                        $q->onCondition('movies.category_id = 2');
1118
                    },
1119
                ],
1120
                false
1121
            )
1122
            ->where(['movies.name' => 'Toy Story']);
1123
        $orders = $query->all();
1124
        $this->assertCount(1, $orders, $query->createCommand()->getRawSql() . print_r($orders, true));
1125
        $this->assertCount(0, $orders[0]->itemsIndexed);
1126
        $this->assertEquals(2, $orders[0]->id);
1127
        $this->assertTrue($orders[0]->isRelationPopulated('itemsIndexed'));
1128
    }
1129
1130
    /**
1131
     * @depends testJoinWith
1132
     */
1133
    public function testJoinWithDuplicateSimple(): void
1134
    {
1135
        $this->checkFixture($this->db, 'order');
1136
1137
        /** left join and eager loading */
1138
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1139
1140
        $orders = $orderQuery
1141
            ->innerJoinWith('customer')
1142
            ->joinWith('customer')
1143
            ->orderBy('customer.id DESC, order.id')
1144
            ->all();
1145
1146
        $this->assertCount(3, $orders);
1147
        $this->assertEquals(2, $orders[0]->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1148
        $this->assertEquals(3, $orders[1]->id);
1149
        $this->assertEquals(1, $orders[2]->id);
1150
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
1151
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
1152
        $this->assertTrue($orders[2]->isRelationPopulated('customer'));
1153
    }
1154
1155
    /**
1156
     * @depends testJoinWith
1157
     */
1158
    public function testJoinWithDuplicateCallbackFiltering(): void
1159
    {
1160
        $this->checkFixture($this->db, 'order');
1161
1162
        /** inner join filtering and eager loading */
1163
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1164
1165
        $orders = $orderQuery
1166
            ->innerJoinWith('customer')
1167
            ->joinWith([
1168
                'customer' => function ($query) {
1169
                    $query->where('{{customer}}.[[id]]=2');
1170
                },
1171
            ])->orderBy('order.id')->all();
1172
1173
        $this->assertCount(2, $orders);
1174
        $this->assertEquals(2, $orders[0]->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1175
        $this->assertEquals(3, $orders[1]->id);
1176
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
1177
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
1178
    }
1179
1180
    /**
1181
     * @depends testJoinWith
1182
     */
1183
    public function testJoinWithDuplicateCallbackFilteringConditionsOnPrimary(): void
1184
    {
1185
        $this->checkFixture($this->db, 'order');
1186
1187
        /** inner join filtering, eager loading, conditions on both primary and relation */
1188
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1189
1190
        $orders = $orderQuery
1191
            ->innerJoinWith('customer')
1192
            ->joinWith([
1193
                'customer' => function ($query) {
1194
                    $query->where(['{{customer}}.[[id]]' => 2]);
1195
                },
1196
            ])->where(['order.id' => [1, 2]])->orderBy('order.id')->all();
1197
1198
        $this->assertCount(1, $orders);
1199
        $this->assertEquals(2, $orders[0]->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1200
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
1201
    }
1202
1203
    /**
1204
     * @depends testJoinWith
1205
     */
1206
    public function testJoinWithDuplicateWithSubRelation(): void
1207
    {
1208
        $this->checkFixture($this->db, 'order');
1209
1210
        /** join with sub-relation */
1211
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1212
1213
        $orders = $orderQuery
1214
            ->innerJoinWith('items')
1215
            ->joinWith([
1216
                'items.category' => function ($q) {
1217
                    $q->where('{{category}}.[[id]] = 2');
1218
                },
1219
            ])->orderBy('order.id')->all();
1220
1221
        $this->assertCount(1, $orders);
1222
        $this->assertTrue($orders[0]->isRelationPopulated('items'));
1223
        $this->assertEquals(2, $orders[0]->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1224
        $this->assertCount(3, $orders[0]->items);
0 ignored issues
show
Bug introduced by
Accessing items on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1225
        $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category'));
1226
        $this->assertEquals(2, $orders[0]->items[0]->category->id);
1227
    }
1228
1229
    /**
1230
     * @depends testJoinWith
1231
     */
1232
    public function testJoinWithDuplicateTableAlias1(): void
1233
    {
1234
        $this->checkFixture($this->db, 'order');
1235
1236
        /** join with table alias */
1237
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1238
1239
        $orders = $orderQuery
1240
            ->innerJoinWith('customer')
1241
            ->joinWith([
1242
                'customer' => function ($q) {
1243
                    $q->from('customer c');
1244
                },
1245
            ])->orderBy('c.id DESC, order.id')->all();
1246
1247
        $this->assertCount(3, $orders);
1248
        $this->assertEquals(2, $orders[0]->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1249
        $this->assertEquals(3, $orders[1]->id);
1250
        $this->assertEquals(1, $orders[2]->id);
1251
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
1252
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
1253
        $this->assertTrue($orders[2]->isRelationPopulated('customer'));
1254
    }
1255
1256
    /**
1257
     * @depends testJoinWith
1258
     */
1259
    public function testJoinWithDuplicateTableAlias2(): void
1260
    {
1261
        $this->checkFixture($this->db, 'order');
1262
1263
        /** join with table alias */
1264
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1265
1266
        $orders = $orderQuery
1267
            ->innerJoinWith('customer')
1268
            ->joinWith('customer as c')
1269
            ->orderBy('c.id DESC, order.id')
1270
            ->all();
1271
1272
        $this->assertCount(3, $orders);
1273
        $this->assertEquals(2, $orders[0]->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1274
        $this->assertEquals(3, $orders[1]->id);
1275
        $this->assertEquals(1, $orders[2]->id);
1276
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
1277
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
1278
        $this->assertTrue($orders[2]->isRelationPopulated('customer'));
1279
    }
1280
1281
    /**
1282
     * @depends testJoinWith
1283
     */
1284
    public function testJoinWithDuplicateTableAliasSubRelation(): void
1285
    {
1286
        $this->checkFixture($this->db, 'order');
1287
1288
        /** join with table alias sub-relation */
1289
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1290
1291
        $orders = $orderQuery
1292
            ->innerJoinWith([
1293
                'items as t' => function ($q) {
1294
                    $q->orderBy('t.id');
1295
                },
1296
            ])
1297
            ->joinWith([
1298
                'items.category as c' => function ($q) {
1299
                    $q->where('{{c}}.[[id]] = 2');
1300
                },
1301
            ])->orderBy('order.id')->all();
1302
1303
        $this->assertCount(1, $orders);
1304
        $this->assertTrue($orders[0]->isRelationPopulated('items'));
1305
        $this->assertEquals(2, $orders[0]->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1306
        $this->assertCount(3, $orders[0]->items);
0 ignored issues
show
Bug introduced by
Accessing items on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1307
        $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category'));
1308
        $this->assertEquals(2, $orders[0]->items[0]->category->id);
1309
    }
1310
1311
    /**
1312
     * @depends testJoinWith
1313
     */
1314
    public function testJoinWithDuplicateSubRelationCalledInsideClosure(): void
1315
    {
1316
        $this->checkFixture($this->db, 'order');
1317
1318
        /** join with sub-relation called inside Closure */
1319
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1320
1321
        $orders = $orderQuery
1322
            ->innerJoinWith('items')
1323
            ->joinWith([
1324
                'items' => function ($q) {
1325
                    $q->orderBy('item.id');
1326
                    $q->joinWith([
1327
                        'category' => function ($q) {
1328
                            $q->where('{{category}}.[[id]] = 2');
1329
                        },
1330
                    ]);
1331
                },
1332
            ])
1333
            ->orderBy('order.id')
1334
            ->all();
1335
1336
        $this->assertCount(1, $orders);
1337
        $this->assertTrue($orders[0]->isRelationPopulated('items'));
1338
        $this->assertEquals(2, $orders[0]->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1339
        $this->assertCount(3, $orders[0]->items);
0 ignored issues
show
Bug introduced by
Accessing items on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1340
        $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category'));
1341
        $this->assertEquals(2, $orders[0]->items[0]->category->id);
1342
    }
1343
1344
    public function testAlias(): void
1345
    {
1346
        $this->checkFixture($this->db, 'order');
1347
1348
        $order = new Order($this->db);
0 ignored issues
show
Unused Code introduced by
The assignment to $order is dead and can be removed.
Loading history...
1349
1350
        $query = new ActiveQuery(Order::class, $this->db);
1351
        $this->assertNull($query->getFrom());
1352
1353
        $query->alias('o');
1354
        $this->assertEquals(['o' => Order::tableName()], $query->getFrom());
1355
1356
        $query->alias('o')->alias('ord');
1357
        $this->assertEquals(['ord' => Order::tableName()], $query->getFrom());
1358
1359
        $query->from(['users', 'o' => Order::tableName()])->alias('ord');
1360
        $this->assertEquals(['users', 'ord' => Order::tableName()], $query->getFrom());
1361
    }
1362
1363
    public function testInverseOf(): void
1364
    {
1365
        $this->checkFixture($this->db, 'customer');
1366
1367
        /** eager loading: find one and all */
1368
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1369
        $customer = $customerQuery->with('orders2')->where(['id' => 1])->one();
1370
        $this->assertSame($customer->orders2[0]->customer2, $customer);
1371
1372
        //$customerQuery = new ActiveQuery(Customer::class, $this->db);
1373
        $customers = $customerQuery->with('orders2')->where(['id' => [1, 3]])->all();
1374
        $this->assertEmpty($customers[1]->orders2);
0 ignored issues
show
Bug introduced by
Accessing orders2 on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1375
        $this->assertSame($customers[0]->orders2[0]->customer2, $customers[0]);
1376
1377
        /** lazy loading */
1378
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1379
        $customer = $customerQuery->findOne(2);
1380
        $orders = $customer->orders2;
1381
        $this->assertCount(2, $orders);
1382
        $this->assertSame($customer->orders2[0]->customer2, $customer);
1383
        $this->assertSame($customer->orders2[1]->customer2, $customer);
1384
1385
        /** ad-hoc lazy loading */
1386
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1387
        $customer = $customerQuery->findOne(2);
1388
        $orders = $customer->getOrders2()->all();
0 ignored issues
show
Bug introduced by
The method getOrders2() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. It seems like you code against a sub-type of Yiisoft\ActiveRecord\ActiveRecordInterface such as Yiisoft\ActiveRecord\Tes...s\ActiveRecord\Customer. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1388
        $orders = $customer->/** @scrutinizer ignore-call */ getOrders2()->all();
Loading history...
1389
        $this->assertCount(2, $orders);
1390
        $this->assertSame($orders[0]->customer2, $customer);
1391
        $this->assertSame($orders[1]->customer2, $customer);
1392
        $this->assertTrue(
1393
            $orders[0]->isRelationPopulated('customer2'),
1394
            'inverse relation did not populate the relation'
1395
        );
1396
        $this->assertTrue(
1397
            $orders[1]->isRelationPopulated('customer2'),
1398
            'inverse relation did not populate the relation'
1399
        );
1400
1401
        /** the other way around */
1402
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1403
        $customer = $customerQuery->with('orders2')->where(['id' => 1])->asArray()->one();
1404
        $this->assertSame($customer['orders2'][0]['customer2']['id'], $customer['id']);
1405
1406
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1407
        $customers = $customerQuery->with('orders2')->where(['id' => [1, 3]])->asArray()->all();
1408
        $this->assertSame($customer['orders2'][0]['customer2']['id'], $customers[0]['id']);
1409
        $this->assertEmpty($customers[1]['orders2']);
1410
1411
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1412
        $orders = $orderQuery->with('customer2')->where(['id' => 1])->all();
1413
        $this->assertSame($orders[0]->customer2->orders2, [$orders[0]]);
0 ignored issues
show
Bug introduced by
Accessing customer2 on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1414
1415
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1416
        $order = $orderQuery->with('customer2')->where(['id' => 1])->one();
1417
        $this->assertSame($order->customer2->orders2, [$order]);
1418
1419
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1420
        $orders = $orderQuery->with('customer2')->where(['id' => 1])->asArray()->all();
1421
        $this->assertSame($orders[0]['customer2']['orders2'][0]['id'], $orders[0]['id']);
1422
1423
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1424
        $order = $orderQuery->with('customer2')->where(['id' => 1])->asArray()->one();
1425
        $this->assertSame($order['customer2']['orders2'][0]['id'], $orders[0]['id']);
1426
1427
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1428
        $orders = $orderQuery->with('customer2')->where(['id' => [1, 3]])->all();
1429
        $this->assertSame($orders[0]->customer2->orders2, [$orders[0]]);
1430
        $this->assertSame($orders[1]->customer2->orders2, [$orders[1]]);
1431
1432
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1433
        $orders = $orderQuery->with('customer2')->where(['id' => [2, 3]])->orderBy('id')->all();
1434
        $this->assertSame($orders[0]->customer2->orders2, $orders);
1435
        $this->assertSame($orders[1]->customer2->orders2, $orders);
1436
1437
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1438
        $orders = $orderQuery->with('customer2')->where(['id' => [2, 3]])->orderBy('id')->asArray()->all();
1439
        $this->assertSame($orders[0]['customer2']['orders2'][0]['id'], $orders[0]['id']);
1440
        $this->assertSame($orders[0]['customer2']['orders2'][1]['id'], $orders[1]['id']);
1441
        $this->assertSame($orders[1]['customer2']['orders2'][0]['id'], $orders[0]['id']);
1442
        $this->assertSame($orders[1]['customer2']['orders2'][1]['id'], $orders[1]['id']);
1443
    }
1444
1445
    public function testUnlinkAllViaTable(): void
1446
    {
1447
        $this->checkFixture($this->db, 'order');
1448
1449
        $this->loadFixture($this->db);
1450
1451
        /** via table with delete. */
1452
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1453
        $order = $orderQuery->findOne(1);
1454
        $this->assertCount(2, $order->booksViaTable);
0 ignored issues
show
Bug introduced by
Accessing booksViaTable on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1455
1456
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
1457
        $orderItemCount = $orderItemQuery->count();
1458
1459
        $itemQuery = new ActiveQuery(Item::class, $this->db);
1460
        $this->assertEquals(5, $itemQuery->count());
1461
1462
        $order->unlinkAll('booksViaTable', true);
0 ignored issues
show
Bug introduced by
The method unlinkAll() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. Did you maybe mean unlink()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1462
        $order->/** @scrutinizer ignore-call */ 
1463
                unlinkAll('booksViaTable', true);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1463
        $this->assertEquals(5, $itemQuery->count());
1464
        $this->assertEquals($orderItemCount - 2, $orderItemQuery->count());
1465
        $this->assertCount(0, $order->booksViaTable);
1466
1467
        /** via table without delete */
1468
        $this->assertCount(2, $order->booksWithNullFKViaTable);
0 ignored issues
show
Bug introduced by
Accessing booksWithNullFKViaTable on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1469
1470
        $orderItemsWithNullFKQuery = new ActiveQuery(OrderItemWithNullFK::class, $this->db);
1471
        $orderItemCount = $orderItemsWithNullFKQuery->count();
1472
        $this->assertEquals(5, $itemQuery->count());
1473
1474
        $order->unlinkAll('booksWithNullFKViaTable', false);
1475
        $this->assertCount(0, $order->booksWithNullFKViaTable);
1476
        $this->assertEquals(2, $orderItemsWithNullFKQuery->where(
1477
            ['AND', ['item_id' => [1, 2]], ['order_id' => null]]
1478
        )->count());
1479
1480
        $orderItemsWithNullFKQuery = new ActiveQuery(OrderItemWithNullFK::class, $this->db);
1481
        $this->assertEquals($orderItemCount, $orderItemsWithNullFKQuery->count());
1482
        $this->assertEquals(5, $itemQuery->count());
1483
    }
1484
1485
    public function testIssues(): void
1486
    {
1487
        $this->checkFixture($this->db, 'category', true);
1488
1489
        /** {@see https://github.com/yiisoft/yii2/issues/4938} */
1490
        $categoryQuery = new ActiveQuery(Category::class, $this->db);
1491
        $category = $categoryQuery->findOne(2);
1492
        $this->assertInstanceOf(Category::class, $category);
1493
        $this->assertEquals(3, $category->getItems()->count());
0 ignored issues
show
Bug introduced by
The method getItems() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. It seems like you code against a sub-type of Yiisoft\ActiveRecord\ActiveRecordInterface such as Yiisoft\ActiveRecord\Tes...tubs\ActiveRecord\Order or Yiisoft\ActiveRecord\Tes...s\ActiveRecord\Category. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1493
        $this->assertEquals(3, $category->/** @scrutinizer ignore-call */ getItems()->count());
Loading history...
1494
        $this->assertEquals(1, $category->getLimitedItems()->count());
0 ignored issues
show
Bug introduced by
The method getLimitedItems() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. It seems like you code against a sub-type of Yiisoft\ActiveRecord\ActiveRecordInterface such as Yiisoft\ActiveRecord\Tes...tubs\ActiveRecord\Order or Yiisoft\ActiveRecord\Tes...s\ActiveRecord\Category. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1494
        $this->assertEquals(1, $category->/** @scrutinizer ignore-call */ getLimitedItems()->count());
Loading history...
1495
        $this->assertEquals(1, $category->getLimitedItems()->distinct(true)->count());
1496
1497
        /** {@see https://github.com/yiisoft/yii2/issues/3197} */
1498
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1499
        $orders = $orderQuery->with('orderItems')->orderBy('id')->all();
1500
        $this->assertCount(3, $orders);
1501
        $this->assertCount(2, $orders[0]->orderItems);
0 ignored issues
show
Bug introduced by
Accessing orderItems on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1502
        $this->assertCount(3, $orders[1]->orderItems);
1503
        $this->assertCount(1, $orders[2]->orderItems);
1504
1505
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1506
        $orders = $orderQuery->with(
1507
            [
1508
                'orderItems' => static function ($q) {
1509
                    $q->indexBy('item_id');
1510
                },
1511
            ]
1512
        )->orderBy('id')->all();
1513
        $this->assertCount(3, $orders);
1514
        $this->assertCount(2, $orders[0]->orderItems);
1515
        $this->assertCount(3, $orders[1]->orderItems);
1516
        $this->assertCount(1, $orders[2]->orderItems);
1517
1518
        /** {@see https://github.com/yiisoft/yii2/issues/8149} */
1519
        $arClass = new Customer($this->db);
1520
1521
        $arClass->name = 'test';
1522
        $arClass->email = 'test';
1523
        $arClass->save();
1524
1525
        $arClass->updateCounters(['status' => 1]);
1526
        $this->assertEquals(1, $arClass->status);
1527
    }
1528
1529
    public function testPopulateWithoutPk(): void
1530
    {
1531
        $this->checkFixture($this->db, 'customer', true);
1532
1533
        /** tests with single pk asArray */
1534
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1535
        $aggregation = $customerQuery
1536
            ->select(['{{customer}}.[[status]]', 'SUM({{order}}.[[total]]) AS [[sumtotal]]'])
1537
            ->joinWith('ordersPlain', false)
1538
            ->groupBy('{{customer}}.[[status]]')
1539
            ->orderBy('status')
1540
            ->asArray()
1541
            ->all();
1542
1543
        $expected = [
1544
            [
1545
                'status' => 1,
1546
                'sumtotal' => 183,
1547
            ],
1548
            [
1549
                'status' => 2,
1550
                'sumtotal' => 0,
1551
            ],
1552
        ];
1553
1554
        $this->assertEquals($expected, $aggregation);
1555
1556
        /** tests with single pk with Models */
1557
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1558
        $aggregation = $customerQuery
1559
            ->select(['{{customer}}.[[status]]', 'SUM({{order}}.[[total]]) AS [[sumTotal]]'])
1560
            ->joinWith('ordersPlain', false)
1561
            ->groupBy('{{customer}}.[[status]]')
1562
            ->orderBy('status')
1563
            ->all();
1564
1565
        $this->assertCount(2, $aggregation);
1566
        $this->assertContainsOnlyInstancesOf(Customer::class, $aggregation);
1567
1568
        foreach ($aggregation as $item) {
1569
            if ($item->status === 1) {
0 ignored issues
show
Bug introduced by
Accessing status on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1570
                $this->assertEquals(183, $item->sumTotal);
0 ignored issues
show
Bug introduced by
Accessing sumTotal on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1571
            } elseif ($item->status === 2) {
1572
                $this->assertEquals(0, $item->sumTotal);
1573
            }
1574
        }
1575
1576
        /** tests with composite pk asArray */
1577
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
1578
        $aggregation = $orderItemQuery
1579
            ->select(['[[order_id]]', 'SUM([[subtotal]]) AS [[subtotal]]'])
1580
            ->joinWith('order', false)
1581
            ->groupBy('[[order_id]]')
1582
            ->orderBy('[[order_id]]')
1583
            ->asArray()
1584
            ->all();
1585
1586
        $expected = [
1587
            [
1588
                'order_id' => 1,
1589
                'subtotal' => 70,
1590
            ],
1591
            [
1592
                'order_id' => 2,
1593
                'subtotal' => 33,
1594
            ],
1595
            [
1596
                'order_id' => 3,
1597
                'subtotal' => 40,
1598
            ],
1599
        ];
1600
        $this->assertEquals($expected, $aggregation);
1601
1602
        /** tests with composite pk with Models */
1603
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
1604
        $aggregation = $orderItemQuery
1605
            ->select(['[[order_id]]', 'SUM([[subtotal]]) AS [[subtotal]]'])
1606
            ->joinWith('order', false)
1607
            ->groupBy('[[order_id]]')
1608
            ->orderBy('[[order_id]]')
1609
            ->all();
1610
1611
        $this->assertCount(3, $aggregation);
1612
        $this->assertContainsOnlyInstancesOf(OrderItem::class, $aggregation);
1613
1614
        foreach ($aggregation as $item) {
1615
            if ($item->order_id === 1) {
0 ignored issues
show
Bug introduced by
Accessing order_id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1616
                $this->assertEquals(70, $item->subtotal);
0 ignored issues
show
Bug introduced by
Accessing subtotal on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1617
            } elseif ($item->order_id === 2) {
1618
                $this->assertEquals(33, $item->subtotal);
1619
            } elseif ($item->order_id === 3) {
1620
                $this->assertEquals(40, $item->subtotal);
1621
            }
1622
        }
1623
    }
1624
1625
    public function testLinkWhenRelationIsIndexed2(): void
1626
    {
1627
        $this->checkFixture($this->db, 'order');
1628
1629
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1630
        $order = $orderQuery->with('orderItems2')->where(['id' => 1])->one();
1631
1632
        $orderItem = new OrderItem($this->db);
1633
1634
        $orderItem->order_id = $order->id;
1635
        $orderItem->item_id = 3;
1636
        $orderItem->quantity = 1;
1637
        $orderItem->subtotal = 10.0;
1638
1639
        $order->link('orderItems2', $orderItem);
1640
        $this->assertTrue(isset($order->orderItems2['3']));
1641
    }
1642
1643
    public function testEmulateExecution(): void
1644
    {
1645
        $this->checkFixture($this->db, 'order');
1646
1647
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1648
1649
        $this->assertGreaterThan(0, $customerQuery->count());
1650
        $this->assertSame([], $customerQuery->emulateExecution()->all());
1651
        $this->assertNull($customerQuery->emulateExecution()->one());
1652
        $this->assertFalse($customerQuery->emulateExecution()->exists());
1653
        $this->assertSame(0, $customerQuery->emulateExecution()->count());
1654
        $this->assertNull($customerQuery->emulateExecution()->sum('id'));
1655
        $this->assertNull($customerQuery->emulateExecution()->average('id'));
1656
        $this->assertNull($customerQuery->emulateExecution()->max('id'));
1657
        $this->assertNull($customerQuery->emulateExecution()->min('id'));
1658
        $this->assertNull($customerQuery->select(['id'])->emulateExecution()->scalar());
1659
        $this->assertSame([], $customerQuery->select(['id'])->emulateExecution()->column());
1660
    }
1661
1662
    /**
1663
     * {@see https://github.com/yiisoft/yii2/issues/12213}
1664
     */
1665
    public function testUnlinkAllOnCondition(): void
1666
    {
1667
        $this->checkFixture($this->db, 'item');
1668
1669
        /** Ensure there are three items with category_id = 2 in the Items table */
1670
        $itemQuery = new ActiveQuery(Item::class, $this->db);
1671
        $itemsCount = $itemQuery->where(['category_id' => 2])->count();
1672
        $this->assertEquals(3, $itemsCount);
1673
1674
        $categoryQuery = new ActiveQuery(Category::class, $this->db);
1675
        $categoryQuery = $categoryQuery->with('limitedItems')->where(['id' => 2]);
1676
1677
        /**
1678
         * Ensure that limitedItems relation returns only one item (category_id = 2 and id in (1,2,3))
1679
         */
1680
        $category = $categoryQuery->one();
1681
        $this->assertCount(1, $category->limitedItems);
1682
1683
        /** Unlink all items in the limitedItems relation */
1684
        $category->unlinkAll('limitedItems', true);
1685
1686
        /** Make sure that only one item was unlinked */
1687
        $itemsCount = $itemQuery->where(['category_id' => 2])->count();
1688
        $this->assertEquals(2, $itemsCount);
1689
1690
        /** Call $categoryQuery again to ensure no items were found */
1691
        $this->assertCount(0, $categoryQuery->one()->limitedItems);
1692
    }
1693
1694
    /**
1695
     * {@see https://github.com/yiisoft/yii2/issues/12213}
1696
     */
1697
    public function testUnlinkAllOnConditionViaTable(): void
1698
    {
1699
        $this->checkFixture($this->db, 'item', true);
1700
1701
        /** Ensure there are three items with category_id = 2 in the Items table */
1702
        $itemQuery = new ActiveQuery(Item::class, $this->db);
1703
        $itemsCount = $itemQuery->where(['category_id' => 2])->count();
1704
        $this->assertEquals(3, $itemsCount);
1705
1706
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1707
        $orderQuery = $orderQuery->with('limitedItems')->where(['id' => 2]);
1708
1709
        /**
1710
         * Ensure that limitedItems relation returns only one item (category_id = 2 and id in (4, 5)).
1711
         */
1712
        $category = $orderQuery->one();
1713
        $this->assertCount(2, $category->limitedItems);
1714
1715
        /** Unlink all items in the limitedItems relation */
1716
        $category->unlinkAll('limitedItems', true);
1717
1718
        /** Call $orderQuery again to ensure that links are removed */
1719
        $this->assertCount(0, $orderQuery->one()->limitedItems);
1720
1721
        /** Make sure that only links were removed, the items were not removed */
1722
        $this->assertEquals(3, $itemQuery->where(['category_id' => 2])->count());
1723
    }
1724
1725
    /**
1726
     * {@see https://github.com/yiisoft/yii2/pull/13891}
1727
     */
1728
    public function testIndexByAfterLoadingRelations(): void
1729
    {
1730
        $this->checkFixture($this->db, 'order');
1731
1732
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1733
        $orderQuery->with('customer')->indexBy(function (Order $order) {
1734
            $this->assertTrue($order->isRelationPopulated('customer'));
1735
            $this->assertNotEmpty($order->customer->id);
0 ignored issues
show
Bug Best Practice introduced by
The property customer does not exist on Yiisoft\ActiveRecord\Tes...tubs\ActiveRecord\Order. Since you implemented __get, consider adding a @property annotation.
Loading history...
1736
1737
            return $order->customer->id;
1738
        })->all();
1739
1740
        $orders = $orderQuery->with('customer')->indexBy('customer.id')->all();
1741
1742
        foreach ($orders as $customer_id => $order) {
1743
            $this->assertEquals($customer_id, $order->customer_id);
0 ignored issues
show
Bug introduced by
Accessing customer_id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1744
        }
1745
    }
1746
1747
    public function filterTableNamesFromAliasesProvider(): array
1748
    {
1749
        return [
1750
            'table name as string' => ['customer', []],
1751
            'table name as array' => [['customer'], []],
1752
            'table names' => [['customer', 'order'], []],
1753
            'table name and a table alias' => [['customer', 'ord' => 'order'], ['ord']],
1754
            'table alias' => [['csr' => 'customer'], ['csr']],
1755
            'table aliases' => [['csr' => 'customer', 'ord' => 'order'], ['csr', 'ord']],
1756
        ];
1757
    }
1758
1759
    /**
1760
     * @dataProvider filterTableNamesFromAliasesProvider
1761
     *
1762
     * @param $expectedAliases
1763
     *
1764
     * @throws ReflectionException
1765
     */
1766
    public function testFilterTableNamesFromAliases(array|string $fromParams, array $expectedAliases): void
1767
    {
1768
        $this->checkFixture($this->db, 'customer');
1769
1770
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1771
1772
        $query = $customerQuery->from($fromParams);
1773
1774
        $aliases = $this->invokeMethod(new Customer($this->db), 'filterValidAliases', [$query]);
1775
1776
        $this->assertEquals($expectedAliases, $aliases);
1777
    }
1778
1779
    public function testExtraFields(): void
1780
    {
1781
        $this->checkFixture($this->db, 'customer');
1782
1783
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1784
1785
        $query = $customerQuery->with('orders2')->where(['id' => 1])->one();
1786
        $this->assertCount(1, $query->getRelatedRecords());
1787
        $this->assertCount(1, $query->extraFields());
1788
        $this->assertArrayHasKey('orders2', $query->getRelatedRecords());
1789
        $this->assertContains('orders2', $query->extraFields());
1790
    }
1791
1792
    public function tableNameProvider(): array
1793
    {
1794
        return [
1795
            ['order', 'order_item'],
1796
            ['order', '{{%order_item}}'],
1797
            ['{{%order}}', 'order_item'],
1798
            ['{{%order}}', '{{%order_item}}'],
1799
        ];
1800
    }
1801
1802
    /**
1803
     * Test whether conditions are quoted correctly in conditions where joinWith is used.
1804
     *
1805
     * {@see https://github.com/yiisoft/yii2/issues/11088}
1806
     *
1807
     * @dataProvider tableNameProvider
1808
     *
1809
     * @param string $orderTableName
1810
     * @param string $orderItemTableName
1811
     *
1812
     * @throws Exception|InvalidConfigException
1813
     */
1814
    public function testRelationWhereParams(string $orderTableName, string $orderItemTableName): void
1815
    {
1816
        $this->checkFixture($this->db, 'order');
1817
1818
        $order = new Order($this->db);
1819
        $orderItem = new OrderItem($this->db);
1820
1821
        $order->setTableName($orderTableName);
1822
        $orderItem->setTableName($orderItemTableName);
1823
1824
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1825
        $order = $orderQuery->findOne(1);
1826
        $itemsSQL = $order->getOrderitems()->createCommand()->getRawSql();
0 ignored issues
show
Bug introduced by
The method getOrderitems() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. It seems like you code against a sub-type of Yiisoft\ActiveRecord\ActiveRecordInterface such as Yiisoft\ActiveRecord\Tes...s\ActiveRecord\Customer or Yiisoft\ActiveRecord\Tes...tubs\ActiveRecord\Order or Yiisoft\ActiveRecord\Tes...s\ActiveRecord\Category. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1826
        $itemsSQL = $order->/** @scrutinizer ignore-call */ getOrderitems()->createCommand()->getRawSql();
Loading history...
1827
        $expectedSQL = $this->replaceQuotes('SELECT * FROM [[order_item]] WHERE [[order_id]]=1');
1828
        $this->assertEquals($expectedSQL, $itemsSQL);
1829
1830
        $order = $orderQuery->findOne(1);
1831
        $itemsSQL = $order->getOrderItems()->joinWith('item')->createCommand()->getRawSql();
0 ignored issues
show
Bug introduced by
The method getOrderItems() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. It seems like you code against a sub-type of Yiisoft\ActiveRecord\ActiveRecordInterface such as Yiisoft\ActiveRecord\Tes...s\ActiveRecord\Customer or Yiisoft\ActiveRecord\Tes...tubs\ActiveRecord\Order or Yiisoft\ActiveRecord\Tes...s\ActiveRecord\Category. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1831
        $itemsSQL = $order->/** @scrutinizer ignore-call */ getOrderItems()->joinWith('item')->createCommand()->getRawSql();
Loading history...
1832
        $expectedSQL = $this->replaceQuotes(
1833
            'SELECT [[order_item]].* FROM [[order_item]] LEFT JOIN [[item]] ' .
1834
            'ON [[order_item]].[[item_id]] = [[item]].[[id]] WHERE [[order_item]].[[order_id]]=1'
1835
        );
1836
        $this->assertEquals($expectedSQL, $itemsSQL);
1837
1838
        $order->setTableName(null);
0 ignored issues
show
Bug introduced by
The method setTableName() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. It seems like you code against a sub-type of Yiisoft\ActiveRecord\ActiveRecordInterface such as Yiisoft\ActiveRecord\Tes...\ActiveRecord\OrderItem or Yiisoft\ActiveRecord\Tes...tubs\ActiveRecord\Order. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1838
        $order->/** @scrutinizer ignore-call */ 
1839
                setTableName(null);
Loading history...
1839
        $orderItem->setTableName(null);
1840
    }
1841
1842
    public function testOutdatedRelationsAreResetForExistingRecords(): void
1843
    {
1844
        $this->checkFixture($this->db, 'order_item', true);
1845
1846
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
1847
        $orderItems = $orderItemQuery->findOne(1);
1848
        $this->assertEquals(1, $orderItems->order->id);
0 ignored issues
show
Bug introduced by
Accessing order on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1849
        $this->assertEquals(1, $orderItems->item->id);
0 ignored issues
show
Bug introduced by
Accessing item on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1850
1851
        /** test `__set()`. */
1852
        $orderItems->order_id = 2;
0 ignored issues
show
Bug introduced by
Accessing order_id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1853
        $orderItems->item_id = 1;
0 ignored issues
show
Bug introduced by
Accessing item_id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1854
        $this->assertEquals(2, $orderItems->order->id);
1855
        $this->assertEquals(1, $orderItems->item->id);
1856
1857
        /** Test `setAttribute()`. */
1858
        $orderItems->setAttribute('order_id', 3);
1859
        $orderItems->setAttribute('item_id', 1);
1860
        $this->assertEquals(3, $orderItems->order->id);
1861
        $this->assertEquals(1, $orderItems->item->id);
1862
    }
1863
1864
    public function testOutdatedCompositeKeyRelationsAreReset(): void
1865
    {
1866
        $this->checkFixture($this->db, 'dossier');
1867
1868
        $dossierQuery = new ActiveQuery(Dossier::class, $this->db);
1869
1870
        $dossiers = $dossierQuery->findOne(['department_id' => 1, 'employee_id' => 1]);
1871
        $this->assertEquals('John Doe', $dossiers->employee->fullName);
0 ignored issues
show
Bug introduced by
Accessing employee on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1872
1873
        $dossiers->department_id = 2;
0 ignored issues
show
Bug introduced by
Accessing department_id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1874
        $this->assertEquals('Ann Smith', $dossiers->employee->fullName);
1875
1876
        $dossiers->employee_id = 2;
0 ignored issues
show
Bug introduced by
Accessing employee_id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1877
        $this->assertEquals('Will Smith', $dossiers->employee->fullName);
1878
1879
        unset($dossiers->employee_id);
1880
        $this->assertNull($dossiers->employee);
1881
1882
        $dossier = new Dossier($this->db);
1883
        $this->assertNull($dossier->employee);
1884
1885
        $dossier->employee_id = 1;
1886
        $dossier->department_id = 2;
1887
        $this->assertEquals('Ann Smith', $dossier->employee->fullName);
1888
1889
        $dossier->employee_id = 2;
1890
        $this->assertEquals('Will Smith', $dossier->employee->fullName);
1891
    }
1892
1893
    public function testOutdatedViaTableRelationsAreReset(): void
1894
    {
1895
        $this->checkFixture($this->db, 'order');
1896
1897
        $this->loadFixture($this->db);
1898
1899
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1900
1901
        $orders = $orderQuery->findOne(1);
1902
        $orderItemIds = ArrayHelper::getColumn($orders->items, 'id');
0 ignored issues
show
Bug introduced by
Accessing items on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1903
        sort($orderItemIds);
1904
        $this->assertSame([1, 2], $orderItemIds);
1905
1906
        $orders->id = 2;
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1907
        sort($orderItemIds);
1908
        $orderItemIds = ArrayHelper::getColumn($orders->items, 'id');
1909
        $this->assertSame([3, 4, 5], $orderItemIds);
1910
1911
        unset($orders->id);
1912
        $this->assertSame([], $orders->items);
1913
1914
        $order = new Order($this->db);
1915
        $this->assertSame([], $order->items);
0 ignored issues
show
Bug Best Practice introduced by
The property items does not exist on Yiisoft\ActiveRecord\Tes...tubs\ActiveRecord\Order. Since you implemented __get, consider adding a @property annotation.
Loading history...
1916
1917
        $order->id = 3;
1918
        $orderItemIds = ArrayHelper::getColumn($order->items, 'id');
0 ignored issues
show
Bug introduced by
It seems like $order->items can also be of type null; however, parameter $array of Yiisoft\Arrays\ArrayHelper::getColumn() does only seem to accept iterable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1918
        $orderItemIds = ArrayHelper::getColumn(/** @scrutinizer ignore-type */ $order->items, 'id');
Loading history...
1919
        $this->assertSame([2], $orderItemIds);
1920
    }
1921
1922
    public function testInverseOfDynamic(): void
1923
    {
1924
        $this->checkFixture($this->db, 'customer');
1925
1926
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1927
1928
        $customer = $customerQuery->findOne(1);
1929
1930
        /** request the inverseOf relation without explicitly (eagerly) loading it */
1931
        $orders2 = $customer->getOrders2()->all();
1932
        $this->assertSame($customer, $orders2[0]->customer2);
1933
1934
        $orders2 = $customer->getOrders2()->one();
1935
        $this->assertSame($customer, $orders2->customer2);
1936
1937
        /**
1938
         * request the inverseOf relation while also explicitly eager loading it (while possible, this is of course
1939
         * redundant)
1940
         */
1941
        $orders2 = $customer->getOrders2()->with('customer2')->all();
1942
        $this->assertSame($customer, $orders2[0]->customer2);
1943
1944
        $orders2 = $customer->getOrders2()->with('customer2')->one();
1945
        $this->assertSame($customer, $orders2->customer2);
1946
1947
        /** request the inverseOf relation as array */
1948
        $orders2 = $customer->getOrders2()->asArray()->all();
1949
        $this->assertEquals($customer['id'], $orders2[0]['customer2']['id']);
1950
1951
        $orders2 = $customer->getOrders2()->asArray()->one();
1952
        $this->assertEquals($customer['id'], $orders2['customer2']['id']);
1953
    }
1954
1955
    public function testOptimisticLock(): void
1956
    {
1957
        $this->checkFixture($this->db, 'document');
1958
1959
        $documentQuery = new ActiveQuery(Document::class, $this->db);
1960
        $record = $documentQuery->findOne(1);
1961
1962
        $record->content = 'New Content';
0 ignored issues
show
Bug introduced by
Accessing content on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1963
        $record->save();
1964
        $this->assertEquals(1, $record->version);
0 ignored issues
show
Bug introduced by
Accessing version on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1965
1966
        $record = $documentQuery->findOne(1);
1967
1968
        $record->content = 'Rewrite attempt content';
1969
        $record->version = 0;
1970
        $this->expectException(StaleObjectException::class);
1971
        $record->save();
1972
    }
1973
1974
    /**
1975
     * {@see https://github.com/yiisoft/yii2/issues/9006}
1976
     */
1977
    public function testBit(): void
1978
    {
1979
        $this->checkFixture($this->db, 'bit_values');
1980
1981
        $bitValueQuery = new ActiveQuery(BitValues::class, $this->db);
1982
        $falseBit = $bitValueQuery->findOne(1);
1983
        $this->assertEquals(false, $falseBit->val);
0 ignored issues
show
Bug introduced by
Accessing val on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1984
1985
        $bitValueQuery = new ActiveQuery(BitValues::class, $this->db);
1986
        $trueBit = $bitValueQuery->findOne(2);
1987
        $this->assertEquals(true, $trueBit->val);
1988
    }
1989
1990
    public function testUpdateAttributes(): void
1991
    {
1992
        $this->checkFixture($this->db, 'order');
1993
1994
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1995
        $order = $orderQuery->findOne(1);
1996
        $newTotal = 978;
1997
        $this->assertSame(1, $order->updateAttributes(['total' => $newTotal]));
0 ignored issues
show
Bug introduced by
The method updateAttributes() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. Did you maybe mean update()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1997
        $this->assertSame(1, $order->/** @scrutinizer ignore-call */ updateAttributes(['total' => $newTotal]));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1998
        $this->assertEquals($newTotal, $order->total);
0 ignored issues
show
Bug introduced by
Accessing total on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1999
2000
        $order = $orderQuery->findOne(1);
2001
        $this->assertEquals($newTotal, $order->total);
2002
2003
        /** @see https://github.com/yiisoft/yii2/issues/12143 */
2004
        $newOrder = new Order($this->db);
2005
        $this->assertTrue($newOrder->getIsNewRecord());
2006
2007
        $newTotal = 200;
2008
        $this->assertSame(0, $newOrder->updateAttributes(['total' => $newTotal]));
2009
        $this->assertTrue($newOrder->getIsNewRecord());
2010
        $this->assertEquals($newTotal, $newOrder->total);
2011
    }
2012
2013
    /**
2014
     * Ensure no ambiguous column error occurs if ActiveQuery adds a JOIN.
2015
     *
2016
     * {@see https://github.com/yiisoft/yii2/issues/13757}
2017
     */
2018
    public function testAmbiguousColumnFindOne(): void
2019
    {
2020
        $this->checkFixture($this->db, 'customer');
2021
2022
        $customerQuery = new CustomerQuery(Customer::class, $this->db);
2023
2024
        $customerQuery->joinWithProfile = true;
2025
2026
        $arClass = $customerQuery->findOne(1);
2027
2028
        $this->assertTrue($arClass->refresh());
0 ignored issues
show
Bug introduced by
The method refresh() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Yiisoft\ActiveRecord\ActiveRecordInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2028
        $this->assertTrue($arClass->/** @scrutinizer ignore-call */ refresh());
Loading history...
2029
2030
        $customerQuery->joinWithProfile = false;
2031
    }
2032
2033
    public function testCustomARRelation(): void
2034
    {
2035
        $this->checkFixture($this->db, 'order_item');
2036
2037
        $orderItem = new ActiveQuery(OrderItem::class, $this->db);
2038
2039
        $orderItem = $orderItem->findOne(1);
2040
2041
        $this->assertInstanceOf(Order::class, $orderItem->custom);
0 ignored issues
show
Bug introduced by
Accessing custom on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2042
    }
2043
2044
    public function testGetAttributes(): void
2045
    {
2046
        $attributesExpected = [];
2047
        $this->checkFixture($this->db, 'customer');
2048
2049
        $attributesExpected['id'] = 1;
2050
        $attributesExpected['email'] = '[email protected]';
2051
        $attributesExpected['name'] = 'user1';
2052
        $attributesExpected['address'] = 'address1';
2053
        $attributesExpected['status'] = 1;
2054
2055
        if ($this->driverName === 'pgsql') {
2056
            $attributesExpected['bool_status'] = true;
2057
        }
2058
2059
        if ($this->driverName === 'oci') {
2060
            $attributesExpected['bool_status'] = '1';
2061
        }
2062
2063
        $attributesExpected['profile_id'] = 1;
2064
2065
        $customer = new ActiveQuery(Customer::class, $this->db);
2066
2067
        $attributes = $customer->findOne(1)->getAttributes();
0 ignored issues
show
Bug introduced by
The method getAttributes() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. Did you maybe mean getAttribute()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2067
        $attributes = $customer->findOne(1)->/** @scrutinizer ignore-call */ getAttributes();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2068
2069
        $this->assertEquals($attributes, $attributesExpected);
2070
    }
2071
2072
    public function testGetAttributesExcept(): void
2073
    {
2074
        $this->checkFixture($this->db, 'customer');
2075
2076
        $customer = new ActiveQuery(Customer::class, $this->db);
2077
2078
        $attributes = $customer->findOne(1)->getAttributes(null, ['status', 'bool_status', 'profile_id']);
2079
2080
        $this->assertEquals(
2081
            $attributes,
2082
            ['id' => 1, 'email' => '[email protected]', 'name' => 'user1', 'address' => 'address1']
2083
        );
2084
    }
2085
2086
    public function testFields(): void
2087
    {
2088
        $this->checkFixture($this->db, 'order_item');
2089
2090
        $orderItem = new ActiveQuery(OrderItem::class, $this->db);
2091
2092
        $fields = $orderItem->findOne(['order_id' => 1, 'item_id' => 2])->fields();
0 ignored issues
show
Bug introduced by
The method fields() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Yiisoft\ActiveRecord\ActiveRecordInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2092
        $fields = $orderItem->findOne(['order_id' => 1, 'item_id' => 2])->/** @scrutinizer ignore-call */ fields();
Loading history...
2093
2094
        $this->assertEquals(
2095
            $fields,
2096
            ['order_id' => 1, 'item_id' => 2, 'quantity' => 2, 'subtotal' => '40', 'price' => 20]
2097
        );
2098
    }
2099
2100
    public function testGetOldAttribute(): void
2101
    {
2102
        $this->checkFixture($this->db, 'customer');
2103
2104
        $customer = new ActiveQuery(Customer::class, $this->db);
2105
2106
        $query = $customer->findOne(1);
2107
        $this->assertEquals('user1', $query->getOldAttribute('name'));
0 ignored issues
show
Bug introduced by
The method getOldAttribute() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. Did you maybe mean getAttribute()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2107
        $this->assertEquals('user1', $query->/** @scrutinizer ignore-call */ getOldAttribute('name'));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2108
        $this->assertEquals($query->getAttributes(), $query->getOldAttributes());
0 ignored issues
show
Bug introduced by
The method getOldAttributes() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. Did you maybe mean getAttribute()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2108
        $this->assertEquals($query->getAttributes(), $query->/** @scrutinizer ignore-call */ getOldAttributes());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2109
2110
        $query->setAttribute('name', 'samdark');
2111
        $this->assertEquals('samdark', $query->getAttribute('name'));
2112
        $this->assertEquals('user1', $query->getOldAttribute('name'));
2113
        $this->assertNotEquals($query->getAttribute('name'), $query->getOldAttribute('name'));
2114
    }
2115
2116
    public function testGetOldAttributes(): void
2117
    {
2118
        $attributes = [];
2119
        $attributesNew = [];
2120
        $this->checkFixture($this->db, 'customer');
2121
2122
        $attributes['id'] = 1;
2123
        $attributes['email'] = '[email protected]';
2124
        $attributes['name'] = 'user1';
2125
        $attributes['address'] = 'address1';
2126
        $attributes['status'] = 1;
2127
2128
        if ($this->driverName === 'pgsql') {
2129
            $attributes['bool_status'] = true;
2130
        }
2131
2132
        if ($this->driverName === 'oci') {
2133
            $attributes['bool_status'] = '1';
2134
        }
2135
2136
        $attributes['profile_id'] = 1;
2137
2138
        $customer = new ActiveQuery(Customer::class, $this->db);
2139
2140
        $query = $customer->findOne(1);
2141
        $this->assertEquals($query->getAttributes(), $attributes);
2142
        $this->assertEquals($query->getAttributes(), $query->getOldAttributes());
2143
2144
        $query->setAttribute('name', 'samdark');
2145
        $attributesNew['id'] = 1;
2146
        $attributesNew['email'] = '[email protected]';
2147
        $attributesNew['name'] = 'samdark';
2148
        $attributesNew['address'] = 'address1';
2149
        $attributesNew['status'] = 1;
2150
2151
        if ($this->driverName === 'pgsql') {
2152
            $attributesNew['bool_status'] = true;
2153
        }
2154
2155
        if ($this->driverName === 'oci') {
2156
            $attributesNew['bool_status'] = '1';
2157
        }
2158
2159
        $attributesNew['profile_id'] = 1;
2160
2161
        $this->assertEquals($attributesNew, $query->getAttributes());
2162
        $this->assertEquals($attributes, $query->getOldAttributes());
2163
        $this->assertNotEquals($query->getAttributes(), $query->getOldAttributes());
2164
    }
2165
2166
    public function testIsAttributeChanged(): void
2167
    {
2168
        $this->checkFixture($this->db, 'customer');
2169
2170
        $customer = new ActiveQuery(Customer::class, $this->db);
2171
2172
        $query = $customer->findOne(1);
2173
        $this->assertEquals('user1', $query->getAttribute('name'));
2174
        $this->assertEquals('user1', $query->getOldAttribute('name'));
2175
2176
        $query->setAttribute('name', 'samdark');
2177
        $this->assertEquals('samdark', $query->getAttribute('name'));
2178
        $this->assertEquals('user1', $query->getOldAttribute('name'));
2179
        $this->assertNotEquals($query->getAttribute('name'), $query->getOldAttribute('name'));
2180
        $this->assertTrue($query->isAttributeChanged('name', true));
0 ignored issues
show
Bug introduced by
The method isAttributeChanged() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Yiisoft\ActiveRecord\ActiveRecordInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2180
        $this->assertTrue($query->/** @scrutinizer ignore-call */ isAttributeChanged('name', true));
Loading history...
2181
    }
2182
2183
    public function testIsAttributeChangedNotIdentical(): void
2184
    {
2185
        $this->checkFixture($this->db, 'customer');
2186
2187
        $customer = new ActiveQuery(Customer::class, $this->db);
2188
2189
        $query = $customer->findOne(1);
2190
        $this->assertEquals('user1', $query->getAttribute('name'));
2191
        $this->assertEquals('user1', $query->getOldAttribute('name'));
2192
2193
        $query->setAttribute('name', 'samdark');
2194
        $this->assertEquals('samdark', $query->getAttribute('name'));
2195
        $this->assertEquals('user1', $query->getOldAttribute('name'));
2196
        $this->assertNotEquals($query->getAttribute('name'), $query->getOldAttribute('name'));
2197
        $this->assertTrue($query->isAttributeChanged('name', false));
2198
    }
2199
2200
    public function testCheckRelationUnknownPropertyException(): void
2201
    {
2202
        $this->checkFixture($this->db, 'customer');
2203
2204
        $customer = new ActiveQuery(Customer::class, $this->db);
2205
2206
        $query = $customer->findOne(1);
2207
2208
        $this->expectException(UnknownPropertyException::class);
2209
        $this->expectExceptionMessage('Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer::noExist');
2210
        $query->noExist;
0 ignored issues
show
Bug introduced by
Accessing noExist on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2211
    }
2212
2213
    public function testCheckRelationInvalidCallException(): void
2214
    {
2215
        $this->checkFixture($this->db, 'customer');
2216
2217
        $customer = new ActiveQuery(Customer::class, $this->db);
2218
2219
        $query = $customer->findOne(2);
2220
2221
        $this->expectException(InvalidCallException::class);
2222
        $this->expectExceptionMessage(
2223
            'Getting write-only property: Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer::ordersReadOnly'
2224
        );
2225
        $query->ordersReadOnly;
0 ignored issues
show
Bug introduced by
Accessing ordersReadOnly on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2226
    }
2227
2228
    public function testGetRelationInvalidArgumentException(): void
2229
    {
2230
        $this->checkFixture($this->db, 'customer');
2231
2232
        $customer = new ActiveQuery(Customer::class, $this->db);
2233
2234
        $query = $customer->findOne(1);
2235
2236
        /** Without throwing exception */
2237
        $this->assertEmpty($query->getRelation('items', false));
2238
2239
        /** Throwing exception */
2240
        $this->expectException(InvalidArgumentException::class);
2241
        $this->expectExceptionMessage(
2242
            'Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer has no relation named "items".'
2243
        );
2244
        $query->getRelation('items');
2245
    }
2246
2247
    public function testGetRelationInvalidArgumentExceptionHasNoRelationNamed(): void
2248
    {
2249
        $this->checkFixture($this->db, 'customer');
2250
2251
        $customer = new ActiveQuery(Customer::class, $this->db);
2252
2253
        $query = $customer->findOne(1);
2254
2255
        /** Without throwing exception */
2256
        $this->assertEmpty($query->getRelation('item', false));
2257
2258
        $this->expectException(InvalidArgumentException::class);
2259
        $this->expectExceptionMessage(
2260
            'Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer has no relation named "item"'
2261
        );
2262
        $query->getRelation('item');
2263
    }
2264
2265
    public function testGetRelationInvalidArgumentExceptionCaseSensitive(): void
2266
    {
2267
        $this->checkFixture($this->db, 'customer');
2268
2269
        $customer = new ActiveQuery(Customer::class, $this->db);
2270
2271
        $query = $customer->findOne(1);
2272
2273
        $this->assertEmpty($query->getRelation('expensiveorders', false));
2274
2275
        $this->expectException(InvalidArgumentException::class);
2276
        $this->expectExceptionMessage(
2277
            'Relation names are case sensitive. Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer ' .
2278
            'has a relation named "expensiveOrders" instead of "expensiveorders"'
2279
        );
2280
        $query->getRelation('expensiveorders');
2281
    }
2282
2283
    public function testExists(): void
2284
    {
2285
        $this->checkFixture($this->db, 'customer');
2286
2287
        $customer = new ActiveQuery(Customer::class, $this->db);
2288
2289
        $this->assertTrue($customer->where(['id' => 2])->exists());
2290
        $this->assertFalse($customer->where(['id' => 5])->exists());
2291
        $this->assertTrue($customer->where(['name' => 'user1'])->exists());
2292
        $this->assertFalse($customer->where(['name' => 'user5'])->exists());
2293
        $this->assertTrue($customer->where(['id' => [2, 3]])->exists());
2294
        $this->assertTrue($customer->where(['id' => [2, 3]])->offset(1)->exists());
2295
        $this->assertFalse($customer->where(['id' => [2, 3]])->offset(2)->exists());
2296
    }
2297
2298
    public function testUnlink(): void
2299
    {
2300
        $this->checkFixture($this->db, 'customer');
2301
2302
        /** has many without delete */
2303
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2304
        $customer = $customerQuery->findOne(2);
2305
        $this->assertCount(2, $customer->ordersWithNullFK);
0 ignored issues
show
Bug introduced by
Accessing ordersWithNullFK on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2306
        $customer->unlink('ordersWithNullFK', $customer->ordersWithNullFK[1], false);
2307
        $this->assertCount(1, $customer->ordersWithNullFK);
2308
2309
        $orderWithNullFKQuery = new ActiveQuery(OrderWithNullFK::class, $this->db);
2310
        $orderWithNullFK = $orderWithNullFKQuery->findOne(3);
2311
        $this->assertEquals(3, $orderWithNullFK->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2312
        $this->assertNull($orderWithNullFK->customer_id);
0 ignored issues
show
Bug introduced by
Accessing customer_id on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2313
2314
        /** has many with delete */
2315
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2316
        $customer = $customerQuery->findOne(2);
2317
        $this->assertCount(2, $customer->orders);
0 ignored issues
show
Bug introduced by
Accessing orders on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2318
2319
        $customer->unlink('orders', $customer->orders[1], true);
2320
        $this->assertCount(1, $customer->orders);
2321
2322
        $orderQuery = new ActiveQuery(Order::class, $this->db);
2323
        $this->assertNull($orderQuery->findOne(3));
2324
2325
        /** via model with delete */
2326
        $orderQuery = new ActiveQuery(Order::class, $this->db);
2327
        $order = $orderQuery->findOne(2);
2328
        $this->assertCount(3, $order->items);
0 ignored issues
show
Bug introduced by
Accessing items on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2329
        $this->assertCount(3, $order->orderItems);
0 ignored issues
show
Bug introduced by
Accessing orderItems on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2330
        $order->unlink('items', $order->items[2], true);
2331
        $this->assertCount(2, $order->items);
2332
        $this->assertCount(2, $order->orderItems);
2333
2334
        /** via model without delete */
2335
        $this->assertCount(3, $order->itemsWithNullFK);
0 ignored issues
show
Bug introduced by
Accessing itemsWithNullFK on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2336
        $order->unlink('itemsWithNullFK', $order->itemsWithNullFK[2], false);
2337
        $this->assertCount(2, $order->itemsWithNullFK);
2338
        $this->assertCount(2, $order->orderItems);
2339
    }
2340
2341
    public function testUnlinkAllAndConditionSetNull(): void
2342
    {
2343
        $this->checkFixture($this->db, 'order_item_with_null_fk');
2344
2345
        /** in this test all orders are owned by customer 1 */
2346
        $orderWithNullFKInstance = new OrderWithNullFK($this->db);
2347
        $orderWithNullFKInstance->updateAll(['customer_id' => 1]);
2348
2349
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2350
        $customer = $customerQuery->findOne(1);
2351
        $this->assertCount(3, $customer->ordersWithNullFK);
0 ignored issues
show
Bug introduced by
Accessing ordersWithNullFK on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2352
        $this->assertCount(1, $customer->expensiveOrdersWithNullFK);
0 ignored issues
show
Bug introduced by
Accessing expensiveOrdersWithNullFK on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2353
2354
        $orderWithNullFKQuery = new ActiveQuery(OrderWithNullFK::class, $this->db);
2355
        $this->assertEquals(3, $orderWithNullFKQuery->count());
2356
2357
        $customer->unlinkAll('expensiveOrdersWithNullFK');
2358
        $this->assertCount(3, $customer->ordersWithNullFK);
2359
        $this->assertCount(0, $customer->expensiveOrdersWithNullFK);
2360
        $this->assertEquals(3, $orderWithNullFKQuery->count());
2361
2362
        $customer = $customerQuery->findOne(1);
2363
        $this->assertCount(2, $customer->ordersWithNullFK);
2364
        $this->assertCount(0, $customer->expensiveOrdersWithNullFK);
2365
    }
2366
2367
    public function testUnlinkAllAndConditionDelete(): void
2368
    {
2369
        $this->checkFixture($this->db, 'customer', true);
2370
2371
        /** in this test all orders are owned by customer 1 */
2372
        $orderInstance = new Order($this->db);
2373
        $orderInstance->updateAll(['customer_id' => 1]);
2374
2375
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2376
        $customer = $customerQuery->findOne(1);
2377
        $this->assertCount(3, $customer->orders);
0 ignored issues
show
Bug introduced by
Accessing orders on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2378
        $this->assertCount(1, $customer->expensiveOrders);
0 ignored issues
show
Bug introduced by
Accessing expensiveOrders on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2379
2380
        $orderQuery = new ActiveQuery(Order::class, $this->db);
2381
        $this->assertEquals(3, $orderQuery->count());
2382
2383
        $customer->unlinkAll('expensiveOrders', true);
2384
        $this->assertCount(3, $customer->orders);
2385
        $this->assertCount(0, $customer->expensiveOrders);
2386
        $this->assertEquals(2, $orderQuery->count());
2387
2388
        $customer = $customerQuery->findOne(1);
2389
        $this->assertCount(2, $customer->orders);
2390
        $this->assertCount(0, $customer->expensiveOrders);
2391
    }
2392
2393
    public function testUpdate(): void
2394
    {
2395
        $this->checkFixture($this->db, 'customer');
2396
2397
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2398
        $customer = $customerQuery->findOne(2);
2399
        $this->assertInstanceOf(Customer::class, $customer);
2400
        $this->assertEquals('user2', $customer->getAttribute('name'));
2401
        $this->assertFalse($customer->getIsNewRecord());
2402
        $this->assertEmpty($customer->getDirtyAttributes());
0 ignored issues
show
Bug introduced by
The method getDirtyAttributes() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Yiisoft\ActiveRecord\ActiveRecordInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2402
        $this->assertEmpty($customer->/** @scrutinizer ignore-call */ getDirtyAttributes());
Loading history...
2403
2404
        $customer->setAttribute('name', 'user2x');
2405
        $customer->save();
2406
        $this->assertEquals('user2x', $customer->getAttribute('name'));
2407
        $this->assertFalse($customer->getIsNewRecord());
2408
2409
        $customer2 = $customerQuery->findOne(2);
2410
        $this->assertEquals('user2x', $customer2->getAttribute('name'));
2411
2412
        /** no update */
2413
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2414
        $customer = $customerQuery->findOne(1);
2415
2416
        $customer->setAttribute('name', 'user1');
2417
        $this->assertEquals(0, $customer->update());
2418
2419
        /** updateAll */
2420
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2421
        $customer = $customerQuery->findOne(3);
2422
        $this->assertEquals('user3', $customer->getAttribute('name'));
2423
2424
        $ret = $customer->updateAll(['name' => 'temp'], ['id' => 3]);
2425
        $this->assertEquals(1, $ret);
2426
2427
        $customer = $customerQuery->findOne(3);
2428
        $this->assertEquals('temp', $customer->getAttribute('name'));
2429
2430
        $ret = $customer->updateAll(['name' => 'tempX']);
2431
        $this->assertEquals(3, $ret);
2432
2433
        $ret = $customer->updateAll(['name' => 'temp'], ['name' => 'user6']);
2434
        $this->assertEquals(0, $ret);
2435
    }
2436
2437
    public function testUpdateCounters(): void
2438
    {
2439
        $this->checkFixture($this->db, 'order_item', true);
2440
2441
        /** updateCounters */
2442
        $pk = ['order_id' => 2, 'item_id' => 4];
2443
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
2444
        $orderItem = $orderItemQuery->findOne($pk);
2445
        $this->assertEquals(1, $orderItem->quantity);
0 ignored issues
show
Bug introduced by
Accessing quantity on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2446
2447
        $ret = $orderItem->updateCounters(['quantity' => -1]);
0 ignored issues
show
Bug introduced by
The method updateCounters() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. Did you maybe mean update()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2447
        /** @scrutinizer ignore-call */ 
2448
        $ret = $orderItem->updateCounters(['quantity' => -1]);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2448
        $this->assertTrue($ret);
2449
        $this->assertEquals(0, $orderItem->quantity);
2450
2451
        $orderItem = $orderItemQuery->findOne($pk);
2452
        $this->assertEquals(0, $orderItem->quantity);
2453
2454
        /** updateAllCounters */
2455
        $pk = ['order_id' => 1, 'item_id' => 2];
2456
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
2457
        $orderItem = $orderItemQuery->findOne($pk);
2458
        $this->assertEquals(2, $orderItem->quantity);
2459
2460
        $orderItem = new OrderItem($this->db);
2461
        $ret = $orderItem->updateAllCounters(['quantity' => 3, 'subtotal' => -10], $pk);
2462
        $this->assertEquals(1, $ret);
2463
2464
        $orderItem = $orderItemQuery->findOne($pk);
2465
        $this->assertEquals(5, $orderItem->quantity);
2466
        $this->assertEquals(30, $orderItem->subtotal);
0 ignored issues
show
Bug introduced by
Accessing subtotal on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2467
    }
2468
2469
    public function testDelete(): void
2470
    {
2471
        $this->checkFixture($this->db, 'customer', true);
2472
2473
        /** delete */
2474
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2475
        $customer = $customerQuery->findOne(2);
2476
        $this->assertInstanceOf(Customer::class, $customer);
2477
        $this->assertEquals('user2', $customer->name);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2478
2479
        $customer->delete();
2480
2481
        $customer = $customerQuery->findOne(2);
2482
        $this->assertNull($customer);
2483
2484
        /** deleteAll */
2485
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2486
        $customers = $customerQuery->all();
2487
        $this->assertCount(2, $customers);
2488
2489
        $customer = new Customer($this->db);
2490
        $ret = $customer->deleteAll();
2491
        $this->assertEquals(2, $ret);
2492
2493
        $customers = $customerQuery->all();
2494
        $this->assertCount(0, $customers);
2495
2496
        $ret = $customer->deleteAll();
2497
        $this->assertEquals(0, $ret);
2498
    }
2499
2500
    /**
2501
     * {@see https://github.com/yiisoft/yii2/issues/17089}
2502
     */
2503
    public function testViaWithCallable(): void
2504
    {
2505
        $this->checkFixture($this->db, 'order', true);
2506
2507
        $orderQuery = new ActiveQuery(Order::class, $this->db);
2508
2509
        $order = $orderQuery->findOne(2);
2510
2511
        $expensiveItems = $order->expensiveItemsUsingViaWithCallable;
0 ignored issues
show
Bug introduced by
Accessing expensiveItemsUsingViaWithCallable on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2512
        $cheapItems = $order->cheapItemsUsingViaWithCallable;
0 ignored issues
show
Bug introduced by
Accessing cheapItemsUsingViaWithCallable on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2513
2514
        $this->assertCount(2, $expensiveItems);
2515
        $this->assertEquals(4, $expensiveItems[0]->id);
2516
        $this->assertEquals(5, $expensiveItems[1]->id);
2517
        $this->assertCount(1, $cheapItems);
2518
        $this->assertEquals(3, $cheapItems[0]->id);
2519
    }
2520
2521
    public function testLink(): void
2522
    {
2523
        $this->checkFixture($this->db, 'customer', true);
2524
2525
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2526
        $customer = $customerQuery->findOne(2);
2527
        $this->assertCount(2, $customer->orders);
0 ignored issues
show
Bug introduced by
Accessing orders on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2528
2529
        /** has many */
2530
        $order = new Order($this->db);
2531
2532
        $order->total = 100;
2533
        $order->created_at = time();
2534
        $this->assertTrue($order->isNewRecord);
2535
2536
        /** belongs to */
2537
        $order = new Order($this->db);
2538
2539
        $order->total = 100;
2540
        $order->created_at = time();
2541
        $this->assertTrue($order->isNewRecord);
2542
2543
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2544
        $customer = $customerQuery->findOne(1);
2545
        $this->assertNull($order->customer);
0 ignored issues
show
Bug Best Practice introduced by
The property customer does not exist on Yiisoft\ActiveRecord\Tes...tubs\ActiveRecord\Order. Since you implemented __get, consider adding a @property annotation.
Loading history...
2546
2547
        $order->link('customer', $customer);
0 ignored issues
show
Bug introduced by
It seems like $customer can also be of type null; however, parameter $arClass of Yiisoft\ActiveRecord\BaseActiveRecord::link() does only seem to accept Yiisoft\ActiveRecord\ActiveRecordInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2547
        $order->link('customer', /** @scrutinizer ignore-type */ $customer);
Loading history...
2548
        $this->assertFalse($order->isNewRecord);
2549
        $this->assertEquals(1, $order->customer_id);
2550
        $this->assertEquals(1, $order->customer->primaryKey);
2551
2552
        /** via model */
2553
        $orderQuery = new ActiveQuery(Order::class, $this->db);
2554
        $order = $orderQuery->findOne(1);
2555
        $this->assertCount(2, $order->items);
0 ignored issues
show
Bug introduced by
Accessing items on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2556
        $this->assertCount(2, $order->orderItems);
0 ignored issues
show
Bug introduced by
Accessing orderItems on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2557
2558
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
2559
        $orderItem = $orderItemQuery->findOne(['order_id' => 1, 'item_id' => 3]);
2560
        $this->assertNull($orderItem);
2561
2562
        $itemQuery = new ActiveQuery(Item::class, $this->db);
2563
        $item = $itemQuery->findOne(3);
2564
        $order->link('items', $item, ['quantity' => 10, 'subtotal' => 100]);
0 ignored issues
show
Bug introduced by
It seems like $item can also be of type null; however, parameter $arClass of Yiisoft\ActiveRecord\ActiveRecordInterface::link() does only seem to accept Yiisoft\ActiveRecord\ActiveRecordInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2564
        $order->link('items', /** @scrutinizer ignore-type */ $item, ['quantity' => 10, 'subtotal' => 100]);
Loading history...
2565
        $this->assertCount(3, $order->items);
2566
        $this->assertCount(3, $order->orderItems);
2567
2568
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
2569
        $orderItem = $orderItemQuery->findOne(['order_id' => 1, 'item_id' => 3]);
2570
        $this->assertInstanceOf(OrderItem::class, $orderItem);
2571
        $this->assertEquals(10, $orderItem->quantity);
0 ignored issues
show
Bug introduced by
Accessing quantity on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2572
        $this->assertEquals(100, $orderItem->subtotal);
0 ignored issues
show
Bug introduced by
Accessing subtotal on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
2573
    }
2574
2575
    public function testEqual(): void
2576
    {
2577
        $this->checkFixture($this->db, 'customer');
2578
2579
        $customerA = (new ActiveQuery(Customer::class, $this->db))->findOne(1);
2580
        $customerB = (new ActiveQuery(Customer::class, $this->db))->findOne(2);
2581
        $this->assertFalse($customerA->equals($customerB));
0 ignored issues
show
Bug introduced by
It seems like $customerB can also be of type null; however, parameter $record of Yiisoft\ActiveRecord\Act...cordInterface::equals() does only seem to accept Yiisoft\ActiveRecord\ActiveRecordInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2581
        $this->assertFalse($customerA->equals(/** @scrutinizer ignore-type */ $customerB));
Loading history...
2582
2583
        $customerB = (new ActiveQuery(Customer::class, $this->db))->findOne(1);
2584
        $this->assertTrue($customerA->equals($customerB));
2585
2586
        $customerA = (new ActiveQuery(Customer::class, $this->db))->findOne(1);
2587
        $customerB = (new ActiveQuery(Item::class, $this->db))->findOne(1);
2588
        $this->assertFalse($customerA->equals($customerB));
2589
    }
2590
}
2591