Skip to content

Commit

Permalink
Merge pull request #576 from wayofdev/feat/soft-deletes
Browse files Browse the repository at this point in the history
  • Loading branch information
lotyp authored Feb 13, 2024
2 parents e8c4d38 + dc1b6b5 commit 760cf51
Show file tree
Hide file tree
Showing 21 changed files with 918 additions and 281 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
"phpunit/phpunit": "^10.5",
"roave/security-advisories": "dev-latest",
"spatie/laravel-ray": "^1.32",
"wayofdev/cs-fixer-config": "^1.1"
"wayofdev/cs-fixer-config": "^1.1",
"beberlei/assert": "^3.3"
},
"autoload": {
"psr-4": {
Expand Down
583 changes: 321 additions & 262 deletions composer.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ protected function verifyConfigured(): bool
return true;
}

protected function verifyEnvironment(string $confirmationQuestion = null): bool
protected function verifyEnvironment(?string $confirmationQuestion = null): bool
{
$confirmationQuestion = $confirmationQuestion ?? self::DEFAULT_CONFIRMATION;

Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/Laravel/LoggerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public function __construct(private readonly Logger $logger)
{
}

public function getLogger(DriverInterface $driver = null): LoggerInterface
public function getLogger(?DriverInterface $driver = null): LoggerInterface
{
return $this->logger->getLogger();
}
Expand Down
30 changes: 30 additions & 0 deletions src/Testing/Concerns/InteractsWithDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
use PHPUnit\Framework\Constraint\LogicalNot as ReverseConstraint;
use WayOfDev\Cycle\Testing\Constraints\CountInDatabase;
use WayOfDev\Cycle\Testing\Constraints\HasInDatabase;
use WayOfDev\Cycle\Testing\Constraints\NotSoftDeletedInDatabase;
use WayOfDev\Cycle\Testing\Constraints\SoftDeletedInDatabase;

/**
* @method void assertThat($value, Constraint $constraint, string $message = '')
Expand Down Expand Up @@ -88,4 +90,32 @@ protected function cleanupMigrations(string $pathGlob): void
File::delete($file);
}
}

protected function assertSoftDeleted($table, array $data = [], $connection = null, $deletedAtColumn = 'deleted_at'): self
{
$this->assertThat(
$table,
new SoftDeletedInDatabase(
app(DatabaseProviderInterface::class),
$data,
$deletedAtColumn,
)
);

return $this;
}

protected function assertNotSoftDeleted($table, array $data = [], $connection = null, $deletedAtColumn = 'deleted_at'): self
{
$this->assertThat(
$table,
new NotSoftDeletedInDatabase(
app(DatabaseProviderInterface::class),
$data,
$deletedAtColumn,
)
);

return $this;
}
}
11 changes: 1 addition & 10 deletions src/Testing/Constraints/HasInDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use Cycle\Database\DatabaseInterface;
use Cycle\Database\DatabaseProviderInterface;
use Cycle\Database\Query\SelectQuery;
use JsonException;
use PHPUnit\Framework\Constraint\Constraint;
use Throwable;

Expand Down Expand Up @@ -44,9 +43,6 @@ public function matches(mixed $other): bool
}
}

/**
* @throws JsonException
*/
public function failureDescription(mixed $other): string
{
return sprintf(
Expand All @@ -56,12 +52,7 @@ public function failureDescription(mixed $other): string
);
}

/**
* @param mixed|null $options
*
* @throws JsonException
*/
public function toString($options = null): string
public function toString(mixed $options = null): string
{
if (is_int($options)) {
$options |= JSON_THROW_ON_ERROR;
Expand Down
64 changes: 64 additions & 0 deletions src/Testing/Constraints/NotSoftDeletedInDatabase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace WayOfDev\Cycle\Testing\Constraints;

use Cycle\Database\DatabaseInterface;
use Cycle\Database\DatabaseProviderInterface;
use Cycle\Database\Query\SelectQuery;
use PHPUnit\Framework\Constraint\Constraint;
use Throwable;

use function json_encode;
use function sprintf;

class NotSoftDeletedInDatabase extends Constraint
{
protected int $show = 3;

protected DatabaseInterface $database;

protected array $data;

protected string $deletedAtColumn;

public function __construct(DatabaseProviderInterface $database, array $data, string $deletedAtColumn)
{
$this->data = $data;

$this->database = $database->database();

$this->deletedAtColumn = $deletedAtColumn;
}

public function matches(mixed $other): bool
{
/** @var SelectQuery $tableInterface */
$tableInterface = $this->database->table($other);

try {
$count = $tableInterface->where($this->data)
->andWhere($this->deletedAtColumn, '=', null)
->count();

return 0 < $count;
} catch (Throwable $e) {
return false;
}
}

public function failureDescription($other): string
{
return sprintf(
'any existing row in the table [%s] matches the attributes %s.\n',
$other,
$this->toString()
);
}

public function toString(): string
{
return json_encode($this->data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
}
}
64 changes: 64 additions & 0 deletions src/Testing/Constraints/SoftDeletedInDatabase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace WayOfDev\Cycle\Testing\Constraints;

use Cycle\Database\DatabaseInterface;
use Cycle\Database\DatabaseProviderInterface;
use Cycle\Database\Query\SelectQuery;
use PHPUnit\Framework\Constraint\Constraint;
use Throwable;

use function json_encode;
use function sprintf;

class SoftDeletedInDatabase extends Constraint
{
protected int $show = 3;

protected DatabaseInterface $database;

protected array $data;

protected string $deletedAtColumn;

public function __construct(DatabaseProviderInterface $database, array $data, string $deletedAtColumn)
{
$this->data = $data;

$this->database = $database->database();

$this->deletedAtColumn = $deletedAtColumn;
}

public function matches(mixed $other): bool
{
/** @var SelectQuery $tableInterface */
$tableInterface = $this->database->table($other);

try {
$count = $tableInterface->where($this->data)
->andWhere($this->deletedAtColumn, '!=', null)
->count();

return 0 < $count;
} catch (Throwable $e) {
return false;
}
}

public function failureDescription($other): string
{
return sprintf(
'a soft deleted row in the table [%s] matches the attributes %s.',
$other,
$this->toString()
);
}

public function toString(): string
{
return json_encode($this->data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
}
}
96 changes: 96 additions & 0 deletions tests/app/Entities/Footprint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

declare(strict_types=1);

namespace WayOfDev\App\Entities;

use Cycle\Database\DatabaseInterface;
use JsonException;
use JsonSerializable;
use Ramsey\Uuid\Uuid;
use Stringable;

use function json_decode;
use function json_encode;

final class Footprint implements JsonSerializable, Stringable
{
private UserId $id;

private string $party;

private string $realm;

public static function empty(string $authorizedParty = 'guest-party', string $realm = 'guest-realm'): self
{
return new self(UserId::create(Uuid::NIL), $authorizedParty, $realm);
}

public static function random(string $authorizedParty = 'random-party', string $realm = 'random-realm'): self
{
return new self(UserId::create(Uuid::uuid7()->toString()), $authorizedParty, $realm);
}

public static function fromArray(array $data): self
{
$userId = UserId::fromString($data['id']);

return new self($userId, $data['party'], $data['realm']);
}

/**
* https://cycle-orm.dev/docs/advanced-column-wrappers/2.x/en.
*
* @throws JsonException
*/
public static function castValue(string $value, DatabaseInterface $db): self
{
return self::fromArray(
json_decode($value, true, 512, JSON_THROW_ON_ERROR)
);
}

public function toArray(): array
{
return [
'id' => $this->id->toString(),
'party' => $this->party,
'realm' => $this->realm,
];
}

public function id(): UserId
{
return $this->id;
}

public function party(): string
{
return $this->party;
}

public function realm(): string
{
return $this->realm;
}

public function jsonSerialize(): array
{
return $this->toArray();
}

/**
* @throws JsonException
*/
public function __toString(): string
{
return json_encode($this->toArray(), JSON_THROW_ON_ERROR);
}

private function __construct(UserId $id, string $party, string $realm)
{
$this->id = $id;
$this->party = $party;
$this->realm = $realm;
}
}
43 changes: 43 additions & 0 deletions tests/app/Entities/HasSignatures.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace WayOfDev\App\Entities;

use Cycle\Annotated\Annotation\Relation\Embedded;

trait HasSignatures
{
#[Embedded(target: Signature::class, prefix: 'created_')]
private Signature $created;

#[Embedded(target: Signature::class, prefix: 'updated_')]
private Signature $updated;

#[Embedded(target: Signature::class, prefix: 'deleted_')]
private ?Signature $deleted;

public function created(): Signature
{
return $this->created;
}

public function updated(): Signature
{
return $this->updated;
}

public function deleted(): ?Signature
{
if (! $this->deleted?->defined()) {
return null;
}

return $this->deleted;
}

public function softDelete(Signature $deleted): void
{
$this->deleted = $deleted;
}
}
Loading

0 comments on commit 760cf51

Please sign in to comment.