ActiveQueryTest::testJoinWithAlias()   F
last analyzed

Complexity

Conditions 35
Paths > 20000

Size

Total Lines 233
Code Lines 166

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 35
eloc 166
nc 65536
nop 1
dl 0
loc 233
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

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

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

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

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

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

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

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

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

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

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

1475
        $order->/** @scrutinizer ignore-call */ 
1476
                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...
1476
        $this->assertEquals(5, $itemQuery->count());
1477
        $this->assertEquals($orderItemCount - 2, $orderItemQuery->count());
1478
        $this->assertCount(0, $order->getBooksViaTable());
1479
1480
        /** via table without delete */
1481
        $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

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

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

1671
        $orderItem->setOrderId($order->/** @scrutinizer ignore-call */ getId());
Loading history...
1672
        $orderItem->setItemId(3);
1673
        $orderItem->setQuantity(1);
1674
        $orderItem->setSubtotal(10.0);
1675
1676
        $order->link('orderItems2', $orderItem);
1677
        $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

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

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

1823
        $this->assertCount(1, $query->/** @scrutinizer ignore-call */ getRelatedRecords());
Loading history...
1824
        $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

1824
        $this->assertCount(1, $query->/** @scrutinizer ignore-call */ extraFields());
Loading history...
1825
        $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

1825
        $this->assertArrayHasKey('orders2', /** @scrutinizer ignore-type */ $query->getRelatedRecords());
Loading history...
1826
        $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

1826
        $this->assertContains('orders2', /** @scrutinizer ignore-type */ $query->extraFields());
Loading history...
1827
    }
1828
1829
    public static function tableNameProvider(): array
1830
    {
1831
        return [
1832
            ['order', 'order_item'],
1833
            ['order', '{{%order_item}}'],
1834
            ['{{%order}}', 'order_item'],
1835
            ['{{%order}}', '{{%order_item}}'],
1836
        ];
1837
    }
1838
1839
    /**
1840
     * Test whether conditions are quoted correctly in conditions where joinWith is used.
1841
     *
1842
     * {@see https://github.com/yiisoft/yii2/issues/11088}
1843
     *
1844
     * @dataProvider tableNameProvider
1845
     *
1846
     * @throws Exception|InvalidConfigException
1847
     */
1848
    public function testRelationWhereParams(string $orderTableName, string $orderItemTableName): void
1849
    {
1850
        $driverName = $this->db->getDriverName();
1851
1852
        $this->checkFixture($this->db, 'order');
1853
1854
        $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...
1855
        $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...
1856
1857
        $orderQuery = new ActiveQuery(Order::class, $this->db);
1858
        $order = $orderQuery->findOne(1);
1859
        $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

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

1885
        $this->assertEquals(1, $orderItems->/** @scrutinizer ignore-call */ getOrder()->getId());
Loading history...
1886
        $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

1886
        $this->assertEquals(1, $orderItems->/** @scrutinizer ignore-call */ getItem()->getId());
Loading history...
1887
1888
        /** test `__set()`. */
1889
        $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

1889
        $orderItems->/** @scrutinizer ignore-call */ 
1890
                     setOrderId(2);
Loading history...
1890
        $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

1890
        $orderItems->/** @scrutinizer ignore-call */ 
1891
                     setItemId(1);
Loading history...
1891
        $this->assertEquals(2, $orderItems->getOrder()->getId());
1892
        $this->assertEquals(1, $orderItems->getItem()->getId());
1893
1894
        /** Test `setAttribute()`. */
1895
        $orderItems->setAttribute('order_id', 3);
1896
        $orderItems->setAttribute('item_id', 1);
1897
        $this->assertEquals(3, $orderItems->getOrder()->getId());
1898
        $this->assertEquals(1, $orderItems->getItem()->getId());
1899
    }
1900
1901
    public function testOutdatedCompositeKeyRelationsAreReset(): void
1902
    {
1903
        $this->checkFixture($this->db, 'dossier');
1904
1905
        $dossierQuery = new ActiveQuery(Dossier::class, $this->db);
1906
1907
        $dossiers = $dossierQuery->findOne(['department_id' => 1, 'employee_id' => 1]);
1908
        $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

1908
        $this->assertEquals('John Doe', $dossiers->/** @scrutinizer ignore-call */ getEmployee()->getFullName());
Loading history...
1909
1910
        $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

1910
        $dossiers->/** @scrutinizer ignore-call */ 
1911
                   setDepartmentId(2);
Loading history...
1911
        $this->assertEquals('Ann Smith', $dossiers->getEmployee()->getFullName());
1912
1913
        $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

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

1938
        $orderItemIds = ArArrayHelper::getColumn($orders->/** @scrutinizer ignore-call */ getItems(), 'id');
Loading history...
1939
        sort($orderItemIds);
1940
        $this->assertSame([1, 2], $orderItemIds);
1941
1942
        $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

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

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

2094
        $this->assertTrue($arClass->/** @scrutinizer ignore-call */ refresh());
Loading history...
2095
2096
        $customerQuery->joinWithProfile = false;
2097
    }
2098
2099
    public function testCustomARRelation(): void
2100
    {
2101
        $this->checkFixture($this->db, 'order_item');
2102
2103
        $orderItem = new ActiveQuery(OrderItem::class, $this->db);
2104
2105
        $orderItem = $orderItem->findOne(1);
2106
2107
        $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

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

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

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

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

2382
        $this->assertCount(2, $customer->/** @scrutinizer ignore-call */ getOrdersWithNullFK());
Loading history...
2383
        $customer->unlink('ordersWithNullFK', $customer->getOrdersWithNullFK()[1], false);
2384
        $this->assertCount(1, $customer->getOrdersWithNullFK());
2385
2386
        $orderWithNullFKQuery = new ActiveQuery(OrderWithNullFK::class, $this->db);
2387
        $orderWithNullFK = $orderWithNullFKQuery->findOne(3);
2388
        $this->assertEquals(3, $orderWithNullFK->getId());
2389
        $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

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

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

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

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

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

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

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

2589
        /** @scrutinizer ignore-call */ 
2590
        $expensiveItems = $order->getExpensiveItemsUsingViaWithCallable();
Loading history...
2590
        $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

2590
        /** @scrutinizer ignore-call */ 
2591
        $cheapItems = $order->getCheapItemsUsingViaWithCallable();
Loading history...
2591
2592
        $this->assertCount(2, $expensiveItems);
2593
        $this->assertEquals(4, $expensiveItems[0]->getId());
2594
        $this->assertEquals(5, $expensiveItems[1]->getId());
2595
        $this->assertCount(1, $cheapItems);
2596
        $this->assertEquals(3, $cheapItems[0]->getId());
2597
    }
2598
2599
    public function testLink(): void
2600
    {
2601
        $this->checkFixture($this->db, 'customer', true);
2602
2603
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2604
        $customer = $customerQuery->findOne(2);
2605
        $this->assertCount(2, $customer->getOrders());
2606
2607
        /** has many */
2608
        $order = new Order($this->db);
2609
2610
        $order->setTotal(100);
2611
        $order->setCreatedAt(time());
2612
        $this->assertTrue($order->getIsNewRecord());
2613
2614
        /** belongs to */
2615
        $order = new Order($this->db);
2616
2617
        $order->setTotal(100);
2618
        $order->setCreatedAt(time());
2619
        $this->assertTrue($order->getIsNewRecord());
2620
2621
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
2622
        $customer = $customerQuery->findOne(1);
2623
        $this->assertNull($order->getCustomer());
2624
2625
        $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

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

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