diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 21ab50a..d996750 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -30,6 +30,12 @@ parameters: count: 1 path: tests/EntityTest.php + - + message: '#^Parameter \#1 \$row of method Tests\\Support\\Models\\UserModel\:\:insert\(\) expects array\\|object\|null, array\ given\.$#' + identifier: argument.type + count: 1 + path: tests/EntityTest.php + - message: '#^Cannot access property \$country on array\|bool\|float\|int\|object\|string\.$#' identifier: property.nonObject diff --git a/src/Relation.php b/src/Relation.php index b33beb2..5caa17d 100644 --- a/src/Relation.php +++ b/src/Relation.php @@ -27,7 +27,7 @@ class Relation /** * @var list */ - private array|Entity $data = []; + private array|Entity|null $data = []; public function __construct( public readonly RelationTypes $type, @@ -61,7 +61,7 @@ public function applyConditions(): static /** * @param list $data */ - public function setData(array|Entity $data): static + public function setData(array|Entity|null $data): static { $this->data = $data; @@ -277,7 +277,7 @@ public function filterResult(array|Entity|null $row, string $returnType): array| public function filterResults(array|Entity $results, string $returnType): array|Entity { - if ($this->type !== RelationTypes::belongsToMany) { + if ($results === [] || $this->type !== RelationTypes::belongsToMany) { return $results; } diff --git a/src/Traits/HasRelations.php b/src/Traits/HasRelations.php index d7f2d20..c8c89af 100644 --- a/src/Traits/HasRelations.php +++ b/src/Traits/HasRelations.php @@ -302,7 +302,14 @@ protected function relationsAfterInsert(array $eventData): array } foreach ($this->relations as $relationObject) { - foreach ($relationObject->getData() as $row) { + $data = $relationObject->getData(); + + // Skip if data is null or empty - nothing to insert + if ($data === [null] || $data === []) { + continue; + } + + foreach ($data as $row) { $row = $this->transformDataToArray($row, 'insert'); $result = $relationObject->applyWith()->model->insert(array_merge($row, [ $relationObject->foreignKey => $eventData[$this->primaryKey], @@ -349,7 +356,14 @@ protected function relationsAfterUpdate(array $eventData): array } foreach ($this->relations as $relationObject) { - foreach ($relationObject->getData() as $row) { + $data = $relationObject->getData(); + + // Skip if data is null or empty - nothing to update + if ($data === [null] || $data === []) { + continue; + } + + foreach ($data as $row) { $row = $this->transformDataToArray($row, 'insert'); foreach ($eventData[$this->primaryKey] as $id) { @@ -432,11 +446,9 @@ protected function getDataForRelationById(int|string $id, Relation $relation, st $relation->applyWith()->applyRelation($id, $this->primaryKey)->applyConditions(); - $results = in_array($relation->type, [RelationTypes::hasOne, RelationTypes::belongsTo], true) ? - $relation->model->first() : - $relation->model->findAll(); - - return $relation->filterResults($results, $this->tempReturnType); + return in_array($relation->type, [RelationTypes::hasOne, RelationTypes::belongsTo], true) ? + $relation->filterResult($relation->model->first(), $this->tempReturnType) : + $relation->filterResults($relation->model->findAll(), $this->tempReturnType); } /** diff --git a/tests/EntityTest.php b/tests/EntityTest.php index 4d4ef69..728fb3a 100644 --- a/tests/EntityTest.php +++ b/tests/EntityTest.php @@ -102,4 +102,46 @@ public function testRelationQueriedOnlyOnce() $this->assertSame($queryCount + 1, $this->queryCount); } + + public function testSaveNullRelationOneToOneSkipsInsert() + { + $model = model(UserModel::class); + $data = [ + 'username' => 'Test User', + 'company_id' => '2', + 'country_id' => '1', + 'profile' => null, + ]; + + $id = $model->with('profile')->insert($data); + + $this->assertIsNumeric($id); + + // Verify user was created but profile was not + $user = $model->with('profile')->find($id); + $this->assertInstanceOf(User::class, $user); + $this->assertSame('Test User', $user->username); + $this->assertNull($user->profile); + } + + public function testSaveEmptyArrayRelationOneToManySkipsInsert() + { + $model = model(UserModel::class); + $data = [ + 'username' => 'Test User', + 'company_id' => '2', + 'country_id' => '1', + 'posts' => [], + ]; + + $id = $model->with('posts')->insert($data); + + $this->assertIsNumeric($id); + + // Verify user was created but no posts were created + $user = $model->with('posts')->find($id); + $this->assertInstanceOf(User::class, $user); + $this->assertSame('Test User', $user->username); + $this->assertSame([], $user->posts); + } }