Passed
Pull Request — master (#365)
by Sergei
02:51
created

ActiveQueryTest::testExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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

420
        /** @scrutinizer ignore-call */ 
421
        $items = $customers->getOrderItems();
Loading history...
421
422
        $this->assertCount(2, $items);
423
        $this->assertEquals(1, $items[0]->getAttribute('id'));
424
        $this->assertEquals(2, $items[1]->getAttribute('id'));
425
        $this->assertInstanceOf(Item::class, $items[0]);
426
        $this->assertInstanceOf(Item::class, $items[1]);
427
    }
428
429
    /**
430
     * {@see https://github.com/yiisoft/yii2/issues/5341}
431
     *
432
     * Issue: Plan 1 -- * Account * -- * User
433
     * Our Tests: Category 1 -- * Item * -- * Order
434
     */
435
    public function testDeeplyNestedTableRelation2(): void
436
    {
437
        $this->checkFixture($this->db, 'category');
438
439
        $categoryQuery = new ActiveQuery(Category::class, $this->db);
440
441
        $categories = $categoryQuery->where(['id' => 1])->onePopulate();
442
        $this->assertNotNull($categories);
443
444
        $orders = $categories->getOrders();
0 ignored issues
show
Bug introduced by
The method getOrders() 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...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

444
        /** @scrutinizer ignore-call */ 
445
        $orders = $categories->getOrders();
Loading history...
445
        $this->assertCount(2, $orders);
446
        $this->assertInstanceOf(Order::class, $orders[0]);
447
        $this->assertInstanceOf(Order::class, $orders[1]);
448
449
        $ids = [$orders[0]->getId(), $orders[1]->getAttribute('id')];
450
        sort($ids);
451
        $this->assertEquals([1, 3], $ids);
452
453
        $categories = $categoryQuery->where(['id' => 2])->onePopulate();
454
        $this->assertNotNull($categories);
455
456
        $orders = $categories->getOrders();
457
        $this->assertCount(1, $orders);
458
        $this->assertEquals(2, $orders[0]->getAttribute('id'));
459
        $this->assertInstanceOf(Order::class, $orders[0]);
460
    }
461
462
    public function testJoinWith(): void
463
    {
464
        $this->checkFixture($this->db, 'order');
465
466
        /** left join and eager loading */
467
        $orderQuery = new ActiveQuery(Order::class, $this->db);
468
        $orders = $orderQuery->joinWith('customer')->orderBy('customer.id DESC, order.id')->all();
469
        $this->assertCount(3, $orders);
470
        $this->assertEquals(2, $orders[0]->getId());
471
        $this->assertEquals(3, $orders[1]->getId());
472
        $this->assertEquals(1, $orders[2]->getId());
473
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
474
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
475
        $this->assertTrue($orders[2]->isRelationPopulated('customer'));
476
477
        /** inner join filtering and eager loading */
478
        $orderQuery = new ActiveQuery(Order::class, $this->db);
479
        $orders = $orderQuery->innerJoinWith(
480
            [
481
                'customer' => function ($query) {
482
                    $query->where('{{customer}}.[[id]]=2');
483
                },
484
            ]
485
        )->orderBy('order.id')->all();
486
        $this->assertCount(2, $orders);
487
        $this->assertEquals(2, $orders[0]->getId());
488
        $this->assertEquals(3, $orders[1]->getId());
489
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
490
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
491
492
        /** inner join filtering, eager loading, conditions on both primary and relation */
493
        $orderQuery = new ActiveQuery(Order::class, $this->db);
494
        $orders = $orderQuery->innerJoinWith(
495
            [
496
                'customer' => function ($query) {
497
                    $query->where(['customer.id' => 2]);
498
                },
499
            ]
500
        )->where(['order.id' => [1, 2]])->orderBy('order.id')->all();
501
        $this->assertCount(1, $orders);
502
        $this->assertEquals(2, $orders[0]->getId());
503
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
504
505
        /** inner join filtering without eager loading */
506
        $orderQuery = new ActiveQuery(Order::class, $this->db);
507
        $orders = $orderQuery->innerJoinWith(
508
            [
509
                'customer' => static function ($query) {
510
                    $query->where('{{customer}}.[[id]]=2');
511
                },
512
            ],
513
            false
514
        )->orderBy('order.id')->all();
515
        $this->assertCount(2, $orders);
516
        $this->assertEquals(2, $orders[0]->getId());
517
        $this->assertEquals(3, $orders[1]->getId());
518
        $this->assertFalse($orders[0]->isRelationPopulated('customer'));
519
        $this->assertFalse($orders[1]->isRelationPopulated('customer'));
520
521
        /** inner join filtering without eager loading, conditions on both primary and relation */
522
        $orderQuery = new ActiveQuery(Order::class, $this->db);
523
        $orders = $orderQuery->innerJoinWith(
524
            [
525
                'customer' => static function ($query) {
526
                    $query->where(['customer.id' => 2]);
527
                },
528
            ],
529
            false
530
        )->where(['order.id' => [1, 2]])->orderBy('order.id')->all();
531
        $this->assertCount(1, $orders);
532
        $this->assertEquals(2, $orders[0]->getId());
533
        $this->assertFalse($orders[0]->isRelationPopulated('customer'));
534
535
        /** join with via-relation */
536
        $orderQuery = new ActiveQuery(Order::class, $this->db);
537
        $orders = $orderQuery->innerJoinWith('books')->orderBy('order.id')->all();
538
        $this->assertCount(2, $orders);
539
        $this->assertCount(2, $orders[0]->getBooks());
540
        $this->assertCount(1, $orders[1]->getBooks());
541
        $this->assertEquals(1, $orders[0]->getId());
542
        $this->assertEquals(3, $orders[1]->getId());
543
        $this->assertTrue($orders[0]->isRelationPopulated('books'));
544
        $this->assertTrue($orders[1]->isRelationPopulated('books'));
545
546
        /** join with sub-relation */
547
        $orderQuery = new ActiveQuery(Order::class, $this->db);
548
        $orders = $orderQuery->innerJoinWith(
549
            [
550
                'items' => function ($q) {
551
                    $q->orderBy('item.id');
552
                },
553
                'items.category' => function ($q) {
554
                    $q->where('{{category}}.[[id]] = 2');
555
                },
556
            ]
557
        )->orderBy('order.id')->all();
558
        $this->assertCount(1, $orders);
559
        $this->assertCount(3, $orders[0]->getItems());
560
        $this->assertEquals(2, $orders[0]->getId());
561
        $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId());
562
        $this->assertTrue($orders[0]->isRelationPopulated('items'));
563
        $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category'));
564
565
        /** join with table alias */
566
        $orderQuery = new ActiveQuery(Order::class, $this->db);
567
        $orders = $orderQuery->joinWith(
568
            [
569
                'customer' => function ($q) {
570
                    $q->from('customer c');
571
                },
572
            ]
573
        )->orderBy('c.id DESC, order.id')->all();
574
        $this->assertCount(3, $orders);
575
        $this->assertEquals(2, $orders[0]->getId());
576
        $this->assertEquals(3, $orders[1]->getId());
577
        $this->assertEquals(1, $orders[2]->getId());
578
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
579
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
580
        $this->assertTrue($orders[2]->isRelationPopulated('customer'));
581
582
        /** join with table alias */
583
        $orderQuery = new ActiveQuery(Order::class, $this->db);
584
        $orders = $orderQuery->joinWith('customer as c')->orderBy('c.id DESC, order.id')->all();
585
        $this->assertCount(3, $orders);
586
        $this->assertEquals(2, $orders[0]->getId());
587
        $this->assertEquals(3, $orders[1]->getId());
588
        $this->assertEquals(1, $orders[2]->getId());
589
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
590
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
591
        $this->assertTrue($orders[2]->isRelationPopulated('customer'));
592
593
        /** join with table alias sub-relation */
594
        $orderQuery = new ActiveQuery(Order::class, $this->db);
595
        $orders = $orderQuery->innerJoinWith(
596
            [
597
                'items as t' => function ($q) {
598
                    $q->orderBy('t.id');
599
                },
600
                'items.category as c' => function ($q) {
601
                    $q->where('{{c}}.[[id]] = 2');
602
                },
603
            ]
604
        )->orderBy('order.id')->all();
605
        $this->assertCount(1, $orders);
606
        $this->assertCount(3, $orders[0]->getItems());
607
        $this->assertEquals(2, $orders[0]->getId());
608
        $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId());
609
        $this->assertTrue($orders[0]->isRelationPopulated('items'));
610
        $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category'));
611
612
        /** join with ON condition */
613
        $orderQuery = new ActiveQuery(Order::class, $this->db);
614
        $orders = $orderQuery->joinWith('books2')->orderBy('order.id')->all();
615
        $this->assertCount(3, $orders);
616
        $this->assertCount(2, $orders[0]->getBooks2());
617
        $this->assertCount(0, $orders[1]->getBooks2());
618
        $this->assertCount(1, $orders[2]->getBooks2());
619
        $this->assertEquals(1, $orders[0]->getId());
620
        $this->assertEquals(2, $orders[1]->getId());
621
        $this->assertEquals(3, $orders[2]->getId());
622
        $this->assertTrue($orders[0]->isRelationPopulated('books2'));
623
        $this->assertTrue($orders[1]->isRelationPopulated('books2'));
624
        $this->assertTrue($orders[2]->isRelationPopulated('books2'));
625
626
        /** lazy loading with ON condition */
627
        $orderQuery = new ActiveQuery(Order::class, $this->db);
628
        $order = $orderQuery->findOne(1);
629
        $this->assertCount(2, $order->getBooks2());
0 ignored issues
show
Bug introduced by
The method getBooks2() 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

629
        $this->assertCount(2, $order->/** @scrutinizer ignore-call */ getBooks2());
Loading history...
630
631
        $orderQuery = new ActiveQuery(Order::class, $this->db);
632
        $order = $orderQuery->findOne(2);
633
        $this->assertCount(0, $order->getBooks2());
634
635
        $order = new ActiveQuery(Order::class, $this->db);
636
        $order = $order->findOne(3);
637
        $this->assertCount(1, $order->getBooks2());
638
639
        /** eager loading with ON condition */
640
        $orderQuery = new ActiveQuery(Order::class, $this->db);
641
        $orders = $orderQuery->with('books2')->all();
642
        $this->assertCount(3, $orders);
643
        $this->assertCount(2, $orders[0]->getBooks2());
644
        $this->assertCount(0, $orders[1]->getBooks2());
645
        $this->assertCount(1, $orders[2]->getBooks2());
646
        $this->assertEquals(1, $orders[0]->getId());
647
        $this->assertEquals(2, $orders[1]->getId());
648
        $this->assertEquals(3, $orders[2]->getId());
649
        $this->assertTrue($orders[0]->isRelationPopulated('books2'));
650
        $this->assertTrue($orders[1]->isRelationPopulated('books2'));
651
        $this->assertTrue($orders[2]->isRelationPopulated('books2'));
652
653
        /** join with count and query */
654
        $orderQuery = new ActiveQuery(Order::class, $this->db);
655
        $query = $orderQuery->joinWith('customer');
656
        $count = $query->count();
657
        $this->assertEquals(3, $count);
658
659
        $orders = $query->all();
660
        $this->assertCount(3, $orders);
661
662
        /** {@see https://github.com/yiisoft/yii2/issues/2880} */
663
        $orderQuery = new ActiveQuery(Order::class, $this->db);
664
        $query = $orderQuery->findOne(1);
665
        $customer = $query->getCustomerQuery()->joinWith(
0 ignored issues
show
Bug introduced by
The method getCustomerQuery() 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...MagicActiveRecord\Order 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

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

804
                $query->/** @scrutinizer ignore-call */ 
805
                        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...
805
            )->all();
806
        }
807
808
        $this->assertCount(3, $orders);
809
        $this->assertEquals(2, $orders[0]->getId());
810
        $this->assertEquals(3, $orders[1]->getId());
811
        $this->assertEquals(1, $orders[2]->getId());
812
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
813
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
814
        $this->assertTrue($orders[2]->isRelationPopulated('customer'));
815
816
        /** inner join filtering and eager loading */
817
        $orderQuery = new ActiveQuery(Order::class, $this->db);
818
        $query = $orderQuery->innerJoinWith(['customer c']);
819
820
        if ($aliasMethod === 'explicit') {
821
            $orders = $query->where('{{c}}.[[id]]=2')->orderBy('order.id')->all();
822
        } elseif ($aliasMethod === 'querysyntax') {
823
            $orders = $query->where('{{@customer}}.[[id]]=2')->orderBy('{{@order}}.id')->all();
824
        } elseif ($aliasMethod === 'applyAlias') {
825
            $orders = $query->where(
826
                [$query->applyAlias('customer', 'id') => 2]
827
            )->orderBy($query->applyAlias('order', 'id'))->all();
828
        }
829
830
        $this->assertCount(2, $orders);
831
        $this->assertEquals(2, $orders[0]->getId());
832
        $this->assertEquals(3, $orders[1]->getId());
833
        $this->assertTrue($orders[0]->isRelationPopulated('customer'));
834
        $this->assertTrue($orders[1]->isRelationPopulated('customer'));
835
836
        /** inner join filtering without eager loading */
837
        $orderQuery = new ActiveQuery(Order::class, $this->db);
838
        $query = $orderQuery->innerJoinWith(['customer c'], false);
839
840
        if ($aliasMethod === 'explicit') {
841
            $orders = $query->where('{{c}}.[[id]]=2')->orderBy('order.id')->all();
842
        } elseif ($aliasMethod === 'querysyntax') {
843
            $orders = $query->where('{{@customer}}.[[id]]=2')->orderBy('{{@order}}.id')->all();
844
        } elseif ($aliasMethod === 'applyAlias') {
845
            $orders = $query->where(
846
                [$query->applyAlias('customer', 'id') => 2]
847
            )->orderBy($query->applyAlias('order', 'id'))->all();
848
        }
849
850
        $this->assertCount(2, $orders);
851
        $this->assertEquals(2, $orders[0]->getId());
852
        $this->assertEquals(3, $orders[1]->getId());
853
        $this->assertFalse($orders[0]->isRelationPopulated('customer'));
854
        $this->assertFalse($orders[1]->isRelationPopulated('customer'));
855
856
        /** join with via-relation */
857
        $orderQuery = new ActiveQuery(Order::class, $this->db);
858
        $query = $orderQuery->innerJoinWith(['books b']);
859
860
        if ($aliasMethod === 'explicit') {
861
            $orders = $query->where(
862
                ['b.name' => 'Yii 1.1 Application Development Cookbook']
863
            )->orderBy('order.id')->all();
864
        } elseif ($aliasMethod === 'querysyntax') {
865
            $orders = $query->where(
866
                ['{{@item}}.name' => 'Yii 1.1 Application Development Cookbook']
867
            )->orderBy('{{@order}}.id')->all();
868
        } elseif ($aliasMethod === 'applyAlias') {
869
            $orders = $query->where(
870
                [$query->applyAlias('book', 'name') => 'Yii 1.1 Application Development Cookbook']
871
            )->orderBy($query->applyAlias('order', 'id'))->all();
872
        }
873
874
        $this->assertCount(2, $orders);
875
        $this->assertCount(2, $orders[0]->getBooks());
876
        $this->assertCount(1, $orders[1]->getBooks());
877
        $this->assertEquals(1, $orders[0]->getId());
878
        $this->assertEquals(3, $orders[1]->getId());
879
        $this->assertTrue($orders[0]->isRelationPopulated('books'));
880
        $this->assertTrue($orders[1]->isRelationPopulated('books'));
881
882
        /** joining sub relations */
883
        $orderQuery = new ActiveQuery(Order::class, $this->db);
884
        $query = $orderQuery->innerJoinWith(
885
            [
886
                'items i' => static function ($q) use ($aliasMethod) {
887
                    /** @var $q ActiveQuery */
888
                    if ($aliasMethod === 'explicit') {
889
                        $q->orderBy('{{i}}.id');
890
                    } elseif ($aliasMethod === 'querysyntax') {
891
                        $q->orderBy('{{@item}}.id');
892
                    } elseif ($aliasMethod === 'applyAlias') {
893
                        $q->orderBy($q->applyAlias('item', 'id'));
894
                    }
895
                },
896
                'items.category c' => static function ($q) use ($aliasMethod) {
897
                    /**  @var $q ActiveQuery */
898
                    if ($aliasMethod === 'explicit') {
899
                        $q->where('{{c}}.[[id]] = 2');
900
                    } elseif ($aliasMethod === 'querysyntax') {
901
                        $q->where('{{@category}}.[[id]] = 2');
902
                    } elseif ($aliasMethod === 'applyAlias') {
903
                        $q->where([$q->applyAlias('category', 'id') => 2]);
904
                    }
905
                },
906
            ]
907
        );
908
909
        if ($aliasMethod === 'explicit') {
910
            $orders = $query->orderBy('{{i}}.id')->all();
911
        } elseif ($aliasMethod === 'querysyntax') {
912
            $orders = $query->orderBy('{{@item}}.id')->all();
913
        } elseif ($aliasMethod === 'applyAlias') {
914
            $orders = $query->orderBy($query->applyAlias('item', 'id'))->all();
915
        }
916
917
        $this->assertCount(1, $orders);
918
        $this->assertCount(3, $orders[0]->getItems());
919
        $this->assertEquals(2, $orders[0]->getId());
920
        $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId());
921
        $this->assertTrue($orders[0]->isRelationPopulated('items'));
922
        $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category'));
923
924
        /** join with ON condition */
925
        if ($aliasMethod === 'explicit' || $aliasMethod === 'querysyntax') {
926
            $relationName = 'books' . ucfirst($aliasMethod);
927
928
            $orderQuery = new ActiveQuery(Order::class, $this->db);
929
            $orders = $orderQuery->joinWith(["$relationName b"])->orderBy('order.id')->all();
930
931
            $this->assertCount(3, $orders);
932
            $this->assertCount(2, $orders[0]->relation($relationName));
933
            $this->assertCount(0, $orders[1]->relation($relationName));
934
            $this->assertCount(1, $orders[2]->relation($relationName));
935
            $this->assertEquals(1, $orders[0]->getId());
936
            $this->assertEquals(2, $orders[1]->getId());
937
            $this->assertEquals(3, $orders[2]->getId());
938
            $this->assertTrue($orders[0]->isRelationPopulated($relationName));
939
            $this->assertTrue($orders[1]->isRelationPopulated($relationName));
940
            $this->assertTrue($orders[2]->isRelationPopulated($relationName));
941
        }
942
943
        /** join with ON condition and alias in relation definition */
944
        if ($aliasMethod === 'explicit' || $aliasMethod === 'querysyntax') {
945
            $relationName = 'books' . ucfirst($aliasMethod) . 'A';
946
947
            $orderQuery = new ActiveQuery(Order::class, $this->db);
948
            $orders = $orderQuery->joinWith([(string)$relationName])->orderBy('order.id')->all();
949
950
            $this->assertCount(3, $orders);
951
            $this->assertCount(2, $orders[0]->relation($relationName));
952
            $this->assertCount(0, $orders[1]->relation($relationName));
953
            $this->assertCount(1, $orders[2]->relation($relationName));
954
            $this->assertEquals(1, $orders[0]->getId());
955
            $this->assertEquals(2, $orders[1]->getId());
956
            $this->assertEquals(3, $orders[2]->getId());
957
            $this->assertTrue($orders[0]->isRelationPopulated($relationName));
958
            $this->assertTrue($orders[1]->isRelationPopulated($relationName));
959
            $this->assertTrue($orders[2]->isRelationPopulated($relationName));
960
        }
961
962
        /** join with count and query */
963
        $orderQuery = new ActiveQuery(Order::class, $this->db);
964
        $query = $orderQuery->joinWith(['customer c']);
965
966
        if ($aliasMethod === 'explicit') {
967
            $count = $query->count('c.id');
968
        } elseif ($aliasMethod === 'querysyntax') {
969
            $count = $query->count('{{@customer}}.id');
970
        } elseif ($aliasMethod === 'applyAlias') {
971
            $count = $query->count($query->applyAlias('customer', 'id'));
972
        }
973
974
        $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...
975
976
        $orders = $query->all();
977
        $this->assertCount(3, $orders);
978
979
        /** relational query */
980
        $orderQuery = new ActiveQuery(Order::class, $this->db);
981
        $order = $orderQuery->findOne(1);
982
983
        $customerQuery = $order->getCustomerQuery()->innerJoinWith(['orders o'], false);
984
985
        if ($aliasMethod === 'explicit') {
986
            $customer = $customerQuery->where(['o.id' => 1])->onePopulate();
987
        } elseif ($aliasMethod === 'querysyntax') {
988
            $customer = $customerQuery->where(['{{@order}}.id' => 1])->onePopulate();
989
        } elseif ($aliasMethod === 'applyAlias') {
990
            $customer = $customerQuery->where([$query->applyAlias('order', 'id') => 1])->onePopulate();
991
        }
992
993
        $this->assertEquals(1, $customer->getId());
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...
994
        $this->assertNotNull($customer);
995
996
        /** join with sub-relation called inside Closure */
997
        $orderQuery = new ActiveQuery(Order::class, $this->db);
998
        $orders = $orderQuery->joinWith(
999
            [
1000
                'items' => static function ($q) use ($aliasMethod) {
1001
                    /** @var $q ActiveQuery */
1002
                    $q->orderBy('item.id');
1003
                    $q->joinWith(['category c']);
1004
1005
                    if ($aliasMethod === 'explicit') {
1006
                        $q->where('{{c}}.[[id]] = 2');
1007
                    } elseif ($aliasMethod === 'querysyntax') {
1008
                        $q->where('{{@category}}.[[id]] = 2');
1009
                    } elseif ($aliasMethod === 'applyAlias') {
1010
                        $q->where([$q->applyAlias('category', 'id') => 2]);
1011
                    }
1012
                },
1013
            ]
1014
        )->orderBy('order.id')->all();
1015
1016
        $this->assertCount(1, $orders);
1017
        $this->assertCount(3, $orders[0]->getItems());
1018
        $this->assertEquals(2, $orders[0]->getId());
1019
        $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId());
1020
        $this->assertTrue($orders[0]->isRelationPopulated('items'));
1021
        $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category'));
1022
    }
1023
1024
    /**
1025
     * @depends testJoinWith
1026
     */
1027
    public function testJoinWithSameTable(): void
1028
    {
1029
        $this->checkFixture($this->db, 'order');
1030
1031
        /**
1032
         * join with the same table but different aliases alias is defined in the relation definition without eager
1033
         * loading
1034
         */
1035
        $query = new ActiveQuery(Order::class, $this->db);
1036
        $query->joinWith('bookItems', false)->joinWith('movieItems', false)->where(['movies.name' => 'Toy Story']);
1037
        $orders = $query->all();
1038
        $this->assertCount(
1039
            1,
1040
            $orders,
1041
            $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

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

1384
        $this->assertSame($customer->/** @scrutinizer ignore-call */ getOrders2()[0]->getCustomer2(), $customer);
Loading history...
1385
1386
        //$customerQuery = new ActiveQuery(Customer::class, $this->db);
1387
        $customers = $customerQuery->with('orders2')->where(['id' => [1, 3]])->all();
1388
        $this->assertEmpty($customers[1]->getOrders2());
1389
        $this->assertSame($customers[0]->getOrders2()[0]->getCustomer2(), $customers[0]);
1390
1391
        /** lazy loading */
1392
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1393
        $customer = $customerQuery->findOne(2);
1394
        $orders = $customer->getOrders2();
1395
        $this->assertCount(2, $orders);
1396
        $this->assertSame($customer->getOrders2()[0]->getCustomer2(), $customer);
1397
        $this->assertSame($customer->getOrders2()[1]->getCustomer2(), $customer);
1398
1399
        /** ad-hoc lazy loading */
1400
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1401
        $customer = $customerQuery->findOne(2);
1402
        $orders = $customer->getOrders2Query()->all();
0 ignored issues
show
Bug introduced by
The method getOrders2Query() 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...icActiveRecord\Customer or 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

1402
        $orders = $customer->/** @scrutinizer ignore-call */ getOrders2Query()->all();
Loading history...
1403
        $this->assertCount(2, $orders);
1404
        $this->assertSame($orders[0]->getCustomer2(), $customer);
1405
        $this->assertSame($orders[1]->getCustomer2(), $customer);
1406
        $this->assertTrue(
1407
            $orders[0]->isRelationPopulated('customer2'),
1408
            'inverse relation did not populate the relation'
1409
        );
1410
        $this->assertTrue(
1411
            $orders[1]->isRelationPopulated('customer2'),
1412
            'inverse relation did not populate the relation'
1413
        );
1414
1415
        /** the other way around */
1416
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1417
        $customer = $customerQuery->with('orders2')->where(['id' => 1])->asArray()->onePopulate();
1418
        $this->assertSame($customer['orders2'][0]['customer2']['id'], $customer['id']);
1419
1420
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1421
        $customers = $customerQuery->with('orders2')->where(['id' => [1, 3]])->asArray()->all();
1422
        $this->assertSame($customer['orders2'][0]['customer2']['id'], $customers[0]['id']);
1423
        $this->assertEmpty($customers[1]['orders2']);
1424
1425
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1426
        $orders = $orderQuery->with('customer2')->where(['id' => 1])->all();
1427
        $this->assertSame($orders[0]->getCustomer2()->getOrders2(), [$orders[0]]);
1428
1429
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1430
        $order = $orderQuery->with('customer2')->where(['id' => 1])->onePopulate();
1431
        $this->assertSame($order->getCustomer2()->getOrders2(), [$order]);
0 ignored issues
show
Bug introduced by
The method getCustomer2() 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

1431
        $this->assertSame($order->/** @scrutinizer ignore-call */ getCustomer2()->getOrders2(), [$order]);
Loading history...
1432
1433
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1434
        $orders = $orderQuery->with('customer2')->where(['id' => 1])->asArray()->all();
1435
        $this->assertSame($orders[0]['customer2']['orders2'][0]['id'], $orders[0]['id']);
1436
1437
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1438
        $order = $orderQuery->with('customer2')->where(['id' => 1])->asArray()->onePopulate();
1439
        $this->assertSame($order['customer2']['orders2'][0]['id'], $orders[0]['id']);
1440
1441
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1442
        $orders = $orderQuery->with('customer2')->where(['id' => [1, 3]])->all();
1443
        $this->assertSame($orders[0]->getCustomer2()->getOrders2(), [$orders[0]]);
1444
        $this->assertSame($orders[1]->getCustomer2()->getOrders2(), [$orders[1]]);
1445
1446
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1447
        $orders = $orderQuery->with('customer2')->where(['id' => [2, 3]])->orderBy('id')->all();
1448
        $this->assertSame($orders[0]->getCustomer2()->getOrders2(), $orders);
1449
        $this->assertSame($orders[1]->getCustomer2()->getOrders2(), $orders);
1450
1451
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1452
        $orders = $orderQuery->with('customer2')->where(['id' => [2, 3]])->orderBy('id')->asArray()->all();
1453
        $this->assertSame($orders[0]['customer2']['orders2'][0]['id'], $orders[0]['id']);
1454
        $this->assertSame($orders[0]['customer2']['orders2'][1]['id'], $orders[1]['id']);
1455
        $this->assertSame($orders[1]['customer2']['orders2'][0]['id'], $orders[0]['id']);
1456
        $this->assertSame($orders[1]['customer2']['orders2'][1]['id'], $orders[1]['id']);
1457
    }
1458
1459
    public function testUnlinkAllViaTable(): void
1460
    {
1461
        $this->checkFixture($this->db, 'order', true);
1462
1463
        /** via table with delete. */
1464
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1465
        $order = $orderQuery->findOne(1);
1466
        $this->assertCount(2, $order->getBooksViaTable());
0 ignored issues
show
Bug introduced by
The method getBooksViaTable() 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

1466
        $this->assertCount(2, $order->/** @scrutinizer ignore-call */ getBooksViaTable());
Loading history...
1467
1468
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
1469
        $orderItemCount = $orderItemQuery->count();
1470
1471
        $itemQuery = new ActiveQuery(Item::class, $this->db);
1472
        $this->assertEquals(5, $itemQuery->count());
1473
1474
        $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

1474
        $order->/** @scrutinizer ignore-call */ 
1475
                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...
1475
        $this->assertEquals(5, $itemQuery->count());
1476
        $this->assertEquals($orderItemCount - 2, $orderItemQuery->count());
1477
        $this->assertCount(0, $order->getBooksViaTable());
1478
1479
        /** via table without delete */
1480
        $this->assertCount(2, $order->getBooksWithNullFKViaTable());
0 ignored issues
show
Bug introduced by
The method getBooksWithNullFKViaTable() 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

1480
        $this->assertCount(2, $order->/** @scrutinizer ignore-call */ getBooksWithNullFKViaTable());
Loading history...
1481
1482
        $orderItemsWithNullFKQuery = new ActiveQuery(OrderItemWithNullFK::class, $this->db);
1483
        $orderItemCount = $orderItemsWithNullFKQuery->count();
1484
        $this->assertEquals(5, $itemQuery->count());
1485
1486
        $order->unlinkAll('booksWithNullFKViaTable', false);
1487
        $this->assertCount(0, $order->getBooksWithNullFKViaTable());
1488
        $this->assertEquals(2, $orderItemsWithNullFKQuery->where(
1489
            ['AND', ['item_id' => [1, 2]], ['order_id' => null]]
1490
        )->count());
1491
1492
        $orderItemsWithNullFKQuery = new ActiveQuery(OrderItemWithNullFK::class, $this->db);
1493
        $this->assertEquals($orderItemCount, $orderItemsWithNullFKQuery->count());
1494
        $this->assertEquals(5, $itemQuery->count());
1495
    }
1496
1497
    public function testIssues(): void
1498
    {
1499
        $this->checkFixture($this->db, 'category', true);
1500
1501
        /** {@see https://github.com/yiisoft/yii2/issues/4938} */
1502
        $categoryQuery = new ActiveQuery(Category::class, $this->db);
1503
        $category = $categoryQuery->findOne(2);
1504
        $this->assertInstanceOf(Category::class, $category);
1505
        $this->assertEquals(3, $category->getItemsQuery()->count());
1506
        $this->assertEquals(1, $category->getLimitedItemsQuery()->count());
1507
        $this->assertEquals(1, $category->getLimitedItemsQuery()->distinct(true)->count());
1508
1509
        /** {@see https://github.com/yiisoft/yii2/issues/3197} */
1510
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1511
        $orders = $orderQuery->with('orderItems')->orderBy('id')->all();
1512
        $this->assertCount(3, $orders);
1513
        $this->assertCount(2, $orders[0]->getOrderItems());
1514
        $this->assertCount(3, $orders[1]->getOrderItems());
1515
        $this->assertCount(1, $orders[2]->getOrderItems());
1516
1517
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1518
        $orders = $orderQuery->with(
1519
            [
1520
                'orderItems' => static function ($q) {
1521
                    $q->indexBy('item_id');
1522
                },
1523
            ]
1524
        )->orderBy('id')->all();
1525
        $this->assertCount(3, $orders);
1526
        $this->assertCount(2, $orders[0]->getOrderItems());
1527
        $this->assertCount(3, $orders[1]->getOrderItems());
1528
        $this->assertCount(1, $orders[2]->getOrderItems());
1529
1530
        /** {@see https://github.com/yiisoft/yii2/issues/8149} */
1531
        $arClass = new Customer($this->db);
1532
1533
        $arClass->setName('test');
1534
        $arClass->setEmail('test');
1535
        $arClass->save();
1536
1537
        $arClass->updateCounters(['status' => 1]);
1538
        $this->assertEquals(1, $arClass->getStatus());
1539
    }
1540
1541
    public function testPopulateWithoutPk(): void
1542
    {
1543
        $this->checkFixture($this->db, 'customer', true);
1544
1545
        /** tests with single pk asArray */
1546
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1547
        $aggregation = $customerQuery
1548
            ->select(['{{customer}}.[[status]]', 'SUM({{order}}.[[total]]) AS [[sumtotal]]'])
1549
            ->joinWith('ordersPlain', false)
1550
            ->groupBy('{{customer}}.[[status]]')
1551
            ->orderBy('status')
1552
            ->asArray()
1553
            ->all();
1554
1555
        $expected = [
1556
            [
1557
                'status' => 1,
1558
                'sumtotal' => 183,
1559
            ],
1560
            [
1561
                'status' => 2,
1562
                'sumtotal' => 0,
1563
            ],
1564
        ];
1565
1566
        $this->assertEquals($expected, $aggregation);
1567
1568
        // tests with single pk asArray with eager loading
1569
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1570
        $aggregation = $customerQuery
1571
            ->select(['{{customer}}.[[status]]', 'SUM({{order}}.[[total]]) AS [[sumtotal]]'])
1572
            ->joinWith('ordersPlain')
1573
            ->groupBy('{{customer}}.[[status]]')
1574
            ->orderBy('status')
1575
            ->asArray()
1576
            ->all();
1577
1578
        $expected = [
1579
            [
1580
                'status' => 1,
1581
                'sumtotal' => 183,
1582
                'ordersPlain' => [],
1583
            ],
1584
            [
1585
                'status' => 2,
1586
                'sumtotal' => 0,
1587
                'ordersPlain' => [],
1588
            ],
1589
        ];
1590
        $this->assertEquals($expected, $aggregation);
1591
1592
        /** tests with single pk with Models */
1593
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1594
        $aggregation = $customerQuery
1595
            ->select(['{{customer}}.[[status]]', 'SUM({{order}}.[[total]]) AS [[sumTotal]]'])
1596
            ->joinWith('ordersPlain', false)
1597
            ->groupBy('{{customer}}.[[status]]')
1598
            ->orderBy('status')
1599
            ->all();
1600
1601
        $this->assertCount(2, $aggregation);
1602
        $this->assertContainsOnlyInstancesOf(Customer::class, $aggregation);
0 ignored issues
show
Bug introduced by
It seems like $aggregation can also be of type Countable; however, parameter $haystack of PHPUnit\Framework\Assert...ntainsOnlyInstancesOf() 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

1602
        $this->assertContainsOnlyInstancesOf(Customer::class, /** @scrutinizer ignore-type */ $aggregation);
Loading history...
1603
1604
        foreach ($aggregation as $item) {
1605
            if ($item->getStatus() === 1) {
1606
                $this->assertEquals(183, $item->sumTotal);
1607
            } elseif ($item->getStatus() === 2) {
1608
                $this->assertEquals(0, $item->sumTotal);
1609
            }
1610
        }
1611
1612
        /** tests with composite pk asArray */
1613
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
1614
        $aggregation = $orderItemQuery
1615
            ->select(['[[order_id]]', 'SUM([[subtotal]]) AS [[subtotal]]'])
1616
            ->joinWith('order', false)
1617
            ->groupBy('[[order_id]]')
1618
            ->orderBy('[[order_id]]')
1619
            ->asArray()
1620
            ->all();
1621
1622
        $expected = [
1623
            [
1624
                'order_id' => 1,
1625
                'subtotal' => 70,
1626
            ],
1627
            [
1628
                'order_id' => 2,
1629
                'subtotal' => 33,
1630
            ],
1631
            [
1632
                'order_id' => 3,
1633
                'subtotal' => 40,
1634
            ],
1635
        ];
1636
        $this->assertEquals($expected, $aggregation);
1637
1638
        /** tests with composite pk with Models */
1639
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
1640
        $aggregation = $orderItemQuery
1641
            ->select(['[[order_id]]', 'SUM([[subtotal]]) AS [[subtotal]]'])
1642
            ->joinWith('order', false)
1643
            ->groupBy('[[order_id]]')
1644
            ->orderBy('[[order_id]]')
1645
            ->all();
1646
1647
        $this->assertCount(3, $aggregation);
1648
        $this->assertContainsOnlyInstancesOf(OrderItem::class, $aggregation);
1649
1650
        foreach ($aggregation as $item) {
1651
            if ($item->getOrderId() === 1) {
1652
                $this->assertEquals(70, $item->getSubtotal());
1653
            } elseif ($item->getOrderId() === 2) {
1654
                $this->assertEquals(33, $item->getSubtotal());
1655
            } elseif ($item->getOrderId() === 3) {
1656
                $this->assertEquals(40, $item->getSubtotal());
1657
            }
1658
        }
1659
    }
1660
1661
    public function testLinkWhenRelationIsIndexed2(): void
1662
    {
1663
        $this->checkFixture($this->db, 'order');
1664
1665
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1666
        $order = $orderQuery->with('orderItems2')->where(['id' => 1])->onePopulate();
1667
1668
        $orderItem = new OrderItem($this->db);
1669
1670
        $orderItem->setOrderId($order->getId());
0 ignored issues
show
Bug introduced by
The method getId() 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...\CustomerWithProperties or Yiisoft\ActiveRecord\Tes...s\ActiveRecord\Customer or Yiisoft\ActiveRecord\Tes...tubs\ActiveRecord\Alpha or Yiisoft\ActiveRecord\Tes...bs\ActiveRecord\Dossier or Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Item or Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Beta or Yiisoft\ActiveRecord\Tes...tubs\ActiveRecord\Order or Yiisoft\ActiveRecord\Tes...s\ActiveRecord\Category or Yiisoft\ActiveRecord\Tes...eRecord\OrderWithNullFK. ( Ignorable by Annotation )

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

1670
        $orderItem->setOrderId($order->/** @scrutinizer ignore-call */ getId());
Loading history...
1671
        $orderItem->setItemId(3);
1672
        $orderItem->setQuantity(1);
1673
        $orderItem->setSubtotal(10.0);
1674
1675
        $order->link('orderItems2', $orderItem);
1676
        $this->assertTrue(isset($order->getOrderItems2()['3']));
0 ignored issues
show
Bug introduced by
The method getOrderItems2() 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. ( Ignorable by Annotation )

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

1676
        $this->assertTrue(isset($order->/** @scrutinizer ignore-call */ getOrderItems2()['3']));
Loading history...
1677
    }
1678
1679
    public function testEmulateExecution(): void
1680
    {
1681
        $this->checkFixture($this->db, 'order');
1682
1683
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1684
1685
        $this->assertGreaterThan(0, $customerQuery->count());
1686
        $this->assertSame([], $customerQuery->emulateExecution()->all());
1687
        $this->assertNull($customerQuery->emulateExecution()->one());
1688
        $this->assertFalse($customerQuery->emulateExecution()->exists());
1689
        $this->assertSame(0, $customerQuery->emulateExecution()->count());
1690
        $this->assertNull($customerQuery->emulateExecution()->sum('id'));
1691
        $this->assertNull($customerQuery->emulateExecution()->average('id'));
1692
        $this->assertNull($customerQuery->emulateExecution()->max('id'));
1693
        $this->assertNull($customerQuery->emulateExecution()->min('id'));
1694
        $this->assertNull($customerQuery->select(['id'])->emulateExecution()->scalar());
1695
        $this->assertSame([], $customerQuery->select(['id'])->emulateExecution()->column());
1696
    }
1697
1698
    /**
1699
     * {@see https://github.com/yiisoft/yii2/issues/12213}
1700
     */
1701
    public function testUnlinkAllOnCondition(): void
1702
    {
1703
        $this->checkFixture($this->db, 'item');
1704
1705
        /** Ensure there are three items with category_id = 2 in the Items table */
1706
        $itemQuery = new ActiveQuery(Item::class, $this->db);
1707
        $itemsCount = $itemQuery->where(['category_id' => 2])->count();
1708
        $this->assertEquals(3, $itemsCount);
1709
1710
        $categoryQuery = new ActiveQuery(Category::class, $this->db);
1711
        $categoryQuery = $categoryQuery->with('limitedItems')->where(['id' => 2]);
1712
1713
        /**
1714
         * Ensure that limitedItems relation returns only one item (category_id = 2 and id in (1,2,3))
1715
         */
1716
        $category = $categoryQuery->onePopulate();
1717
        $this->assertCount(1, $category->getLimitedItems());
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

1717
        $this->assertCount(1, $category->/** @scrutinizer ignore-call */ getLimitedItems());
Loading history...
1718
1719
        /** Unlink all items in the limitedItems relation */
1720
        $category->unlinkAll('limitedItems', true);
1721
1722
        /** Make sure that only one item was unlinked */
1723
        $itemsCount = $itemQuery->where(['category_id' => 2])->count();
1724
        $this->assertEquals(2, $itemsCount);
1725
1726
        /** Call $categoryQuery again to ensure no items were found */
1727
        $this->assertCount(0, $categoryQuery->onePopulate()->getLimitedItems());
1728
    }
1729
1730
    /**
1731
     * {@see https://github.com/yiisoft/yii2/issues/12213}
1732
     */
1733
    public function testUnlinkAllOnConditionViaTable(): void
1734
    {
1735
        $this->checkFixture($this->db, 'item', true);
1736
1737
        /** Ensure there are three items with category_id = 2 in the Items table */
1738
        $itemQuery = new ActiveQuery(Item::class, $this->db);
1739
        $itemsCount = $itemQuery->where(['category_id' => 2])->count();
1740
        $this->assertEquals(3, $itemsCount);
1741
1742
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1743
        $orderQuery = $orderQuery->with('limitedItems')->where(['id' => 2]);
1744
1745
        /**
1746
         * Ensure that limitedItems relation returns only one item (category_id = 2 and id in (4, 5)).
1747
         */
1748
        $category = $orderQuery->onePopulate();
1749
        $this->assertCount(2, $category->getLimitedItems());
1750
1751
        /** Unlink all items in the limitedItems relation */
1752
        $category->unlinkAll('limitedItems', true);
1753
1754
        /** Call $orderQuery again to ensure that links are removed */
1755
        $this->assertCount(0, $orderQuery->onePopulate()->getLimitedItems());
1756
1757
        /** Make sure that only links were removed, the items were not removed */
1758
        $this->assertEquals(3, $itemQuery->where(['category_id' => 2])->count());
1759
    }
1760
1761
    /**
1762
     * {@see https://github.com/yiisoft/yii2/pull/13891}
1763
     */
1764
    public function testIndexByAfterLoadingRelations(): void
1765
    {
1766
        $this->checkFixture($this->db, 'order');
1767
1768
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1769
        $orderQuery->with('customer')->indexBy(function (Order $order) {
1770
            $this->assertTrue($order->isRelationPopulated('customer'));
1771
            $this->assertNotEmpty($order->getCustomer()?->getId());
1772
1773
            return $order->getCustomer()?->getId();
1774
        })->all();
1775
1776
        $orders = $orderQuery->with('customer')->indexBy('customer.id')->all();
1777
1778
        foreach ($orders as $customer_id => $order) {
1779
            $this->assertEquals($customer_id, $order->getCustomerId());
1780
        }
1781
    }
1782
1783
    public static function filterTableNamesFromAliasesProvider(): array
1784
    {
1785
        return [
1786
            'table name as string' => ['customer', []],
1787
            'table name as array' => [['customer'], []],
1788
            'table names' => [['customer', 'order'], []],
1789
            'table name and a table alias' => [['customer', 'ord' => 'order'], ['ord']],
1790
            'table alias' => [['csr' => 'customer'], ['csr']],
1791
            'table aliases' => [['csr' => 'customer', 'ord' => 'order'], ['csr', 'ord']],
1792
        ];
1793
    }
1794
1795
    /**
1796
     * @dataProvider filterTableNamesFromAliasesProvider
1797
     *
1798
     * @param $expectedAliases
1799
     *
1800
     * @throws ReflectionException
1801
     */
1802
    public function testFilterTableNamesFromAliases(array|string $fromParams, array $expectedAliases): void
1803
    {
1804
        $this->checkFixture($this->db, 'customer');
1805
1806
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1807
1808
        $query = $customerQuery->from($fromParams);
1809
1810
        $aliases = Assert::invokeMethod(new Customer($this->db), 'filterValidAliases', [$query]);
1811
1812
        $this->assertEquals($expectedAliases, $aliases);
1813
    }
1814
1815
    public function testExtraFields(): void
1816
    {
1817
        $this->checkFixture($this->db, 'customer');
1818
1819
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1820
1821
        $query = $customerQuery->with('orders2')->where(['id' => 1])->onePopulate();
1822
        $this->assertCount(1, $query->getRelatedRecords());
0 ignored issues
show
Bug introduced by
The method getRelatedRecords() 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

1822
        $this->assertCount(1, $query->/** @scrutinizer ignore-call */ getRelatedRecords());
Loading history...
1823
        $this->assertCount(1, $query->extraFields());
0 ignored issues
show
Bug introduced by
The method extraFields() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. It seems like you code against a sub-type of Yiisoft\ActiveRecord\ActiveRecordInterface such as Yiisoft\ActiveRecord\Tests\Stubs\MagicActiveRecord or Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord. ( Ignorable by Annotation )

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

1823
        $this->assertCount(1, $query->/** @scrutinizer ignore-call */ extraFields());
Loading history...
1824
        $this->assertArrayHasKey('orders2', $query->getRelatedRecords());
0 ignored issues
show
Bug introduced by
It seems like $query->getRelatedRecords() can also be of type Countable; however, parameter $array of PHPUnit\Framework\Assert::assertArrayHasKey() does only seem to accept ArrayAccess|array, 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

1824
        $this->assertArrayHasKey('orders2', /** @scrutinizer ignore-type */ $query->getRelatedRecords());
Loading history...
1825
        $this->assertContains('orders2', $query->extraFields());
0 ignored issues
show
Bug introduced by
It seems like $query->extraFields() can also be of type Countable; however, parameter $haystack of PHPUnit\Framework\Assert::assertContains() 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

1825
        $this->assertContains('orders2', /** @scrutinizer ignore-type */ $query->extraFields());
Loading history...
1826
    }
1827
1828
    public static function tableNameProvider(): array
1829
    {
1830
        return [
1831
            ['order', 'order_item'],
1832
            ['order', '{{%order_item}}'],
1833
            ['{{%order}}', 'order_item'],
1834
            ['{{%order}}', '{{%order_item}}'],
1835
        ];
1836
    }
1837
1838
    /**
1839
     * Test whether conditions are quoted correctly in conditions where joinWith is used.
1840
     *
1841
     * {@see https://github.com/yiisoft/yii2/issues/11088}
1842
     *
1843
     * @dataProvider tableNameProvider
1844
     *
1845
     * @throws Exception|InvalidConfigException
1846
     */
1847
    public function testRelationWhereParams(string $orderTableName, string $orderItemTableName): void
1848
    {
1849
        $driverName = $this->db->getDriverName();
1850
1851
        $this->checkFixture($this->db, 'order');
1852
1853
        $order = new Order(db: $this->db, tableName: $orderTableName);
0 ignored issues
show
Unused Code introduced by
The assignment to $order is dead and can be removed.
Loading history...
1854
        $orderItem = new OrderItem(db: $this->db, tableName: $orderItemTableName);
0 ignored issues
show
Unused Code introduced by
The assignment to $orderItem is dead and can be removed.
Loading history...
1855
1856
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1857
        $order = $orderQuery->findOne(1);
1858
        $itemsSQL = $order->getOrderItemsQuery()->createCommand()->getRawSql();
0 ignored issues
show
Bug introduced by
The method getOrderItemsQuery() 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...icActiveRecord\Customer or Yiisoft\ActiveRecord\Tes...icActiveRecord\Category or Yiisoft\ActiveRecord\Tes...MagicActiveRecord\Order or 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

1858
        $itemsSQL = $order->/** @scrutinizer ignore-call */ getOrderItemsQuery()->createCommand()->getRawSql();
Loading history...
1859
        $expectedSQL = DbHelper::replaceQuotes(
1860
            <<<SQL
1861
            SELECT * FROM [[order_item]] WHERE [[order_id]]=1
1862
            SQL,
1863
            $driverName,
1864
        );
1865
        $this->assertEquals($expectedSQL, $itemsSQL);
1866
1867
        $order = $orderQuery->findOne(1);
1868
        $itemsSQL = $order->getOrderItemsQuery()->joinWith('item')->createCommand()->getRawSql();
1869
        $expectedSQL = DbHelper::replaceQuotes(
1870
            <<<SQL
1871
            SELECT [[order_item]].* FROM [[order_item]] LEFT JOIN [[item]] ON [[order_item]].[[item_id]] = [[item]].[[id]] WHERE [[order_item]].[[order_id]]=1
1872
            SQL,
1873
            $driverName,
1874
        );
1875
        $this->assertEquals($expectedSQL, $itemsSQL);
1876
    }
1877
1878
    public function testOutdatedRelationsAreResetForExistingRecords(): void
1879
    {
1880
        $this->checkFixture($this->db, 'order_item', true);
1881
1882
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
1883
        $orderItems = $orderItemQuery->findOne(1);
1884
        $this->assertEquals(1, $orderItems->getOrder()->getId());
0 ignored issues
show
Bug introduced by
The method getOrder() 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. ( Ignorable by Annotation )

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

1884
        $this->assertEquals(1, $orderItems->/** @scrutinizer ignore-call */ getOrder()->getId());
Loading history...
1885
        $this->assertEquals(1, $orderItems->getItem()->getId());
0 ignored issues
show
Bug introduced by
The method getItem() 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...\ActiveRecord\OrderItem. ( Ignorable by Annotation )

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

1885
        $this->assertEquals(1, $orderItems->/** @scrutinizer ignore-call */ getItem()->getId());
Loading history...
1886
1887
        /** test `__set()`. */
1888
        $orderItems->setOrderId(2);
0 ignored issues
show
Bug introduced by
The method setOrderId() 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. ( Ignorable by Annotation )

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

1888
        $orderItems->/** @scrutinizer ignore-call */ 
1889
                     setOrderId(2);
Loading history...
1889
        $orderItems->setItemId(1);
0 ignored issues
show
Bug introduced by
The method setItemId() 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. ( Ignorable by Annotation )

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

1889
        $orderItems->/** @scrutinizer ignore-call */ 
1890
                     setItemId(1);
Loading history...
1890
        $this->assertEquals(2, $orderItems->getOrder()->getId());
1891
        $this->assertEquals(1, $orderItems->getItem()->getId());
1892
1893
        /** Test `setAttribute()`. */
1894
        $orderItems->setAttribute('order_id', 3);
1895
        $orderItems->setAttribute('item_id', 1);
1896
        $this->assertEquals(3, $orderItems->getOrder()->getId());
1897
        $this->assertEquals(1, $orderItems->getItem()->getId());
1898
    }
1899
1900
    public function testOutdatedCompositeKeyRelationsAreReset(): void
1901
    {
1902
        $this->checkFixture($this->db, 'dossier');
1903
1904
        $dossierQuery = new ActiveQuery(Dossier::class, $this->db);
1905
1906
        $dossiers = $dossierQuery->findOne(['department_id' => 1, 'employee_id' => 1]);
1907
        $this->assertEquals('John Doe', $dossiers->getEmployee()->getFullName());
0 ignored issues
show
Bug introduced by
The method getEmployee() 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...bs\ActiveRecord\Dossier. ( Ignorable by Annotation )

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

1907
        $this->assertEquals('John Doe', $dossiers->/** @scrutinizer ignore-call */ getEmployee()->getFullName());
Loading history...
1908
1909
        $dossiers->setDepartmentId(2);
0 ignored issues
show
Bug introduced by
The method setDepartmentId() 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...bs\ActiveRecord\Dossier. ( Ignorable by Annotation )

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

1909
        $dossiers->/** @scrutinizer ignore-call */ 
1910
                   setDepartmentId(2);
Loading history...
1910
        $this->assertEquals('Ann Smith', $dossiers->getEmployee()->getFullName());
1911
1912
        $dossiers->setEmployeeId(2);
0 ignored issues
show
Bug introduced by
The method setEmployeeId() 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...bs\ActiveRecord\Dossier. ( Ignorable by Annotation )

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

1912
        $dossiers->/** @scrutinizer ignore-call */ 
1913
                   setEmployeeId(2);
Loading history...
1913
        $this->assertEquals('Will Smith', $dossiers->getEmployee()->getFullName());
1914
1915
        // Dossier::$employee_id property cannot be null
1916
        // unset($dossiers->employee_id);
1917
        // $this->assertNull($dossiers->getEmployee());
1918
1919
        $dossier = new Dossier($this->db);
1920
        $this->assertNull($dossier->getEmployee());
1921
1922
        $dossier->setEmployeeId(1);
1923
        $dossier->setDepartmentId(2);
1924
        $this->assertEquals('Ann Smith', $dossier->getEmployee()->getFullName());
1925
1926
        $dossier->setEmployeeId(2);
1927
        $this->assertEquals('Will Smith', $dossier->getEmployee()->getFullName());
1928
    }
1929
1930
    public function testOutdatedViaTableRelationsAreReset(): void
1931
    {
1932
        $this->checkFixture($this->db, 'order', true);
1933
1934
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1935
1936
        $orders = $orderQuery->findOne(1);
1937
        $orderItemIds = ArArrayHelper::getColumn($orders->getItems(), 'id');
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

1937
        $orderItemIds = ArArrayHelper::getColumn($orders->/** @scrutinizer ignore-call */ getItems(), 'id');
Loading history...
1938
        sort($orderItemIds);
1939
        $this->assertSame([1, 2], $orderItemIds);
1940
1941
        $orders->setId(2);
0 ignored issues
show
Bug introduced by
The method setId() 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...bs\ActiveRecord\Dossier 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

1941
        $orders->/** @scrutinizer ignore-call */ 
1942
                 setId(2);
Loading history...
1942
        sort($orderItemIds);
1943
        $orderItemIds = ArArrayHelper::getColumn($orders->getItems(), 'id');
1944
        $this->assertSame([3, 4, 5], $orderItemIds);
1945
1946
        $orders->setId(null);
1947
        $this->assertSame([], $orders->getItems());
1948
1949
        $order = new Order($this->db);
1950
        $this->assertSame([], $order->getItems());
1951
1952
        $order->setId(3);
1953
        $orderItemIds = ArArrayHelper::getColumn($order->getItems(), 'id');
1954
        $this->assertSame([2], $orderItemIds);
1955
    }
1956
1957
    public function testInverseOfDynamic(): void
1958
    {
1959
        $this->checkFixture($this->db, 'customer');
1960
1961
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
1962
1963
        $customer = $customerQuery->findOne(1);
1964
1965
        /** request the inverseOf relation without explicitly (eagerly) loading it */
1966
        $orders2 = $customer->getOrders2Query()->all();
1967
        $this->assertSame($customer, $orders2[0]->getCustomer2());
1968
1969
        $orders2 = $customer->getOrders2Query()->onePopulate();
1970
        $this->assertSame($customer, $orders2->getCustomer2());
1971
1972
        /**
1973
         * request the inverseOf relation while also explicitly eager loading it (while possible, this is of course
1974
         * redundant)
1975
         */
1976
        $orders2 = $customer->getOrders2Query()->with('customer2')->all();
1977
        $this->assertSame($customer, $orders2[0]->getCustomer2());
1978
1979
        $orders2 = $customer->getOrders2Query()->with('customer2')->onePopulate();
1980
        $this->assertSame($customer, $orders2->getCustomer2());
1981
1982
        /** request the inverseOf relation as array */
1983
        $orders2 = $customer->getOrders2Query()->asArray()->all();
1984
        $this->assertEquals($customer->getId(), $orders2[0]['customer2']->getId());
1985
1986
        $orders2 = $customer->getOrders2Query()->asArray()->onePopulate();
1987
        $this->assertEquals($customer->getId(), $orders2['customer2']->getId());
1988
    }
1989
1990
    public function testOptimisticLock(): void
1991
    {
1992
        $this->checkFixture($this->db, 'document');
1993
1994
        $documentQuery = new ActiveQuery(Document::class, $this->db);
1995
        $record = $documentQuery->findOne(1);
1996
1997
        $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...
1998
        $record->save();
1999
        $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...
2000
2001
        $record = $documentQuery->findOne(1);
2002
2003
        $record->content = 'Rewrite attempt content';
2004
        $record->version = 0;
2005
        $this->expectException(StaleObjectException::class);
2006
        $record->save();
2007
    }
2008
2009
    public function testOptimisticLockOnDelete(): void
2010
    {
2011
        $this->checkFixture($this->db, 'document', true);
2012
2013
        $documentQuery = new ActiveQuery(Document::class, $this->db);
2014
        $document = $documentQuery->findOne(1);
2015
2016
        $this->assertSame(0, $document->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...
2017
2018
        $document->version = 1;
2019
2020
        $this->expectException(StaleObjectException::class);
2021
        $document->delete();
2022
    }
2023
2024
    public function testOptimisticLockAfterDelete(): void
2025
    {
2026
        $this->checkFixture($this->db, 'document', true);
2027
2028
        $documentQuery = new ActiveQuery(Document::class, $this->db);
2029
        $document = $documentQuery->findOne(1);
2030
2031
        $this->assertSame(0, $document->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...
2032
        $this->assertSame(1, $document->delete());
2033
        $this->assertTrue($document->getIsNewRecord());
2034
2035
        $this->expectException(StaleObjectException::class);
2036
        $document->delete();
2037
    }
2038
2039
    /**
2040
     * {@see https://github.com/yiisoft/yii2/issues/9006}
2041
     */
2042
    public function testBit(): void
2043
    {
2044
        $this->checkFixture($this->db, 'bit_values');
2045
2046
        $bitValueQuery = new ActiveQuery(BitValues::class, $this->db);
2047
        $falseBit = $bitValueQuery->findOne(1);
2048
        $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...
2049
2050
        $bitValueQuery = new ActiveQuery(BitValues::class, $this->db);
2051
        $trueBit = $bitValueQuery->findOne(2);
2052
        $this->assertEquals(true, $trueBit->val);
2053
    }
2054
2055
    public function testUpdateAttributes(): void
2056
    {
2057
        $this->checkFixture($this->db, 'order');
2058
2059
        $orderQuery = new ActiveQuery(Order::class, $this->db);
2060
        $order = $orderQuery->findOne(1);
2061
        $newTotal = 978;
2062
        $this->assertSame(1, $order->updateAttributes(['total' => $newTotal]));
2063
        $this->assertEquals($newTotal, $order->getTotal());
0 ignored issues
show
Bug introduced by
The method getTotal() 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...eRecord\OrderWithNullFK. ( Ignorable by Annotation )

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

2063
        $this->assertEquals($newTotal, $order->/** @scrutinizer ignore-call */ getTotal());
Loading history...
2064
2065
        $order = $orderQuery->findOne(1);
2066
        $this->assertEquals($newTotal, $order->getTotal());
2067
2068
        /** @see https://github.com/yiisoft/yii2/issues/12143 */
2069
        $newOrder = new Order($this->db);
2070
        $this->assertTrue($newOrder->getIsNewRecord());
2071
2072
        $newTotal = 200;
2073
        $this->assertSame(0, $newOrder->updateAttributes(['total' => $newTotal]));
2074
        $this->assertTrue($newOrder->getIsNewRecord());
2075
        $this->assertEquals($newTotal, $newOrder->getTotal());
2076
    }
2077
2078
    /**
2079
     * Ensure no ambiguous column error occurs if ActiveQuery adds a JOIN.
2080
     *
2081
     * {@see https://github.com/yiisoft/yii2/issues/13757}
2082
     */
2083
    public function testAmbiguousColumnFindOne(): void
2084
    {
2085
        $this->checkFixture($this->db, 'customer');
2086
2087
        $customerQuery = new CustomerQuery(Customer::class, $this->db);
2088
2089
        $customerQuery->joinWithProfile = true;
2090
2091
        $arClass = $customerQuery->findOne(1);
2092
2093
        $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

2093
        $this->assertTrue($arClass->/** @scrutinizer ignore-call */ refresh());
Loading history...
2094
2095
        $customerQuery->joinWithProfile = false;
2096
    }
2097
2098
    public function testCustomARRelation(): void
2099
    {
2100
        $this->checkFixture($this->db, 'order_item');
2101
2102
        $orderItem = new ActiveQuery(OrderItem::class, $this->db);
2103
2104
        $orderItem = $orderItem->findOne(1);
2105
2106
        $this->assertInstanceOf(Order::class, $orderItem->getCustom());
0 ignored issues
show
Bug introduced by
The method getCustom() 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. ( Ignorable by Annotation )

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

2106
        $this->assertInstanceOf(Order::class, $orderItem->/** @scrutinizer ignore-call */ getCustom());
Loading history...
2107
    }
2108
2109
    public function testGetAttributes(): void
2110
    {
2111
        $attributesExpected = [];
2112
        $this->checkFixture($this->db, 'customer');
2113
2114
        $attributesExpected['id'] = 1;
2115
        $attributesExpected['email'] = '[email protected]';
2116
        $attributesExpected['name'] = 'user1';
2117
        $attributesExpected['address'] = 'address1';
2118
        $attributesExpected['status'] = 1;
2119
        $attributesExpected['bool_status'] = true;
2120
        $attributesExpected['profile_id'] = 1;
2121
2122
        $customer = new ActiveQuery(Customer::class, $this->db);
2123
2124
        $attributes = $customer->findOne(1)->getAttributes();
2125
2126
        $this->assertEquals($attributes, $attributesExpected);
2127
    }
2128
2129
    public function testGetAttributesOnly(): void
2130
    {
2131
        $this->checkFixture($this->db, 'customer');
2132
2133
        $customer = new ActiveQuery(Customer::class, $this->db);
2134
2135
        $attributes = $customer->findOne(1)->getAttributes(['id', 'email', 'name']);
2136
2137
        $this->assertEquals(['id' => 1, 'email' => '[email protected]', 'name' => 'user1'], $attributes);
2138
    }
2139
2140
    public function testGetAttributesExcept(): void
2141
    {
2142
        $this->checkFixture($this->db, 'customer');
2143
2144
        $customer = new ActiveQuery(Customer::class, $this->db);
2145
2146
        $attributes = $customer->findOne(1)->getAttributes(null, ['status', 'bool_status', 'profile_id']);
2147
2148
        $this->assertEquals(
2149
            $attributes,
2150
            ['id' => 1, 'email' => '[email protected]', 'name' => 'user1', 'address' => 'address1']
2151
        );
2152
    }
2153
2154
    public function testFields(): void
2155
    {
2156
        $this->checkFixture($this->db, 'order_item');
2157
2158
        $orderItem = new ActiveQuery(OrderItem::class, $this->db);
2159
2160
        $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. It seems like you code against a sub-type of Yiisoft\ActiveRecord\ActiveRecordInterface such as Yiisoft\ActiveRecord\Tests\Stubs\MagicActiveRecord or Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord. ( Ignorable by Annotation )

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

2160
        $fields = $orderItem->findOne(['order_id' => 1, 'item_id' => 2])->/** @scrutinizer ignore-call */ fields();
Loading history...
2161
2162
        $this->assertEquals(
2163
            $fields,
2164
            ['order_id' => 1, 'item_id' => 2, 'quantity' => 2, 'subtotal' => '40', 'price' => 20]
2165
        );
2166
    }
2167
2168
    public function testGetOldAttribute(): void
2169
    {
2170
        $this->checkFixture($this->db, 'customer');
2171
2172
        $customer = new ActiveQuery(Customer::class, $this->db);
2173
2174
        $query = $customer->findOne(1);
2175
        $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 getOldAttributes()? ( Ignorable by Annotation )

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

2175
        $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...
2176
        $this->assertEquals($query->getAttributes(), $query->getOldAttributes());
2177
2178
        $query->setAttribute('name', 'samdark');
2179
        $this->assertEquals('samdark', $query->getAttribute('name'));
2180
        $this->assertEquals('user1', $query->getOldAttribute('name'));
2181
        $this->assertNotEquals($query->getAttribute('name'), $query->getOldAttribute('name'));
2182
    }
2183
2184
    public function testGetOldAttributes(): void
2185
    {
2186
        $attributes = [];
2187
        $attributesNew = [];
2188
        $this->checkFixture($this->db, 'customer');
2189
2190
        $attributes['id'] = 1;
2191
        $attributes['email'] = '[email protected]';
2192
        $attributes['name'] = 'user1';
2193
        $attributes['address'] = 'address1';
2194
        $attributes['status'] = 1;
2195
        $attributes['bool_status'] = true;
2196
        $attributes['profile_id'] = 1;
2197
2198
        $customer = new ActiveQuery(Customer::class, $this->db);
2199
2200
        $query = $customer->findOne(1);
2201
        $this->assertEquals($query->getAttributes(), $attributes);
2202
        $this->assertEquals($query->getAttributes(), $query->getOldAttributes());
2203
2204
        $query->setAttribute('name', 'samdark');
2205
        $attributesNew['id'] = 1;
2206
        $attributesNew['email'] = '[email protected]';
2207
        $attributesNew['name'] = 'samdark';
2208
        $attributesNew['address'] = 'address1';
2209
        $attributesNew['status'] = 1;
2210
        $attributesNew['bool_status'] = true;
2211
        $attributesNew['profile_id'] = 1;
2212
2213
        $this->assertEquals($attributesNew, $query->getAttributes());
2214
        $this->assertEquals($attributes, $query->getOldAttributes());
2215
        $this->assertNotEquals($query->getAttributes(), $query->getOldAttributes());
2216
    }
2217
2218
    public function testIsAttributeChanged(): void
2219
    {
2220
        $this->checkFixture($this->db, 'customer');
2221
2222
        $customer = new ActiveQuery(Customer::class, $this->db);
2223
2224
        $query = $customer->findOne(1);
2225
        $this->assertEquals('user1', $query->getAttribute('name'));
2226
        $this->assertEquals('user1', $query->getOldAttribute('name'));
2227
2228
        $query->setAttribute('name', 'samdark');
2229
        $this->assertEquals('samdark', $query->getAttribute('name'));
2230
        $this->assertEquals('user1', $query->getOldAttribute('name'));
2231
        $this->assertNotEquals($query->getAttribute('name'), $query->getOldAttribute('name'));
2232
        $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

2232
        $this->assertTrue($query->/** @scrutinizer ignore-call */ isAttributeChanged('name', true));
Loading history...
2233
    }
2234
2235
    public function testIsAttributeChangedNotIdentical(): void
2236
    {
2237
        $this->checkFixture($this->db, 'customer');
2238
2239
        $customer = new ActiveQuery(Customer::class, $this->db);
2240
2241
        $query = $customer->findOne(1);
2242
        $this->assertEquals('user1', $query->getAttribute('name'));
2243
        $this->assertEquals('user1', $query->getOldAttribute('name'));
2244
2245
        $query->setAttribute('name', 'samdark');
2246
        $this->assertEquals('samdark', $query->getAttribute('name'));
2247
        $this->assertEquals('user1', $query->getOldAttribute('name'));
2248
        $this->assertNotEquals($query->getAttribute('name'), $query->getOldAttribute('name'));
2249
        $this->assertTrue($query->isAttributeChanged('name', false));
2250
    }
2251
2252
    public function testOldAttributeAfterInsertAndUpdate(): void
2253
    {
2254
        $this->checkFixture($this->db, 'customer');
2255
2256
        $customer = new Customer($this->db);
2257
2258
        $customer->setAttributes([
2259
            'email' => '[email protected]',
2260
            'name' => 'Jack',
2261
            'address' => '123 Ocean Dr',
2262
            'status' => 1,
2263
        ]);
2264
2265
        $this->assertNull($customer->getOldAttribute('name'));
2266
        $this->assertTrue($customer->save());
2267
        $this->assertSame('Jack', $customer->getOldAttribute('name'));
2268
2269
        $customer->setAttribute('name', 'Harry');
2270
2271
        $this->assertTrue($customer->save());
2272
        $this->assertSame('Harry', $customer->getOldAttribute('name'));
2273
    }
2274
2275
    public function testCheckRelationUnknownPropertyException(): void
2276
    {
2277
        self::markTestSkipped('There is no check for access to an unknown property.');
2278
2279
        $this->checkFixture($this->db, 'customer');
2280
2281
        $customer = new ActiveQuery(Customer::class, $this->db);
2282
2283
        $query = $customer->findOne(1);
2284
2285
        $this->expectException(UnknownPropertyException::class);
2286
        $this->expectExceptionMessage('Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer::noExist');
2287
        $query->noExist;
2288
    }
2289
2290
    public function testCheckRelationInvalidCallException(): void
2291
    {
2292
        self::markTestSkipped('There is no check for access to an unknown property.');
2293
2294
        $this->checkFixture($this->db, 'customer');
2295
2296
        $customer = new ActiveQuery(Customer::class, $this->db);
2297
2298
        $query = $customer->findOne(2);
2299
2300
        $this->expectException(InvalidCallException::class);
2301
        $this->expectExceptionMessage(
2302
            'Getting write-only property: Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer::ordersReadOnly'
2303
        );
2304
        $query->ordersReadOnly;
2305
    }
2306
2307
    public function testGetRelationInvalidArgumentException(): void
2308
    {
2309
        $this->checkFixture($this->db, 'customer');
2310
2311
        $customer = new ActiveQuery(Customer::class, $this->db);
2312
2313
        $query = $customer->findOne(1);
2314
2315
        /** Throwing exception */
2316
        $this->expectException(InvalidArgumentException::class);
2317
        $this->expectExceptionMessage(
2318
            'Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer has no relation named "items".'
2319
        );
2320
        $query->relationQuery('items');
2321
    }
2322
2323
    public function testGetRelationInvalidArgumentExceptionHasNoRelationNamed(): void
2324
    {
2325
        self::markTestSkipped('The same as test testGetRelationInvalidArgumentException()');
2326
2327
        $this->checkFixture($this->db, 'customer');
2328
2329
        $customer = new ActiveQuery(Customer::class, $this->db);
2330
2331
        $query = $customer->findOne(1);
2332
2333
        $this->expectException(InvalidArgumentException::class);
2334
        $this->expectExceptionMessage(
2335
            'Relation query method "Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer::getItemQuery()" should'
2336
            . ' return type "Yiisoft\ActiveRecord\ActiveQueryInterface", but  returns "void" type.'
2337
        );
2338
        $query->relationQuery('item');
2339
    }
2340
2341
    public function testGetRelationInvalidArgumentExceptionCaseSensitive(): void
2342
    {
2343
        self::markTestSkipped('The same as test testGetRelationInvalidArgumentException()');
2344
2345
        $this->checkFixture($this->db, 'customer');
2346
2347
        $customer = new ActiveQuery(Customer::class, $this->db);
2348
2349
        $query = $customer->findOne(1);
2350
2351
        $this->expectException(InvalidArgumentException::class);
2352
        $this->expectExceptionMessage(
2353
            'Relation names are case sensitive. Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer ' .
2354
            'has a relation named "expensiveOrders" instead of "expensiveorders"'
2355
        );
2356
        $query->relationQuery('expensiveorders');
2357
    }
2358
2359
    public function testExists(): void
2360
    {
2361
        $this->checkFixture($this->db, 'customer');
2362
2363
        $customer = new ActiveQuery(Customer::class, $this->db);
2364
2365
        $this->assertTrue($customer->where(['id' => 2])->exists());
2366
        $this->assertFalse($customer->where(['id' => 5])->exists());
2367
        $this->assertTrue($customer->where(['name' => 'user1'])->exists());
2368
        $this->assertFalse($customer->where(['name' => 'user5'])->exists());
2369
        $this->assertTrue($customer->where(['id' => [2, 3]])->exists());
2370
        $this->assertTrue($customer->where(['id' => [2, 3]])->offset(1)->exists());
2371
        $this->assertFalse($customer->where(['id' => [2, 3]])->offset(2)->exists());
2372
    }
2373
2374
    public function testUnlink(): void
2375
    {
2376
        $this->checkFixture($this->db, 'customer');
2377
2378
        /** has many without delete */
2379
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2380
        $customer = $customerQuery->findOne(2);
2381
        $this->assertCount(2, $customer->getOrdersWithNullFK());
0 ignored issues
show
Bug introduced by
The method getOrdersWithNullFK() 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

2381
        $this->assertCount(2, $customer->/** @scrutinizer ignore-call */ getOrdersWithNullFK());
Loading history...
2382
        $customer->unlink('ordersWithNullFK', $customer->getOrdersWithNullFK()[1], false);
2383
        $this->assertCount(1, $customer->getOrdersWithNullFK());
2384
2385
        $orderWithNullFKQuery = new ActiveQuery(OrderWithNullFK::class, $this->db);
2386
        $orderWithNullFK = $orderWithNullFKQuery->findOne(3);
2387
        $this->assertEquals(3, $orderWithNullFK->getId());
2388
        $this->assertNull($orderWithNullFK->getCustomerId());
0 ignored issues
show
Bug introduced by
The method getCustomerId() 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...eRecord\OrderWithNullFK. ( Ignorable by Annotation )

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

2388
        $this->assertNull($orderWithNullFK->/** @scrutinizer ignore-call */ getCustomerId());
Loading history...
2389
2390
        /** has many with delete */
2391
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2392
        $customer = $customerQuery->findOne(2);
2393
        $this->assertCount(2, $customer->getOrders());
2394
2395
        $customer->unlink('orders', $customer->getOrders()[1], true);
2396
        $this->assertCount(1, $customer->getOrders());
2397
2398
        $orderQuery = new ActiveQuery(Order::class, $this->db);
2399
        $this->assertNull($orderQuery->findOne(3));
2400
2401
        /** via model with delete */
2402
        $orderQuery = new ActiveQuery(Order::class, $this->db);
2403
        $order = $orderQuery->findOne(2);
2404
        $this->assertCount(3, $order->getItems());
2405
        $this->assertCount(3, $order->getOrderItems());
2406
        $order->unlink('items', $order->getItems()[2], true);
2407
        $this->assertCount(2, $order->getItems());
2408
        $this->assertCount(2, $order->getOrderItems());
2409
2410
        /** via model without delete */
2411
        $this->assertCount(2, $order->getItemsWithNullFK());
0 ignored issues
show
Bug introduced by
The method getItemsWithNullFK() 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

2411
        $this->assertCount(2, $order->/** @scrutinizer ignore-call */ getItemsWithNullFK());
Loading history...
2412
        $order->unlink('itemsWithNullFK', $order->getItemsWithNullFK()[1], false);
2413
2414
        $this->assertCount(1, $order->getItemsWithNullFK());
2415
        $this->assertCount(2, $order->getOrderItems());
2416
    }
2417
2418
    public function testUnlinkAllAndConditionSetNull(): void
2419
    {
2420
        $this->checkFixture($this->db, 'order_item_with_null_fk');
2421
2422
        /** in this test all orders are owned by customer 1 */
2423
        $orderWithNullFKInstance = new OrderWithNullFK($this->db);
2424
        $orderWithNullFKInstance->updateAll(['customer_id' => 1]);
2425
2426
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2427
        $customer = $customerQuery->findOne(1);
2428
        $this->assertCount(3, $customer->getOrdersWithNullFK());
2429
        $this->assertCount(1, $customer->getExpensiveOrdersWithNullFK());
0 ignored issues
show
Bug introduced by
The method getExpensiveOrdersWithNullFK() 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

2429
        $this->assertCount(1, $customer->/** @scrutinizer ignore-call */ getExpensiveOrdersWithNullFK());
Loading history...
2430
2431
        $orderWithNullFKQuery = new ActiveQuery(OrderWithNullFK::class, $this->db);
2432
        $this->assertEquals(3, $orderWithNullFKQuery->count());
2433
2434
        $customer->unlinkAll('expensiveOrdersWithNullFK');
2435
        $this->assertCount(3, $customer->getOrdersWithNullFK());
2436
        $this->assertCount(0, $customer->getExpensiveOrdersWithNullFK());
2437
        $this->assertEquals(3, $orderWithNullFKQuery->count());
2438
2439
        $customer = $customerQuery->findOne(1);
2440
        $this->assertCount(2, $customer->getOrdersWithNullFK());
2441
        $this->assertCount(0, $customer->getExpensiveOrdersWithNullFK());
2442
    }
2443
2444
    public function testUnlinkAllAndConditionDelete(): void
2445
    {
2446
        $this->checkFixture($this->db, 'customer', true);
2447
2448
        /** in this test all orders are owned by customer 1 */
2449
        $orderInstance = new Order($this->db);
2450
        $orderInstance->updateAll(['customer_id' => 1]);
2451
2452
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2453
        $customer = $customerQuery->findOne(1);
2454
        $this->assertCount(3, $customer->getOrders());
2455
        $this->assertCount(1, $customer->getExpensiveOrders());
0 ignored issues
show
Bug introduced by
The method getExpensiveOrders() 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

2455
        $this->assertCount(1, $customer->/** @scrutinizer ignore-call */ getExpensiveOrders());
Loading history...
2456
2457
        $orderQuery = new ActiveQuery(Order::class, $this->db);
2458
        $this->assertEquals(3, $orderQuery->count());
2459
2460
        $customer->unlinkAll('expensiveOrders', true);
2461
        $this->assertCount(3, $customer->getOrders());
2462
        $this->assertCount(0, $customer->getExpensiveOrders());
2463
        $this->assertEquals(2, $orderQuery->count());
2464
2465
        $customer = $customerQuery->findOne(1);
2466
        $this->assertCount(2, $customer->getOrders());
2467
        $this->assertCount(0, $customer->getExpensiveOrders());
2468
    }
2469
2470
    public function testUpdate(): void
2471
    {
2472
        $this->checkFixture($this->db, 'customer');
2473
2474
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2475
        $customer = $customerQuery->findOne(2);
2476
        $this->assertInstanceOf(Customer::class, $customer);
2477
        $this->assertEquals('user2', $customer->getAttribute('name'));
2478
        $this->assertFalse($customer->getIsNewRecord());
2479
        $this->assertEmpty($customer->getDirtyAttributes());
2480
2481
        $customer->setAttribute('name', 'user2x');
2482
        $customer->save();
2483
        $this->assertEquals('user2x', $customer->getAttribute('name'));
2484
        $this->assertFalse($customer->getIsNewRecord());
2485
2486
        $customer2 = $customerQuery->findOne(2);
2487
        $this->assertEquals('user2x', $customer2->getAttribute('name'));
2488
2489
        /** no update */
2490
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2491
        $customer = $customerQuery->findOne(1);
2492
2493
        $customer->setAttribute('name', 'user1');
2494
        $this->assertEquals(0, $customer->update());
2495
2496
        /** updateAll */
2497
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2498
        $customer = $customerQuery->findOne(3);
2499
        $this->assertEquals('user3', $customer->getAttribute('name'));
2500
2501
        $ret = $customer->updateAll(['name' => 'temp'], ['id' => 3]);
2502
        $this->assertEquals(1, $ret);
2503
2504
        $customer = $customerQuery->findOne(3);
2505
        $this->assertEquals('temp', $customer->getAttribute('name'));
2506
2507
        $ret = $customer->updateAll(['name' => 'tempX']);
2508
        $this->assertEquals(3, $ret);
2509
2510
        $ret = $customer->updateAll(['name' => 'temp'], ['name' => 'user6']);
2511
        $this->assertEquals(0, $ret);
2512
    }
2513
2514
    public function testUpdateCounters(): void
2515
    {
2516
        $this->checkFixture($this->db, 'order_item', true);
2517
2518
        /** updateCounters */
2519
        $pk = ['order_id' => 2, 'item_id' => 4];
2520
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
2521
        $orderItem = $orderItemQuery->findOne($pk);
2522
        $this->assertEquals(1, $orderItem->getQuantity());
0 ignored issues
show
Bug introduced by
The method getQuantity() 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. ( Ignorable by Annotation )

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

2522
        $this->assertEquals(1, $orderItem->/** @scrutinizer ignore-call */ getQuantity());
Loading history...
2523
2524
        $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

2524
        /** @scrutinizer ignore-call */ 
2525
        $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...
2525
        $this->assertTrue($ret);
2526
        $this->assertEquals(0, $orderItem->getQuantity());
2527
2528
        $orderItem = $orderItemQuery->findOne($pk);
2529
        $this->assertEquals(0, $orderItem->getQuantity());
2530
2531
        /** updateAllCounters */
2532
        $pk = ['order_id' => 1, 'item_id' => 2];
2533
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
2534
        $orderItem = $orderItemQuery->findOne($pk);
2535
        $this->assertEquals(2, $orderItem->getQuantity());
2536
2537
        $orderItem = new OrderItem($this->db);
2538
        $ret = $orderItem->updateAllCounters(['quantity' => 3, 'subtotal' => -10], $pk);
2539
        $this->assertEquals(1, $ret);
2540
2541
        $orderItem = $orderItemQuery->findOne($pk);
2542
        $this->assertEquals(5, $orderItem->getQuantity());
2543
        $this->assertEquals(30, $orderItem->getSubtotal());
0 ignored issues
show
Bug introduced by
The method getSubtotal() 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. ( Ignorable by Annotation )

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

2543
        $this->assertEquals(30, $orderItem->/** @scrutinizer ignore-call */ getSubtotal());
Loading history...
2544
    }
2545
2546
    public function testDelete(): void
2547
    {
2548
        $this->checkFixture($this->db, 'customer', true);
2549
2550
        /** delete */
2551
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2552
        $customer = $customerQuery->findOne(2);
2553
        $this->assertInstanceOf(Customer::class, $customer);
2554
        $this->assertEquals('user2', $customer->getName());
2555
2556
        $customer->delete();
2557
2558
        $customer = $customerQuery->findOne(2);
2559
        $this->assertNull($customer);
2560
2561
        /** deleteAll */
2562
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2563
        $customers = $customerQuery->all();
2564
        $this->assertCount(2, $customers);
2565
2566
        $customer = new Customer($this->db);
2567
        $ret = $customer->deleteAll();
2568
        $this->assertEquals(2, $ret);
2569
2570
        $customers = $customerQuery->all();
2571
        $this->assertCount(0, $customers);
2572
2573
        $ret = $customer->deleteAll();
2574
        $this->assertEquals(0, $ret);
2575
    }
2576
2577
    /**
2578
     * {@see https://github.com/yiisoft/yii2/issues/17089}
2579
     */
2580
    public function testViaWithCallable(): void
2581
    {
2582
        $this->checkFixture($this->db, 'order', true);
2583
2584
        $orderQuery = new ActiveQuery(Order::class, $this->db);
2585
2586
        $order = $orderQuery->findOne(2);
2587
2588
        $expensiveItems = $order->getExpensiveItemsUsingViaWithCallable();
0 ignored issues
show
Bug introduced by
The method getExpensiveItemsUsingViaWithCallable() 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

2588
        /** @scrutinizer ignore-call */ 
2589
        $expensiveItems = $order->getExpensiveItemsUsingViaWithCallable();
Loading history...
2589
        $cheapItems = $order->getCheapItemsUsingViaWithCallable();
0 ignored issues
show
Bug introduced by
The method getCheapItemsUsingViaWithCallable() 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

2589
        /** @scrutinizer ignore-call */ 
2590
        $cheapItems = $order->getCheapItemsUsingViaWithCallable();
Loading history...
2590
2591
        $this->assertCount(2, $expensiveItems);
2592
        $this->assertEquals(4, $expensiveItems[0]->getId());
2593
        $this->assertEquals(5, $expensiveItems[1]->getId());
2594
        $this->assertCount(1, $cheapItems);
2595
        $this->assertEquals(3, $cheapItems[0]->getId());
2596
    }
2597
2598
    public function testLink(): void
2599
    {
2600
        $this->checkFixture($this->db, 'customer', true);
2601
2602
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2603
        $customer = $customerQuery->findOne(2);
2604
        $this->assertCount(2, $customer->getOrders());
2605
2606
        /** has many */
2607
        $order = new Order($this->db);
2608
2609
        $order->setTotal(100);
2610
        $order->setCreatedAt(time());
2611
        $this->assertTrue($order->getIsNewRecord());
2612
2613
        /** belongs to */
2614
        $order = new Order($this->db);
2615
2616
        $order->setTotal(100);
2617
        $order->setCreatedAt(time());
2618
        $this->assertTrue($order->getIsNewRecord());
2619
2620
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2621
        $customer = $customerQuery->findOne(1);
2622
        $this->assertNull($order->getCustomer());
2623
2624
        $order->link('customer', $customer);
0 ignored issues
show
Bug introduced by
It seems like $customer can also be of type array and null; however, parameter $arClass of Yiisoft\ActiveRecord\AbstractActiveRecord::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

2624
        $order->link('customer', /** @scrutinizer ignore-type */ $customer);
Loading history...
2625
        $this->assertFalse($order->getIsNewRecord());
2626
        $this->assertEquals(1, $order->getCustomerId());
2627
        $this->assertEquals(1, $order->getCustomer()->getPrimaryKey());
2628
2629
        /** via model */
2630
        $orderQuery = new ActiveQuery(Order::class, $this->db);
2631
        $order = $orderQuery->findOne(1);
2632
        $this->assertCount(2, $order->getItems());
2633
        $this->assertCount(2, $order->getOrderItems());
2634
2635
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
2636
        $orderItem = $orderItemQuery->findOne(['order_id' => 1, 'item_id' => 3]);
2637
        $this->assertNull($orderItem);
2638
2639
        $itemQuery = new ActiveQuery(Item::class, $this->db);
2640
        $item = $itemQuery->findOne(3);
2641
        $order->link('items', $item, ['quantity' => 10, 'subtotal' => 100]);
2642
        $this->assertCount(3, $order->getItems());
2643
        $this->assertCount(3, $order->getOrderItems());
2644
2645
        $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db);
2646
        $orderItem = $orderItemQuery->findOne(['order_id' => 1, 'item_id' => 3]);
2647
        $this->assertInstanceOf(OrderItem::class, $orderItem);
2648
        $this->assertEquals(10, $orderItem->getQuantity());
2649
        $this->assertEquals(100, $orderItem->getSubtotal());
2650
    }
2651
2652
    public function testEqual(): void
2653
    {
2654
        $this->checkFixture($this->db, 'customer');
2655
2656
        $customerA = (new ActiveQuery(Customer::class, $this->db))->findOne(1);
2657
        $customerB = (new ActiveQuery(Customer::class, $this->db))->findOne(2);
2658
        $this->assertFalse($customerA->equals($customerB));
0 ignored issues
show
Bug introduced by
It seems like $customerB can also be of type array and 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

2658
        $this->assertFalse($customerA->equals(/** @scrutinizer ignore-type */ $customerB));
Loading history...
2659
2660
        $customerB = (new ActiveQuery(Customer::class, $this->db))->findOne(1);
2661
        $this->assertTrue($customerA->equals($customerB));
2662
2663
        $customerA = (new ActiveQuery(Customer::class, $this->db))->findOne(1);
2664
        $customerB = (new ActiveQuery(Item::class, $this->db))->findOne(1);
2665
        $this->assertFalse($customerA->equals($customerB));
2666
    }
2667
2668
    public function testARClassAsString(): void
2669
    {
2670
        $query = new ActiveQuery(Customer::class, $this->db);
2671
2672
        $this->assertSame($query->getARClass(), Customer::class);
2673
        $this->assertSame($query->getARClassName(), Customer::class);
2674
        $this->assertInstanceOf(Customer::class, $query->getARInstance());
2675
    }
2676
2677
    public function testARClassAsInstance(): void
2678
    {
2679
        $customer = new Customer($this->db);
2680
        $query = new ActiveQuery($customer, $this->db);
2681
2682
        $this->assertSame($query->getARClass(), $customer);
2683
        $this->assertSame($query->getARClassName(), Customer::class);
2684
        $this->assertInstanceOf(Customer::class, $query->getARInstance());
2685
    }
2686
2687
    public function testARClassAsClosure(): void
2688
    {
2689
        $closure = fn () => new Customer($this->db);
2690
        $query = new ActiveQuery($closure, $this->db);
2691
2692
        $this->assertSame($query->getARClass(), $closure);
2693
        $this->assertSame($query->getARClassName(), Customer::class);
2694
        $this->assertInstanceOf(Customer::class, $query->getARInstance());
2695
    }
2696
}
2697