Passed
Pull Request — master (#361)
by Alexander
02:53
created

ActiveRecordTest::testRelationViaArray()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 17
c 0
b 0
f 0
dl 0
loc 27
rs 9.7
cc 3
nc 3
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\ActiveRecord\Tests\Driver\Pgsql;
6
7
use ArrayAccess;
8
use Traversable;
9
use Yiisoft\ActiveRecord\ActiveQuery;
10
use Yiisoft\ActiveRecord\ArArrayHelper;
11
use Yiisoft\ActiveRecord\Tests\Driver\Pgsql\Stubs\Promotion;
12
use Yiisoft\ActiveRecord\Tests\Driver\Pgsql\Stubs\Type;
13
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\ArrayAndJsonTypes;
14
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Beta;
15
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\BoolAR;
16
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer;
17
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\CustomerClosureField;
18
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\DefaultPk;
19
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\UserAR;
20
use Yiisoft\ActiveRecord\Tests\Support\PgsqlHelper;
21
use Yiisoft\Db\Expression\ArrayExpression;
22
use Yiisoft\Db\Expression\Expression;
23
use Yiisoft\Db\Expression\JsonExpression;
24
use Yiisoft\Db\Pgsql\Schema as SchemaPgsql;
25
26
final class ActiveRecordTest extends \Yiisoft\ActiveRecord\Tests\ActiveRecordTest
0 ignored issues
show
Bug introduced by
The type Yiisoft\ActiveRecord\Tests\ActiveRecordTest was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
27
{
28
    public function setUp(): void
29
    {
30
        parent::setUp();
31
32
        $pgsqlHelper = new PgsqlHelper();
33
        $this->db = $pgsqlHelper->createConnection();
0 ignored issues
show
Bug Best Practice introduced by
The property db does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
34
    }
35
36
    protected function tearDown(): void
37
    {
38
        parent::tearDown();
39
40
        $this->db->close();
41
42
        unset($this->db);
43
    }
44
45
    public function testDefaultValues(): void
46
    {
47
        $this->checkFixture($this->db, 'type');
48
49
        $arClass = new Type($this->db);
50
51
        $arClass->loadDefaultValues();
52
53
        $this->assertEquals(1, $arClass->int_col2);
54
        $this->assertEquals('something', $arClass->char_col2);
55
        $this->assertEquals(1.23, $arClass->float_col2);
56
        $this->assertEquals(33.22, $arClass->numeric_col);
57
        $this->assertEquals(true, $arClass->bool_col2);
58
        $this->assertEquals('2002-01-01 00:00:00', $arClass->time);
59
60
        $arClass = new Type($this->db);
61
        $arClass->char_col2 = 'not something';
62
63
        $arClass->loadDefaultValues();
64
        $this->assertEquals('not something', $arClass->char_col2);
65
66
        $arClass = new Type($this->db);
67
        $arClass->char_col2 = 'not something';
68
69
        $arClass->loadDefaultValues(false);
70
        $this->assertEquals('something', $arClass->char_col2);
71
    }
72
73
    public function testCastValues(): void
74
    {
75
        $this->checkFixture($this->db, 'type');
76
77
        $arClass = new Type($this->db);
78
79
        $arClass->int_col = 123;
80
        $arClass->int_col2 = 456;
81
        $arClass->smallint_col = 42;
82
        $arClass->char_col = '1337';
83
        $arClass->char_col2 = 'test';
84
        $arClass->char_col3 = 'test123';
85
        $arClass->float_col = 3.742;
86
        $arClass->float_col2 = 42.1337;
87
        $arClass->bool_col = true;
88
        $arClass->bool_col2 = false;
89
90
        $arClass->save();
91
92
        /** @var $model Type */
93
        $aqClass = new ActiveQuery(Type::class, $this->db);
94
        $query = $aqClass->onePopulate();
95
96
        $this->assertSame(123, $query->int_col);
0 ignored issues
show
Bug introduced by
Accessing int_col on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
97
        $this->assertSame(456, $query->int_col2);
0 ignored issues
show
Bug introduced by
Accessing int_col2 on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
98
        $this->assertSame(42, $query->smallint_col);
0 ignored issues
show
Bug introduced by
Accessing smallint_col on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
99
        $this->assertSame('1337', trim($query->char_col));
0 ignored issues
show
Bug introduced by
Accessing char_col on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
100
        $this->assertSame('test', $query->char_col2);
0 ignored issues
show
Bug introduced by
Accessing char_col2 on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
101
        $this->assertSame('test123', $query->char_col3);
0 ignored issues
show
Bug introduced by
Accessing char_col3 on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
102
    }
103
104
    public function testExplicitPkOnAutoIncrement(): void
105
    {
106
        $this->checkFixture($this->db, 'customer');
107
108
        $customer = new Customer($this->db);
109
        $customer->setId(1337);
110
        $customer->setEmail('[email protected]');
111
        $customer->setName('user1337');
112
        $customer->setAddress('address1337');
113
        $this->assertTrue($customer->getIsNewRecord());
114
115
        $customer->save();
116
        $this->assertEquals(1337, $customer->getId());
117
        $this->assertFalse($customer->getIsNewRecord());
118
    }
119
120
    /**
121
     * {@see https://github.com/yiisoft/yii2/issues/15482}
122
     */
123
    public function testEagerLoadingUsingStringIdentifiers(): void
124
    {
125
        $this->checkFixture($this->db, 'beta');
126
127
        $betaQuery = new ActiveQuery(Beta::class, $this->db);
128
        $betas = $betaQuery->with('alpha')->all();
129
        $this->assertNotEmpty($betas);
130
131
        $alphaIdentifiers = [];
132
133
        /** @var Beta[] $betas */
134
        foreach ($betas as $beta) {
135
            $this->assertNotNull($beta->getAlpha());
136
            $this->assertEquals($beta->getAlphaStringIdentifier(), $beta->getAlpha()->getStringIdentifier());
137
            $alphaIdentifiers[] = $beta->getAlpha()->getStringIdentifier();
138
        }
139
140
        $this->assertEquals(['1', '01', '001', '001', '2', '2b', '2b', '02'], $alphaIdentifiers);
141
    }
142
143
    public function testBooleanAttribute(): void
144
    {
145
        $this->checkFixture($this->db, 'customer', true);
146
147
        $customer = new Customer($this->db);
148
        $customer->setName('boolean customer');
149
        $customer->setEmail('[email protected]');
150
        $customer->setBoolStatus(false);
151
        $customer->save();
152
153
        $customer->refresh();
154
        $this->assertFalse($customer->getBoolStatus());
155
156
        $customer->setBoolStatus(true);
157
158
        $customer->save();
159
        $customer->refresh();
160
        $this->assertTrue($customer->getBoolStatus());
161
162
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
163
        $customers = $customerQuery->where(['bool_status' => true])->all();
164
        $this->assertCount(3, $customers);
165
166
        $customers = $customerQuery->where(['bool_status' => false])->all();
167
        $this->assertCount(1, $customers);
168
    }
169
170
    public function testBooleanValues(): void
171
    {
172
        $this->checkFixture($this->db, 'bool_values');
173
174
        $command = $this->db->createCommand();
175
        $command->insertBatch('bool_values', [[true], [false]], ['bool_col'])->execute();
176
        $boolARQuery = new ActiveQuery(BoolAR::class, $this->db);
177
178
        $this->assertTrue($boolARQuery->where(['bool_col' => true])->onePopulate()->bool_col);
0 ignored issues
show
Bug introduced by
Accessing bool_col on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
179
        $this->assertFalse($boolARQuery->where(['bool_col' => false])->onePopulate()->bool_col);
180
181
        $this->assertEquals(1, $boolARQuery->where('bool_col = TRUE')->count('*'));
182
        $this->assertEquals(1, $boolARQuery->where('bool_col = FALSE')->count('*'));
183
        $this->assertEquals(2, $boolARQuery->where('bool_col IN (TRUE, FALSE)')->count('*'));
184
185
        $this->assertEquals(1, $boolARQuery->where(['bool_col' => true])->count('*'));
186
        $this->assertEquals(1, $boolARQuery->where(['bool_col' => false])->count('*'));
187
        $this->assertEquals(2, $boolARQuery->where(['bool_col' => [true, false]])->count('*'));
188
189
        $this->assertEquals(1, $boolARQuery->where('bool_col = :bool_col', ['bool_col' => true])->count('*'));
190
        $this->assertEquals(1, $boolARQuery->where('bool_col = :bool_col', ['bool_col' => false])->count('*'));
191
    }
192
193
    /**
194
     * {@see https://github.com/yiisoft/yii2/issues/4672}
195
     */
196
    public function testBooleanValues2(): void
197
    {
198
        $this->checkFixture($this->db, 'bool_user');
199
200
        //$this->db->setCharset('utf8');
201
        $this->db->createCommand('DROP TABLE IF EXISTS bool_user;')->execute();
202
        $this->db->createCommand()->createTable('bool_user', [
203
            'id' => SchemaPgsql::TYPE_PK,
204
            'username' => SchemaPgsql::TYPE_STRING . ' NOT NULL',
205
            'auth_key' => SchemaPgsql::TYPE_STRING . '(32) NOT NULL',
206
            'password_hash' => SchemaPgsql::TYPE_STRING . ' NOT NULL',
207
            'password_reset_token' => SchemaPgsql::TYPE_STRING,
208
            'email' => SchemaPgsql::TYPE_STRING . ' NOT NULL',
209
            'role' => SchemaPgsql::TYPE_SMALLINT . ' NOT NULL DEFAULT 10',
210
            'status' => SchemaPgsql::TYPE_SMALLINT . ' NOT NULL DEFAULT 10',
211
            'created_at' => SchemaPgsql::TYPE_INTEGER . ' NOT NULL',
212
            'updated_at' => SchemaPgsql::TYPE_INTEGER . ' NOT NULL',
213
        ])->execute();
214
        $this->db->createCommand()->addColumn(
215
            'bool_user',
216
            'is_deleted',
217
            SchemaPgsql::TYPE_BOOLEAN . ' NOT NULL DEFAULT FALSE'
218
        )->execute();
219
220
        $user = new UserAR($this->db);
221
        $user->username = 'test';
222
        $user->auth_key = 'test';
223
        $user->password_hash = 'test';
224
        $user->email = '[email protected]';
225
        $user->created_at = time();
226
        $user->updated_at = time();
227
        $user->save();
228
229
        $userQuery = new ActiveQuery(UserAR::class, $this->db);
230
        $this->assertCount(1, $userQuery->where(['is_deleted' => false])->all());
231
        $this->assertCount(0, $userQuery->where(['is_deleted' => true])->all());
232
        $this->assertCount(1, $userQuery->where(['is_deleted' => [true, false]])->all());
233
    }
234
235
    public function testBooleanDefaultValues(): void
236
    {
237
        $this->checkFixture($this->db, 'bool_values');
238
239
        $arClass = new BoolAR($this->db);
240
241
        $this->assertNull($arClass->bool_col);
242
        $this->assertTrue($arClass->default_true);
243
        $this->assertFalse($arClass->default_false);
244
245
        $arClass->loadDefaultValues();
246
247
        $this->assertNull($arClass->bool_col);
248
        $this->assertTrue($arClass->default_true);
249
        $this->assertFalse($arClass->default_false);
250
        $this->assertTrue($arClass->save());
251
    }
252
253
    public function testPrimaryKeyAfterSave(): void
254
    {
255
        $this->checkFixture($this->db, 'default_pk');
256
257
        $record = new DefaultPk($this->db);
258
259
        $record->type = 'type';
260
261
        $record->save();
262
263
        $this->assertEquals(5, $record->getPrimaryKey());
264
    }
265
266
    public static function arrayValuesProvider(): array
267
    {
268
        return [
269
            'simple arrays values' => [[
270
                'intarray_col' => [
271
                    new ArrayExpression([1,-2,null,'42'], 'int4', 1),
272
                    new ArrayExpression([1,-2,null,42], 'int4', 1),
273
                ],
274
                'textarray2_col' => [
275
                    new ArrayExpression([['text'], [null], [1]], 'text', 2),
276
                    new ArrayExpression([['text'], [null], ['1']], 'text', 2),
277
                ],
278
                'json_col' => [['a' => 1, 'b' => null, 'c' => [1,3,5]]],
279
                'jsonb_col' => [[null, 'a', 'b', '\"', '{"af"}']],
280
                'jsonarray_col' => [new ArrayExpression([[',', 'null', true, 'false', 'f']], 'json')],
281
            ]],
282
            'null arrays values' => [[
283
                'intarray_col' => [
284
                    null,
285
                ],
286
                'textarray2_col' => [
287
                    [null, null],
288
                    new ArrayExpression([null, null], 'text', 2),
289
                ],
290
                'json_col' => [
291
                    null,
292
                ],
293
                'jsonarray_col' => [
294
                    null,
295
                ],
296
            ]],
297
            'empty arrays values' => [[
298
                'textarray2_col' => [
299
                    [[], []],
300
                    new ArrayExpression([], 'text', 2),
301
                ],
302
            ]],
303
            'nested objects' => [[
304
                'intarray_col' => [
305
                    new ArrayExpression(new ArrayExpression([1,2,3]), 'int', 1),
306
                    new ArrayExpression([1,2,3], 'int4', 1),
307
                ],
308
                'textarray2_col' => [
309
                    new ArrayExpression([new ArrayExpression(['text']), [null], [1]], 'text', 2),
310
                    new ArrayExpression([['text'], [null], ['1']], 'text', 2),
311
                ],
312
                'json_col' => [
313
                    new JsonExpression(new JsonExpression(new JsonExpression(['a' => 1, 'b' => null, 'c' => new JsonExpression([1,3,5])]))),
314
                    ['a' => 1, 'b' => null, 'c' => [1,3,5]],
315
                ],
316
                'jsonb_col' => [
317
                    new JsonExpression(new ArrayExpression([1,2,3])),
318
                    [1,2,3],
319
                ],
320
                'jsonarray_col' => [
321
                    new ArrayExpression([new JsonExpression(['1', 2]), [3,4,5]], 'json'),
322
                    new ArrayExpression([['1', 2], [3,4,5]], 'json'),
323
                ],
324
            ]],
325
            'arrays packed in classes' => [[
326
                'intarray_col' => [
327
                    new ArrayExpression([1,-2,null,'42'], 'int', 1),
328
                    new ArrayExpression([1,-2,null,42], 'int4', 1),
329
                ],
330
                'textarray2_col' => [
331
                    new ArrayExpression([['text'], [null], [1]], 'text', 2),
332
                    new ArrayExpression([['text'], [null], ['1']], 'text', 2),
333
                ],
334
                'json_col' => [
335
                    new JsonExpression(['a' => 1, 'b' => null, 'c' => [1,3,5]]),
336
                    ['a' => 1, 'b' => null, 'c' => [1,3,5]],
337
                ],
338
                'jsonb_col' => [
339
                    new JsonExpression([null, 'a', 'b', '\"', '{"af"}']),
340
                    [null, 'a', 'b', '\"', '{"af"}'],
341
                ],
342
                'jsonarray_col' => [
343
                    new Expression("array['[\",\",\"null\",true,\"false\",\"f\"]'::json]::json[]"),
344
                    new ArrayExpression([[',', 'null', true, 'false', 'f']], 'json'),
345
                ],
346
            ]],
347
            'scalars' => [[
348
                'json_col' => [
349
                    '5.8',
350
                ],
351
                'jsonb_col' => [
352
                    M_PI,
353
                ],
354
            ]],
355
        ];
356
    }
357
358
    /**
359
     * @dataProvider arrayValuesProvider
360
     */
361
    public function testArrayValues($attributes): void
362
    {
363
        $this->checkFixture($this->db, 'array_and_json_types', true);
364
365
        $type = new ArrayAndJsonTypes($this->db);
366
367
        foreach ($attributes as $attribute => $expected) {
368
            $type->setAttribute($attribute, $expected[0]);
369
        }
370
371
        $type->save();
372
373
        $typeQuery = new ActiveQuery($type::class, $this->db);
374
375
        $type = $typeQuery->onePopulate();
376
377
        foreach ($attributes as $attribute => $expected) {
378
            $expected = $expected[1] ?? $expected[0];
379
            $value = $type->getAttribute($attribute);
380
381
            if ($expected instanceof ArrayExpression) {
382
                $expected = $expected->getValue();
383
            }
384
385
            $this->assertEquals($expected, $value, 'In column ' . $attribute);
386
387
            if ($value instanceof ArrayExpression) {
388
                $this->assertInstanceOf(ArrayAccess::class, $value);
389
                $this->assertInstanceOf(Traversable::class, $value);
390
                /** testing arrayaccess */
391
                foreach ($type->getAttribute($attribute) as $key => $v) {
392
                    $this->assertSame($expected[$key], $value[$key]);
393
                }
394
            }
395
        }
396
397
        /** Testing update */
398
        foreach ($attributes as $attribute => $expected) {
399
            $type->markAttributeDirty($attribute);
0 ignored issues
show
Bug introduced by
The method markAttributeDirty() 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

399
            $type->/** @scrutinizer ignore-call */ 
400
                   markAttributeDirty($attribute);
Loading history...
400
        }
401
402
        $this->assertSame(1, $type->update(), 'The record got updated');
403
    }
404
405
    public function testToArray(): void
406
    {
407
        $this->checkFixture($this->db, 'customer', true);
408
409
        $customerQuery = new ActiveQuery(Customer::class, $this->db);
410
        $customer = $customerQuery->findOne(1);
411
412
        $this->assertSame(
413
            [
414
                'id' => 1,
415
                'email' => '[email protected]',
416
                'name' => 'user1',
417
                'address' => 'address1',
418
                'status' => 1,
419
                'bool_status' => true,
420
                'profile_id' => 1,
421
            ],
422
            $customer->toArray(),
0 ignored issues
show
Bug introduced by
The method toArray() 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

422
            $customer->/** @scrutinizer ignore-call */ 
423
                       toArray(),
Loading history...
423
        );
424
    }
425
426
    public function testToArrayWithClosure(): void
427
    {
428
        $this->checkFixture($this->db, 'customer', true);
429
430
        $customerQuery = new ActiveQuery(CustomerClosureField::class, $this->db);
431
        $customer = $customerQuery->findOne(1);
432
433
        $this->assertSame(
434
            [
435
                'id' => 1,
436
                'email' => '[email protected]',
437
                'name' => 'user1',
438
                'address' => 'address1',
439
                'status' => 'active',
440
                'bool_status' => true,
441
                'profile_id' => 1,
442
            ],
443
            $customer->toArray(),
444
        );
445
    }
446
447
    public function testRelationViaArray()
448
    {
449
        $this->checkFixture($this->db, 'promotion');
450
451
        $promotionQuery = new ActiveQuery(Promotion::class, $this->db);
452
        /** @var Promotion[] $promotions */
453
        $promotions = $promotionQuery->with('items')->all();
454
455
        $this->assertSame([1, 2], ArArrayHelper::getColumn($promotions[0]->getItems(), 'id'));
456
        $this->assertSame([3, 4, 5], ArArrayHelper::getColumn($promotions[1]->getItems(), 'id'));
457
        $this->assertSame([1, 3], ArArrayHelper::getColumn($promotions[2]->getItems(), 'id'));
458
        $this->assertCount(0, $promotions[3]->getItems());
459
460
        /** Test inverse relation */
461
        foreach ($promotions as $promotion) {
462
            foreach ($promotion->getItems() as $item) {
463
                $this->assertTrue($item->isRelationPopulated('promotions'));
464
            }
465
        }
466
467
        $this->assertSame([1, 3], ArArrayHelper::getColumn($promotions[0]->getItems()[0]->getPromotions(), 'id'));
468
        $this->assertSame([1], ArArrayHelper::getColumn($promotions[0]->getItems()[1]->getPromotions(), 'id'));
469
        $this->assertSame([2, 3], ArArrayHelper::getColumn($promotions[1]->getItems()[0]->getPromotions(), 'id'));
470
        $this->assertSame([2], ArArrayHelper::getColumn($promotions[1]->getItems()[1]->getPromotions(), 'id'));
471
        $this->assertSame([2], ArArrayHelper::getColumn($promotions[1]->getItems()[2]->getPromotions(), 'id'));
472
        $this->assertSame([1, 3], ArArrayHelper::getColumn($promotions[2]->getItems()[0]->getPromotions(), 'id'));
473
        $this->assertSame([2, 3], ArArrayHelper::getColumn($promotions[2]->getItems()[1]->getPromotions(), 'id'));
474
    }
475
}
476