Skip to content

Commit fc77327

Browse files
authored
fix(database): nullable belongsto relations (#1575)
1 parent 0b86c3d commit fc77327

File tree

8 files changed

+78
-14
lines changed

8 files changed

+78
-14
lines changed

mago.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ empty-line-after-opening-tag = false
3535
integrations = ["php-unit", "tempest"]
3636

3737
[linter.rules]
38+
yoda-conditions = { enabled = false }
3839
interface-name = { psr = false }
3940
trait-name = { psr = false }
4041
class-name = { psr = false }

packages/console/src/Components/Renderers/TextInputRenderer.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ public function render(
4040
}
4141

4242
// splits the text to an array so we can work with individual lines
43-
// @mago-expect lint:no-nested-ternary
4443
$lines = str($buffer->text ?: ($placeholder ?: ''))
4544
->explode("\n")
4645
->flatMap(fn (string $line) => str($line)->chunk($this->maxLineCharacters)->toArray())

packages/database/src/Mappers/SelectModelMapper.php

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,24 @@ private function values(ModelInspector $model, array $data): array
5454
foreach ($data as $key => $value) {
5555
$relation = $model->getRelation($key);
5656

57-
if (! $relation instanceof HasMany) {
57+
if ($relation instanceof BelongsTo) {
58+
if ($relation->property->isNullable() && array_filter($data[$relation->name] ?? []) === []) {
59+
$data[$relation->name] = null;
60+
}
61+
5862
continue;
5963
}
6064

61-
$mapped = [];
62-
$relationModel = inspect($relation);
65+
if ($relation instanceof HasMany) {
66+
$mapped = [];
67+
$relationModel = inspect($relation);
6368

64-
foreach ($value as $item) {
65-
$mapped[] = $this->values($relationModel, $item);
66-
}
69+
foreach ($value as $item) {
70+
$mapped[] = $this->values($relationModel, $item);
71+
}
6772

68-
$data[$key] = $mapped;
73+
$data[$key] = $mapped;
74+
}
6975
}
7076

7177
return $data;

packages/mapper/src/Mappers/ArrayToObjectMapper.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ public function resolveValue(PropertyReflector $property, mixed $value): mixed
154154
{
155155
$caster = $this->casterFactory->forProperty($property);
156156

157+
if ($property->isNullable() && $value === null) {
158+
return null;
159+
}
160+
157161
if ($caster === null) {
158162
return $value;
159163
}

packages/support/src/Str/functions.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ function to_snake_case(Stringable|string $string, Stringable|string $delimiter =
6363

6464
$string = preg_replace('/(?<=\p{Ll}|\p{N})(\p{Lu})/u', $delimiter . '$1', $string);
6565
$string = preg_replace('/(?<=\p{Lu})(\p{Lu}\p{Ll})/u', $delimiter . '$1', $string);
66+
// @mago-expect lint:require-preg-quote-delimiter
6667
$string = preg_replace('![^' . preg_quote($delimiter) . '\pL\pN\s]+!u', $delimiter, mb_strtolower($string, 'UTF-8'));
6768
$string = preg_replace('/\s+/u', $delimiter, $string);
6869
$string = trim($string, $delimiter);

packages/view/src/Elements/PhpDataElement.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ public function compile(): string
4242
$localVariableName,
4343
$localVariableName,
4444
$isExpression
45-
// @mago-expect lint:no-nested-ternary
4645
? ($value ?: 'null')
4746
: var_export($value, return: true),
4847
);

tests/Integration/Database/Builder/IsDatabaseModelTest.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,21 @@ public function test_delete_nonexistent_record(): void
617617

618618
$this->assertNull(Foo::get($fooId));
619619
}
620+
621+
public function test_nullable_relations(): void
622+
{
623+
$this->migrate(
624+
CreateMigrationsTable::class,
625+
CreateBNullableTable::class,
626+
CreateANullableTable::class,
627+
);
628+
629+
$a = ANullableModel::create();
630+
631+
$a->load('b');
632+
633+
$this->assertNull($a->b);
634+
}
620635
}
621636

622637
final class Foo
@@ -963,3 +978,43 @@ public function up(): QueryStatement
963978
->text('description');
964979
}
965980
}
981+
982+
final class CreateANullableTable implements MigratesUp
983+
{
984+
private(set) string $name = '100-create-a-nullable';
985+
986+
public function up(): QueryStatement
987+
{
988+
return new CreateTableStatement('a')
989+
->primary()
990+
->belongsTo('a.b_id', 'b.id', nullable: true);
991+
}
992+
}
993+
994+
final class CreateBNullableTable implements MigratesUp
995+
{
996+
private(set) string $name = '100-create-b-nullable';
997+
998+
public function up(): QueryStatement
999+
{
1000+
return new CreateTableStatement('b')
1001+
->primary()
1002+
->string('name');
1003+
}
1004+
}
1005+
1006+
#[Table('a')]
1007+
final class ANullableModel
1008+
{
1009+
use IsDatabaseModel;
1010+
1011+
public ?BNullableModel $b = null;
1012+
}
1013+
1014+
#[Table('b')]
1015+
final class BNullableModel
1016+
{
1017+
use IsDatabaseModel;
1018+
1019+
public string $name;
1020+
}

tests/Integration/Database/MultiDatabaseTest.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ public function test_database_seed_on_selected_database(): void
306306

307307
$this->assertSame(
308308
'Timeline Taxi',
309-
query(Book::class)->select()->onDatabase('main')->first()->title,
309+
query(Book::class)->select()->onDatabase('main')->where('title', 'Timeline Taxi')->first()->title,
310310
);
311311

312312
$this->assertNull(
@@ -318,8 +318,7 @@ public function test_database_seed_on_selected_database(): void
318318
->assertSuccess();
319319

320320
/** @var Book $book */
321-
/** @phpstan-ignore-next-line */
322-
$book = query(Book::class)->select()->onDatabase('backup')->first();
321+
$book = query(Book::class)->select()->onDatabase('backup')->where('title', 'Timeline Taxi')->first();
323322

324323
$this->assertSame(
325324
'Timeline Taxi',
@@ -335,7 +334,7 @@ public function test_migrate_fresh_seed_on_selected_database(): void
335334

336335
$this->assertSame(
337336
'Timeline Taxi',
338-
query(Book::class)->select()->onDatabase('main')->first()->title,
337+
query(Book::class)->select()->onDatabase('main')->where('title', 'Timeline Taxi')->first()->title,
339338
);
340339

341340
$this->assertException(QueryWasInvalid::class, function (): void {
@@ -348,7 +347,7 @@ public function test_migrate_fresh_seed_on_selected_database(): void
348347

349348
$this->assertSame(
350349
'Timeline Taxi',
351-
query(Book::class)->select()->onDatabase('backup')->first()->title,
350+
query(Book::class)->select()->onDatabase('backup')->where('title', 'Timeline Taxi')->first()->title,
352351
);
353352
}
354353

0 commit comments

Comments
 (0)