Skip to content

Commit

Permalink
Merge 10346ce into 15291bc
Browse files Browse the repository at this point in the history
  • Loading branch information
dennis-koster committed Oct 21, 2019
2 parents 15291bc + 10346ce commit 256595d
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 8 deletions.
32 changes: 32 additions & 0 deletions src/Contracts/ModelUpdaterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ interface ModelUpdaterInterface extends
*/
public function create(array $data): UpdateResult;

/**
* Force creates a new model with (potential) nested data
*
* @param array $data
* @return UpdateResult
* @throws ModelSaveFailureException
*/
public function forceCreate(array $data): UpdateResult;

/**
* Updates an existing model with (potential) nested update data
*
Expand All @@ -37,4 +46,27 @@ public function update(
array $saveOptions = []
): UpdateResult;

/**
* Force updates an existing model with (potential) nested update data
*
* @param array $data
* @param mixed|Model $model either an existing model or its ID
* @param string $attribute lookup column, if not primary key, only if $model is int
* @param array $saveOptions options to pass on to the save() Eloquent method
* @return UpdateResult
* @throws ModelSaveFailureException
*/
public function forceUpdate(array $data, $model, ?string $attribute = null, array $saveOptions = []): UpdateResult;

/**
* Sets the forceFill property on the current instance. When
* set to true, forceFill() will be used to set attributes
* on the model, rather than the regular fill(), which takes
* guarded attributes into consideration.
*
* @param bool $force
* @return $this
*/
public function force(bool $force): self;

}
65 changes: 65 additions & 0 deletions src/ModelUpdater.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ class ModelUpdater extends AbstractNestedParser implements ModelUpdaterInterface
*/
protected $unguardedAttributes = [];

/**
* Whether to execute a forceFill() on the model attributes, which
* ignores the fillable guards, instead of a regular fill().
*
* @var bool
*/
protected $forceFill = false;


/**
* Creates a new model with (potential) nested data
Expand All @@ -80,6 +88,20 @@ public function create(array $data): UpdateResult
return $this->createOrUpdate();
}

/**
* Force creates a new model with (potential) nested data
*
* @param array $data
* @return UpdateResult
* @throws ModelSaveFailureException
*/
public function forceCreate(array $data): UpdateResult
{
return $this
->force(true)
->create($data);
}

/**
* Updates an existing model with (potential) nested update data
*
Expand Down Expand Up @@ -109,6 +131,43 @@ public function update(
return $this->createOrUpdate();
}

/**
* Force updates an existing model with (potential) nested update data
*
* @param array $data
* @param mixed|Model $model either an existing model or its ID
* @param string $attribute lookup column, if not primary key, only if $model is int
* @param array $saveOptions options to pass on to the save() Eloquent method
* @return UpdateResult
* @throws ModelSaveFailureException
*/
public function forceUpdate(
array $data,
$model,
?string $attribute = null,
array $saveOptions = []
): UpdateResult {
return $this
->force(true)
->update($data, $model, $attribute, $saveOptions);
}

/**
* Sets the forceFill property on the current instance. When
* set to true, forceFill() will be used to set attributes
* on the model, rather than the regular fill(), which takes
* guarded attributes into consideration.
*
* @param bool $force
* @return $this
*/
public function force(bool $force): ModelUpdaterInterface
{
$this->forceFill = $force;

return $this;
}

/**
* Returns list of currently queued unguarded attributes.
*
Expand Down Expand Up @@ -337,6 +396,12 @@ protected function updateAndPersistModel(): void

$this->model->fill($modelData);

if ($this->forceFill) {
$this->model->forceFill($modelData);
} else {
$this->model->fill($modelData);
}

$this->storeUnguardedAttributes();

// if we're saving a separate, top-level or belongs to related model,
Expand Down
97 changes: 91 additions & 6 deletions tests/BasicModelUpdaterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Czim\NestedModelUpdater\Exceptions\NestedModelNotFoundException;
use Czim\NestedModelUpdater\ModelUpdater;
use Czim\NestedModelUpdater\Test\Helpers\ArrayableData;
use Czim\NestedModelUpdater\Test\Helpers\Models\Genre;
use Czim\NestedModelUpdater\Test\Helpers\Models\Post;
use UnexpectedValueException;

Expand All @@ -30,7 +31,7 @@ function it_creates_a_model_without_any_nested_relations()
];

$updater = new ModelUpdater(Post::class);
$result = $updater->create($data);
$result = $updater->create($data);

static::assertTrue($result->model()->exists, 'Created model should exist');

Expand Down Expand Up @@ -81,8 +82,8 @@ function it_creates_a_new_nested_model_related_as_belongs_to()
$updater->update($data, $post);

$this->assertDatabaseHas('posts', [
'id' => $post->id,
'title' => 'updated aswell',
'id' => $post->id,
'title' => 'updated aswell',
]);

$post = Post::find($post->id);
Expand Down Expand Up @@ -217,6 +218,90 @@ function it_attaches_has_many_related_models_that_were_related_to_a_different_mo
$this->assertDatabaseHas('comments', [ 'id' => $commentB->id, 'post_id' => $post->id ]);
}

// ------------------------------------------------------------------------------
// Force updating / creating
// ------------------------------------------------------------------------------

/**
* @test
*/
public function it_force_updates_deleted_at_on_an_existing_model_through_force_update()
{
$genre = $this->createGenre();

$updater = new ModelUpdater(Genre::class);
$updater->forceUpdate([
'deleted_at' => '2019-10-12 09:00:00',
], $genre);

$this->assertDatabaseHas('genres', [
'id' => $genre->id,
'deleted_at' => '2019-10-12 09:00:00',
]);
}

/**
* @test
*/
public function it_force_updates_deleted_at_on_an_existing_model_through_setting_force_first()
{
$genre = $this->createGenre();

$updater = new ModelUpdater(Genre::class);
$updater->force(true);
$updater->update([
'deleted_at' => '2019-10-12 09:00:00',
], $genre);

$this->assertDatabaseHas('genres', [
'id' => $genre->id,
'deleted_at' => '2019-10-12 09:00:00',
]);
}

/**
* @test
*/
public function it_force_creates_model_with_deleted_at_through_force_create()
{
$updater = new ModelUpdater(Genre::class);
$result = $updater->forceCreate([
'name' => 'Test genre',
'deleted_at' => '2019-10-12 09:00:00',
]);

$this->assertInstanceOf(UpdateResult::class, $result);
$this->assertTrue($result->model()->exists, "Created model should exist");

$this->assertDatabaseHas('genres', [
'id' => $result->model()->id,
'name' => 'Test genre',
'deleted_at' => '2019-10-12 09:00:00',
]);
}

/**
* @test
*/
public function it_force_creates_model_with_deleted_at_through_setting_force_first()
{
$updater = new ModelUpdater(Genre::class);
$updater->force(true);
$result = $updater->create([
'name' => 'Test genre',
'deleted_at' => '2019-10-12 09:00:00',
]);

$this->assertInstanceOf(UpdateResult::class, $result);
$this->assertTrue($result->model()->exists, "Created model should exist");

$this->assertDatabaseHas('genres', [
'id' => $result->model()->id,
'name' => 'Test genre',
'deleted_at' => '2019-10-12 09:00:00',
]);
}

// ------------------------------------------------------------------------------
// Normalization
// ------------------------------------------------------------------------------
Expand Down Expand Up @@ -276,7 +361,7 @@ function it_normalizes_nested_data_for_arrayable_content()
'genre' => new ArrayableData([
'id' => $genre->id,
'name' => 'updated',
])
]),
];

$updater = new ModelUpdater(Post::class);
Expand Down Expand Up @@ -415,7 +500,7 @@ function it_rolls_back_changes_if_exception_is_thrown()
'comments' => [
[
'id' => 999, // does not exist
]
],
],
];

Expand Down Expand Up @@ -453,7 +538,7 @@ function it_can_be_configured_not_to_use_database_transactions()
'comments' => [
[
'id' => 999, // does not exist
]
],
],
];

Expand Down
4 changes: 2 additions & 2 deletions tests/ElaborateModelUpdaterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ function it_deletes_omitted_nested_belongsto_relations_on_replacement_and_dissoc
$updater->update($data, $post);

$this->assertDatabaseHas('posts', [ 'id' => $post->id, 'genre_id' => null ]);
$this->assertDatabaseMissing('genres', [ 'id' => $oldGenreId ]);
$this->assertSoftDeleted('genres', [ 'id' => $oldGenreId ]);

// reset

Expand Down Expand Up @@ -313,7 +313,7 @@ function it_deletes_omitted_nested_belongsto_relations_on_replacement_and_dissoc
static::assertInstanceOf(Genre::class, $genre);

$this->assertDatabaseHas('posts', [ 'id' => $post->id, 'genre_id' => $genre->id ]);
$this->assertDatabaseMissing('genres', [ 'id' => $oldGenreId ]);
$this->assertSoftDeleted('genres', [ 'id' => $oldGenreId ]);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions tests/Helpers/Models/Genre.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace Czim\NestedModelUpdater\Test\Helpers\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\HasMany;

/**
Expand All @@ -10,6 +11,8 @@
*/
class Genre extends Model
{
use SoftDeletes;

/**
* @var array
*/
Expand Down
1 change: 1 addition & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ protected function migrateDatabase(): void
$table->increments('id');
$table->string('name', 50);
$table->timestamps();
$table->softDeletes();
});

Schema::create('authors', static function (Blueprint $table) {
Expand Down

0 comments on commit 256595d

Please sign in to comment.