Skip to content

Commit

Permalink
feat: add softDelete
Browse files Browse the repository at this point in the history
  • Loading branch information
JoaoFerrazfs committed Aug 16, 2023
1 parent 90c61cb commit 4744c81
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 2 deletions.
9 changes: 7 additions & 2 deletions src/DataMapper/DataMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Mongolid\Schema\Schema;
use Mongolid\Util\ObjectIdUtils;
use Mongolid\Connection\Connection;
Use Mongolid\Util\SoftDeleteQueries;

/**
* The DataMapper class will abstract how an Entity is persisted and retrieved
Expand Down Expand Up @@ -259,13 +260,14 @@ public function where(
$cursorClass = $cacheable ? SchemaCacheableCursor::class : SchemaCursor::class;

$model = new $this->schema->entityClass;
$query = $this->prepareValueQuery($query);

return new $cursorClass(
$this->schema,
$this->getCollection(),
'find',
[
$this->prepareValueQuery($query),
SoftDeleteQueries::insertFilterForSoftDelete($query, $model),
[
'projection' => $this->prepareProjection($projection),
'eagerLoads' => $model->with ?? [],
Expand Down Expand Up @@ -302,8 +304,11 @@ public function first(
return $this->where($query, $projection, true)->first();
}

$model = new $this->schema->entityClass;
$query = $this->prepareValueQuery($query);

$document = $this->getCollection()->findOne(
$this->prepareValueQuery($query),
SoftDeleteQueries::insertFilterForSoftDelete($query, $model),
['projection' => $this->prepareProjection($projection)]
);

Expand Down
42 changes: 42 additions & 0 deletions src/Model/SoftDeletesTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace Mongolid\Model;

trait SoftDeletesTrait
{
public $enabledSoftDeletes = true;

public function isTrashed(): bool
{
return !is_null($this->{$this->getDeletedAtColumn()});
}

public function restore(): bool
{
$collumn = $this->getDeletedAtColumn();

if (!$this->{$collumn}) {
return false;
}

$this->{$collumn} = null;

return $this->execute('save');
}

public function forceDelete(): bool
{
$this->forceDelete = true;

return $this->execute('delete');
}

private function getDeletedAtColumn(): string
{
return defined(
static::class . '::DELETED_AT'
)
? static::DELETED_AT
: 'deleted_at';
}
}
36 changes: 36 additions & 0 deletions src/Util/SoftDeleteQueries.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Mongolid\Util;

use Mongolid\Model\ModelInterface;

class SoftDeleteQueries
{
public static function insertFilterForSoftDelete(array $query, ModelInterface $model): array
{
$field = self::getDeletedAtColumn($model);

return array_merge(
$query,
[
'$or' => [
[
$field => null,
],
[
$field => ['$exists' => false],
],
],
]
);
}

private static function getDeletedAtColumn(ModelInterface $model): string
{
return defined(
$model::class . '::DELETED_AT'
)
? $model::DELETED_AT
: 'deleted_at';
}
}
99 changes: 99 additions & 0 deletions tests/Integration/PersisteModelWithSoftDeleteTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

namespace Mongolid\Tests\Integration;

use DateTime;
use MongoDB\BSON\ObjectId;
use MongoDB\BSON\UTCDateTime;
use Mongolid\Tests\Stubs\ProductWithSoftDelete;

final class PersisteModelWithSoftDeleteTest extends IntegrationTestCase
{
private ObjectId $_id;

public function testShouldFindNotDeletedProduct(): void
{
// Set
$product = $this->persiteProduct();

// Actions
$result = ProductWithSoftDelete::where()->first();

// Assertion
$this->assertEquals($product, $result);
}

public function testShouldNotFindDeletedProduct(): void
{
// Set
$this->persiteProduct(true);

// Actions
$result = ProductWithSoftDelete::where()->first();

// Assertion
$this->assertNull($result);
}

public function testShouldFindDeletedProductWithFirst(): void
{
// Set
$product = $this->persiteProduct();

// Actions
$result = ProductWithSoftDelete::first($this->_id);

// Assertion
$this->assertEquals($product, $result);
}

public function testShouldNotFindDeletedProductWithFirst(): void
{
// Set
$this->persiteProduct(true);

// Actions
$result = ProductWithSoftDelete::first($this->_id);

// Assertion
$this->assertNull($result);
}

public function testShouldRestoreDeletedProduct(): void
{
// Set
$product = $this->persiteProduct(true);

// Actions
$product->restore();
$result = ProductWithSoftDelete::first($this->_id);

// Assertion
$this->assertEquals($product, $result);
}

protected function setUp(): void
{
parent::setUp();

$this->_id = new ObjectId('5bcb310783a7fcdf1bf1a672');
}

private function persiteProduct(bool $softDeleted = false): ProductWithSoftDelete
{
$product = new ProductWithSoftDelete();
$product->_id = $this->_id;
$product->short_name = 'Furadeira de Impacto Bosch com Chave de Mandril ';
$product->name = 'Furadeira de Impacto Bosch com Chave de Mandril e Acessórios 550W 1/2 GSB 550 RE 127V (110V)';

if ($softDeleted) {
$date = new UTCDateTime(new DateTime('today'));

$product->deleted_at = $date;
}

$product->save();

return $product;
}
}
10 changes: 10 additions & 0 deletions tests/Stubs/ProductWithSoftDelete.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Mongolid\Tests\Stubs;

use Mongolid\Model\SoftDeletesTrait;

class ProductWithSoftDelete extends Product
{
use SoftDeletesTrait;
}
67 changes: 67 additions & 0 deletions tests/Unit/Model/SoftDeletesTraitTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace Mongolid\Model;

use DateTime;
use MongoDB\BSON\UTCDateTime;
use Mongolid\DataMapper\DataMapper;
use Mongolid\Schema\DynamicSchema;
use Mongolid\TestCase;
use Mongolid\Tests\Stubs\Product;
use Mockery as m;
use Mongolid\Tests\Stubs\ProductWithSoftDelete;

class SoftDeletesTraitTest extends TestCase
{
public function testShouldReturnStatusOfSoftDelete(): void
{
// Set
$date = new UTCDateTime(new DateTime('today'));
$product = new ProductWithSoftDelete();
$product->deleted_at = $date;

// Actions
$actual = $product->isTrashed();

// Assertions
$this->assertTrue($actual);
}

public function testShouldRestoreProduct(): void
{
// Set
$product = new ProductWithSoftDelete();
$date = new UTCDateTime(new DateTime('today'));
$product->deleted_at = $date;
$dataMapper = $this->instance(
DataMapper::class,
m::mock(DataMapper::class)
);

// Expectations
$dataMapper->expects()
->setSchema(m::type(DynamicSchema::class));

$dataMapper->expects()
->save(m::type(Product::class), m::type('array'))
->andReturnTrue();

// Actions
$actual = $product->restore();

// Assertions
$this->assertTrue($actual);
}

public function testShouldNotRestoreProduct(): void
{
// Set
$product = new ProductWithSoftDelete();

// Actions
$actual = $product->restore();

// Assertions
$this->assertFalse($actual);
}
}

0 comments on commit 4744c81

Please sign in to comment.