Skip to content

Fix Model::unset with dot field name #2582

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/Eloquent/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -315,11 +315,16 @@ public function originalIsEquivalent($key)
*/
public function offsetUnset($offset): void
{
parent::offsetUnset($offset);

// Force unsetting even if the attribute is not set.
// End user can optimize DB calls by checking if the attribute is set before unsetting it.
$this->unset[$offset] = true;
if (str_contains($offset, '.')) {
// Update the field in the subdocument
Arr::forget($this->attributes, $offset);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will ignore if the field is not an array.

} else {
parent::offsetUnset($offset);

// Force unsetting even if the attribute is not set.
// End user can optimize DB calls by checking if the attribute is set before unsetting it.
$this->unset[$offset] = true;
}
}

/**
Expand Down
73 changes: 73 additions & 0 deletions tests/ModelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,79 @@ public function testUnsetAndSet(): void
$this->assertFalse($user->isDirty());
}

public function testUnsetDotAttributes(): void
{
$user = User::create(['name' => 'John Doe', 'notes' => ['note1' => 'ABC', 'note2' => 'DEF']]);

$user->unset('notes.note1');

$this->assertFalse(isset($user->notes['note1']));
$this->assertTrue(isset($user->notes['note2']));
$this->assertTrue($user->isDirty());
$dirty = $user->getDirty();
$this->assertArrayHasKey('notes', $dirty);
$this->assertArrayNotHasKey('$unset', $dirty);

$user->save();

$this->assertFalse(isset($user->notes['note1']));
$this->assertTrue(isset($user->notes['note2']));

// Re-fetch to be sure
$user = User::find($user->_id);

$this->assertFalse(isset($user->notes['note1']));
$this->assertTrue(isset($user->notes['note2']));

// Unset the parent key
$user->unset('notes');

$this->assertFalse(isset($user->notes['note1']));
$this->assertFalse(isset($user->notes['note2']));
$this->assertFalse(isset($user->notes));

$user->save();

$this->assertFalse(isset($user->notes));

// Re-fetch to be sure
$user = User::find($user->_id);

$this->assertFalse(isset($user->notes));
}

public function testUnsetDotAttributesAndSet(): void
{
$user = User::create(['name' => 'John Doe', 'notes' => ['note1' => 'ABC', 'note2' => 'DEF']]);

// notes.note2 is the last attribute of the document
$user->unset('notes.note2');
$this->assertTrue($user->isDirty());
$this->assertSame(['note1' => 'ABC'], $user->notes);

$user->setAttribute('notes.note2', 'DEF');
$this->assertFalse($user->isDirty());
$this->assertSame(['note1' => 'ABC', 'note2' => 'DEF'], $user->notes);

// Unsetting and resetting the 1st attribute of the document will change the order of the attributes
$user->unset('notes.note1');
$this->assertSame(['note2' => 'DEF'], $user->notes);
$this->assertTrue($user->isDirty());

$user->setAttribute('notes.note1', 'ABC');
$this->assertSame(['note2' => 'DEF', 'note1' => 'ABC'], $user->notes);
$this->assertTrue($user->isDirty());
$this->assertSame(['notes' => ['note2' => 'DEF', 'note1' => 'ABC']], $user->getDirty());

$user->save();
$this->assertSame(['note2' => 'DEF', 'note1' => 'ABC'], $user->notes);

// Re-fetch to be sure
$user = User::find($user->_id);

$this->assertSame(['note2' => 'DEF', 'note1' => 'ABC'], $user->notes);
}

public function testDates(): void
{
$user = User::create(['name' => 'John Doe', 'birthday' => new DateTime('1965/1/1')]);
Expand Down