diff --git a/src/PrimaryKey.php b/src/PrimaryKey.php index bb3e965ddc7bc5bbcee351bd008c83cb77c0842a..2cead6699d141ada8f41de2dd7ede4223cf8bd61 100644 --- a/src/PrimaryKey.php +++ b/src/PrimaryKey.php @@ -17,6 +17,11 @@ interface PrimaryKey */ public function getCondition(Table $table): Condition; + /** + * @return string[] + */ + public static function getColumnNames(): array; + /** @return static */ //public static function fromRow($row); diff --git a/src/Scaffolding/PrimaryKeyImplementation.php b/src/Scaffolding/PrimaryKeyImplementation.php index 56c68dee0a0307a837ca3938fb1c3a390b29f4a7..73ce2da484e5c1747171a72b0ff8decd4c661506 100644 --- a/src/Scaffolding/PrimaryKeyImplementation.php +++ b/src/Scaffolding/PrimaryKeyImplementation.php @@ -51,6 +51,12 @@ final class PrimaryKeyImplementation implements Capability ), ]); + $classType->addMethod('getColumnNames') + ->addComment('@return string[]') + ->setReturnType('array') + ->setStatic() + ->addBody('return ?;', [map($definition->getFields(), static fn(Field $field) => $field->getName())]); + $namespace->addUse(Condition::class); $namespace->addUse(Composite::class); $namespace->addUseFunction('Grifart\Tables\Conditions\equalTo'); diff --git a/src/Scaffolding/TableImplementation.php b/src/Scaffolding/TableImplementation.php index 44c946141a22ba6861d1c0e1970e5e23968a3aeb..9f850fa7967dfa034fea74cd1ce37ab88f2d4bf8 100644 --- a/src/Scaffolding/TableImplementation.php +++ b/src/Scaffolding/TableImplementation.php @@ -322,6 +322,16 @@ final class TableImplementation implements Capability '$this->tableManager->insert($this, $changes);', ); + $classType->addMethod('insertAndGet') + ->addComment('@throws RowWithGivenPrimaryKeyAlreadyExists') + ->setReturnType($this->rowClass) + ->setParameters([ + (new Code\Parameter('changes'))->setType($this->modificationClass), + ]) + ->addBody('$row = $this->tableManager->insertAndGet($this, $changes);') + ->addBody('\assert($row instanceof ?);', [new Code\Literal($namespace->simplifyName($this->rowClass))]) + ->addBody('return $row;'); + $classType->addMethod('update') ->addComment('@throws GivenSearchCriteriaHaveNotMatchedAnyRows') ->setReturnType('void') @@ -332,6 +342,41 @@ final class TableImplementation implements Capability '$this->tableManager->update($this, $changes);', ); + $classType->addMethod('updateAndGet') + ->addComment('@throws GivenSearchCriteriaHaveNotMatchedAnyRows') + ->setReturnType($this->rowClass) + ->setParameters([ + (new Code\Parameter('changes'))->setType($this->modificationClass), + ]) + ->addBody('$row = $this->tableManager->updateAndGet($this, $changes);') + ->addBody('\assert($row instanceof ?);', [new Code\Literal($namespace->simplifyName($this->rowClass))]) + ->addBody('return $row;'); + + $classType->addMethod('updateBy') + ->setReturnType('void') + ->setParameters([ + (new Code\Parameter('conditions'))->setType(Condition::class . '|array'), + (new Code\Parameter('changes'))->setType($this->modificationClass), + ]) + ->addComment('@param Condition|Condition[] $conditions') + ->addBody('$this->tableManager->updateBy($this, $conditions, $changes);'); + + $classType->addMethod('upsert') + ->setReturnType('void') + ->setParameters([ + (new Code\Parameter('changes'))->setType($this->modificationClass), + ]) + ->addBody('$this->tableManager->upsert($this, $changes);'); + + $classType->addMethod('upsertAndGet') + ->setReturnType($this->rowClass) + ->setParameters([ + (new Code\Parameter('changes'))->setType($this->modificationClass), + ]) + ->addBody('$row = $this->tableManager->upsertAndGet($this, $changes);') + ->addBody('\assert($row instanceof ?);', [new Code\Literal($namespace->simplifyName($this->rowClass))]) + ->addBody('return $row;'); + $classType->addMethod('delete') ->setReturnType('void') ->setParameters([ @@ -343,6 +388,27 @@ final class TableImplementation implements Capability ]) ->addBody('$this->tableManager->delete($this, $primaryKey);'); + $classType->addMethod('deleteAndGet') + ->setReturnType($this->rowClass) + ->setParameters([ + (new Code\Parameter('rowOrKey'))->setType($this->rowClass . '|' . $this->primaryKeyClass) + ]) + ->addBody('$primaryKey = $rowOrKey instanceof ? \? $rowOrKey : ?::fromRow($rowOrKey);', [ + new Code\Literal($namespace->simplifyName($this->primaryKeyClass)), + new Code\Literal($namespace->simplifyName($this->primaryKeyClass)), + ]) + ->addBody('$row = $this->tableManager->deleteAndGet($this, $primaryKey);') + ->addBody('\assert($row instanceof ?);', [new Code\Literal($namespace->simplifyName($this->rowClass))]) + ->addBody('return $row;'); + + $classType->addMethod('deleteBy') + ->setReturnType('void') + ->setParameters([ + (new Code\Parameter('conditions'))->setType(Condition::class . '|array'), + ]) + ->addComment('@param Condition|Condition[] $conditions') + ->addBody('$this->tableManager->deleteBy($this, $conditions);'); + $namespace->addUse(TableManager::class); $namespace->addUse(TypeResolver::class); $constructor = $classType->addMethod('__construct'); diff --git a/src/SingleConnectionTableManager.php b/src/SingleConnectionTableManager.php index 94127603689ca7f7556b13c3ae169ba11033fac7..7d5258497824ab00fe48ce664a9eec8dcb89096c 100644 --- a/src/SingleConnectionTableManager.php +++ b/src/SingleConnectionTableManager.php @@ -10,6 +10,7 @@ use Grifart\Tables\Conditions\Composite; use Grifart\Tables\Conditions\Condition; use Grifart\Tables\OrderBy\OrderBy; use Grifart\Tables\OrderBy\OrderByDirection; +use Grifart\Tables\Table as TableType; use Nette\Utils\Paginator; use function Phun\map; use function Phun\mapWithKeys; @@ -24,32 +25,6 @@ final class SingleConnectionTableManager implements TableManager private IConnection $connection, ) {} - /** - * @template TableType of Table - * @param TableType $table - * @param Modifications<TableType> $changes - * @throws RowWithGivenPrimaryKeyAlreadyExists - */ - public function insert(Table $table, Modifications $changes): void - { - \assert($changes->getPrimaryKey() === NULL); - - try { - $this->connection->query( - 'INSERT', - 'INTO %n.%n', $table::getSchema(), $table::getTableName(), - mapWithKeys( - $changes->getModifications(), - static fn(string $columnName, mixed $value) => $table->getTypeOf($columnName)->toDatabase($value), - ), - ); - } catch (UniqueConstraintViolationException $e) { - throw new RowWithGivenPrimaryKeyAlreadyExists(previous: $e); - } - - \assert($this->connection->getAffectedRows() === 1); - } - /** * @template TableType of Table * @param TableType $table @@ -181,6 +156,88 @@ final class SingleConnectionTableManager implements TableManager ); } + /** + * @template TableType of Table + * @param TableType $table + * @param Modifications<TableType> $changes + * @throws RowWithGivenPrimaryKeyAlreadyExists + * @throws GivenSearchCriteriaHaveNotMatchedAnyRows + */ + public function save(Table $table, Modifications $changes): void { + if ($changes->getPrimaryKey() === NULL) { + // INSERT + $this->insert($table, $changes); + return; + } + + // UPDATE: + $this->update($table, $changes); + } + + /** + * @template TableType of Table + * @param TableType $table + * @param Modifications<TableType> $changes + * @throws RowWithGivenPrimaryKeyAlreadyExists + */ + public function insert(Table $table, Modifications $changes): void + { + \assert($changes->getPrimaryKey() === NULL); + + try { + $this->connection->query( + 'INSERT', + 'INTO %n.%n', $table::getSchema(), $table::getTableName(), + mapWithKeys( + $changes->getModifications(), + static fn(string $columnName, mixed $value) => $table->getTypeOf($columnName)->toDatabase($value), + ), + ); + } catch (UniqueConstraintViolationException $e) { + throw new RowWithGivenPrimaryKeyAlreadyExists(previous: $e); + } + + \assert($this->connection->getAffectedRows() === 1); + } + + /** + * @template TableType of Table + * @param TableType $table + * @param Modifications<TableType> $changes + * @throws RowWithGivenPrimaryKeyAlreadyExists + */ + public function insertAndGet(Table $table, Modifications $changes): Row + { + \assert($changes->getPrimaryKey() === NULL); + + try { + $result = $this->connection->query( + 'INSERT INTO %n.%n', $table::getSchema(), $table::getTableName(), + mapWithKeys( + $changes->getModifications(), + static fn(string $columnName, mixed $value) => $table->getTypeOf($columnName)->toDatabase($value), + ), + 'RETURNING *', + ); + } catch (UniqueConstraintViolationException $e) { + throw new RowWithGivenPrimaryKeyAlreadyExists(previous: $e); + } + + \assert($this->connection->getAffectedRows() === 1); + + $dibiRow = $result->fetch(); + \assert($dibiRow instanceof \Dibi\Row); + + /** @var class-string<Row> $rowClass */ + $rowClass = $table::getRowClass(); + return $rowClass::reconstitute( + mapWithKeys( + $dibiRow->toArray(), + static fn(string $columnName, mixed $value) => $table->getTypeOf($columnName)->fromDatabase($value), + ), + ); + } + /** * @template TableType of Table * @param TableType $table @@ -210,6 +267,135 @@ final class SingleConnectionTableManager implements TableManager } } + /** + * @template TableType of Table + * @param TableType $table + * @param Modifications<TableType> $changes + * @throws GivenSearchCriteriaHaveNotMatchedAnyRows + */ + public function updateAndGet(Table $table, Modifications $changes): Row + { + $primaryKey = $changes->getPrimaryKey(); + \assert($primaryKey !== NULL); + $result = $this->connection->query( + 'UPDATE %n.%n', $table::getSchema(), $table::getTableName(), + 'SET %a', + mapWithKeys( + $changes->getModifications(), + static fn(string $columnName, mixed $value) => $table->getTypeOf($columnName)->toDatabase($value), + ), + 'WHERE %ex', $primaryKey->getCondition($table)->toSql()->getValues(), + 'RETURNING *', + ); + + $affectedRows = $this->connection->getAffectedRows(); + if ($affectedRows !== 1) { + if ($affectedRows === 0) { + throw new GivenSearchCriteriaHaveNotMatchedAnyRows(); + } + + throw new ProbablyBrokenPrimaryIndexImplementation($table, $affectedRows); + } + + $dibiRow = $result->fetch(); + \assert($dibiRow instanceof \Dibi\Row); + + /** @var class-string<Row> $rowClass */ + $rowClass = $table::getRowClass(); + return $rowClass::reconstitute( + mapWithKeys( + $dibiRow->toArray(), + static fn(string $columnName, mixed $value) => $table->getTypeOf($columnName)->fromDatabase($value), + ), + ); + } + + /** + * @template TableType of Table + * @param TableType $table + * @param Condition|Condition[] $conditions + * @param Modifications<TableType> $changes + */ + public function updateBy(Table $table, Condition|array $conditions, Modifications $changes): void + { + \assert($changes->getPrimaryKey() === null); + + $this->connection->query( + 'UPDATE %n.%n', $table::getSchema(), $table::getTableName(), + 'SET %a', + mapWithKeys( + $changes->getModifications(), + static fn(string $columnName, mixed $value) => $table->getTypeOf($columnName)->toDatabase($value), + ), + 'WHERE %ex', (\is_array($conditions) ? Composite::and(...$conditions) : $conditions)->toSql()->getValues(), + ); + } + + /** + * @template TableType of Table + * @param TableType $table + * @param Modifications<TableType> $changes + */ + public function upsert(Table $table, Modifications $changes): void + { + \assert($changes->getPrimaryKey() === null); + + $values = mapWithKeys( + $changes->getModifications(), + static fn(string $columnName, mixed $value) => $table->getTypeOf($columnName)->toDatabase($value), + ); + + $primaryKey = $table::getPrimaryKeyClass(); + + $this->connection->query( + 'INSERT INTO %n.%n', $table::getSchema(), $table::getTableName(), + $values, + 'ON CONFLICT (%n)', $primaryKey::getColumnNames(), + 'DO UPDATE SET %a', $values, + ); + + \assert($this->connection->getAffectedRows() === 1); + } + + /** + * @template TableType of Table + * @param TableType $table + * @param Modifications<TableType> $changes + */ + public function upsertAndGet(Table $table, Modifications $changes): Row + { + \assert($changes->getPrimaryKey() === null); + + $values = mapWithKeys( + $changes->getModifications(), + static fn(string $columnName, mixed $value) => $table->getTypeOf($columnName)->toDatabase($value), + ); + + $primaryKey = $table::getPrimaryKeyClass(); + + $result = $this->connection->query( + 'INSERT INTO %n.%n', $table::getSchema(), $table::getTableName(), + $values, + 'ON CONFLICT (%n)', $primaryKey::getColumnNames(), + 'DO UPDATE SET %a', $values, + 'RETURNING *', + ); + + \assert($this->connection->getAffectedRows() === 1); + + $dibiRow = $result->fetch(); + \assert($dibiRow instanceof \Dibi\Row); + + /** @var class-string<Row> $rowClass */ + $rowClass = $table::getRowClass(); + return $rowClass::reconstitute( + mapWithKeys( + $dibiRow->toArray(), + static fn(string $columnName, mixed $value) => $table->getTypeOf($columnName)->fromDatabase($value), + ), + ); + } + /** * @template TableType of Table * @param TableType $table @@ -228,18 +414,43 @@ final class SingleConnectionTableManager implements TableManager /** * @template TableType of Table * @param TableType $table - * @param Modifications<TableType> $changes - * @throws RowWithGivenPrimaryKeyAlreadyExists - * @throws GivenSearchCriteriaHaveNotMatchedAnyRows + * @param PrimaryKey<TableType> $primaryKey */ - public function save(Table $table, Modifications $changes): void { - if ($changes->getPrimaryKey() === NULL) { - // INSERT - $this->insert($table, $changes); - return; - } + public function deleteAndGet(Table $table, PrimaryKey $primaryKey): Row + { + $result = $this->connection->query( + 'DELETE', + 'FROM %n.%n', $table::getSchema(), $table::getTableName(), + 'WHERE %ex', $primaryKey->getCondition($table)->toSql()->getValues(), + 'RETURNING *', + ); - // UPDATE: - $this->update($table, $changes); + \assert($this->connection->getAffectedRows() === 1); + + $dibiRow = $result->fetch(); + \assert($dibiRow instanceof \Dibi\Row); + + /** @var class-string<Row> $rowClass */ + $rowClass = $table::getRowClass(); + return $rowClass::reconstitute( + mapWithKeys( + $dibiRow->toArray(), + static fn(string $columnName, mixed $value) => $table->getTypeOf($columnName)->fromDatabase($value), + ), + ); + } + + /** + * @template TableType of Table + * @param TableType $table + * @param Condition|Condition[] $conditions + */ + public function deleteBy(Table $table, Condition|array $conditions): void + { + $this->connection->query( + 'DELETE', + 'FROM %n.%n', $table::getSchema(), $table::getTableName(), + 'WHERE %ex', (\is_array($conditions) ? Composite::and(...$conditions) : $conditions)->toSql()->getValues(), + ); } } diff --git a/src/TableManager.php b/src/TableManager.php index 7575c1c8e0ea5acefac4a8183b093db270e7bf51..370994e863d38aff1198aada3af0de1f63e4975e 100644 --- a/src/TableManager.php +++ b/src/TableManager.php @@ -11,14 +11,6 @@ use Nette\Utils\Paginator; interface TableManager { - /** - * @template TableType of Table - * @param TableType $table - * @param Modifications<TableType> $changes - * @throws RowWithGivenPrimaryKeyAlreadyExists - */ - public function insert(Table $table, Modifications $changes): void; - /** * @template TableType of Table * @param TableType $table @@ -55,6 +47,31 @@ interface TableManager */ public function findOneBy(Table $table, Condition|array $conditions, array $orderBy = [], bool $required = true, bool $unique = true): ?Row; + /** + * @template TableType of Table + * @param TableType $table + * @param Modifications<TableType> $changes + * @throws RowWithGivenPrimaryKeyAlreadyExists + * @throws GivenSearchCriteriaHaveNotMatchedAnyRows + */ + public function save(Table $table, Modifications $changes): void; + + /** + * @template TableType of Table + * @param TableType $table + * @param Modifications<TableType> $changes + * @throws RowWithGivenPrimaryKeyAlreadyExists + */ + public function insert(Table $table, Modifications $changes): void; + + /** + * @template TableType of Table + * @param TableType $table + * @param Modifications<TableType> $changes + * @throws RowWithGivenPrimaryKeyAlreadyExists + */ + public function insertAndGet(Table $table, Modifications $changes): Row; + /** * @template TableType of Table * @param TableType $table @@ -63,6 +80,36 @@ interface TableManager */ public function update(Table $table, Modifications $changes): void; + /** + * @template TableType of Table + * @param TableType $table + * @param Modifications<TableType> $changes + * @throws GivenSearchCriteriaHaveNotMatchedAnyRows if no rows matches given criteria + */ + public function updateAndGet(Table $table, Modifications $changes): Row; + + /** + * @template TableType of Table + * @param TableType $table + * @param Condition|Condition[] $conditions + * @param Modifications<TableType> $changes + */ + public function updateBy(Table $table, Condition|array $conditions, Modifications $changes): void; + + /** + * @template TableType of Table + * @param TableType $table + * @param Modifications<TableType> $changes + */ + public function upsert(Table $table, Modifications $changes): void; + + /** + * @template TableType of Table + * @param TableType $table + * @param Modifications<TableType> $changes + */ + public function upsertAndGet(Table $table, Modifications $changes): Row; + /** * @template TableType of Table * @param TableType $table @@ -73,9 +120,14 @@ interface TableManager /** * @template TableType of Table * @param TableType $table - * @param Modifications<TableType> $changes - * @throws RowWithGivenPrimaryKeyAlreadyExists - * @throws GivenSearchCriteriaHaveNotMatchedAnyRows + * @param PrimaryKey<TableType> $primaryKey */ - public function save(Table $table, Modifications $changes): void; + public function deleteAndGet(Table $table, PrimaryKey $primaryKey): Row; + + /** + * @template TableType of Table + * @param TableType $table + * @param Condition|Condition[] $conditions + */ + public function deleteBy(Table $table, Condition|array $conditions): void; } diff --git a/tests/BulkTableTest.phpt b/tests/BulkTableTest.phpt new file mode 100644 index 0000000000000000000000000000000000000000..577c20b7b7a21ebd2c21fac7c57a89f7a15ce916 --- /dev/null +++ b/tests/BulkTableTest.phpt @@ -0,0 +1,52 @@ +<?php + +declare(strict_types=1); + +namespace Grifart\Tables\Tests; + +use Grifart\Tables\RowNotFound; +use Grifart\Tables\Tests\Fixtures\BulkModifications; +use Grifart\Tables\Tests\Fixtures\BulkTable; +use Grifart\Tables\Tests\Fixtures\TestFixtures; +use Grifart\Tables\TooManyRowsFound; +use Tester\Assert; +use function Grifart\Tables\Conditions\lesserThan; + +require __DIR__ . '/bootstrap.php'; + +$connection = connect(); + +$connection->nativeQuery("TRUNCATE TABLE public.bulk"); +$connection->nativeQuery("INSERT INTO public.bulk (id, value) VALUES ('2e166649-da0f-4c0e-bc3a-4759aac50092', 42), ('6c554e1c-6be0-4d52-87e2-602782bba59e', -5), ('a7723ed8-ec2e-4e06-9a84-7da20532103e', 0);"); + +$table = new BulkTable( + TestFixtures::createTableManager($connection), + TestFixtures::createTypeResolver($connection), +); + +[$a, $b, $c] = $table->getAll([$table->value()->ascending()]); +Assert::same(-5, $a->getValue()); +Assert::same(0, $b->getValue()); +Assert::same(42, $c->getValue()); + +$changes = BulkModifications::new(); +$changes->modifyFlagged(true); + +$table->updateBy( + $table->value()->is(lesserThan(0)), + $changes, +); + +[$a, $b, $c] = $table->getAll([$table->value()->ascending()]); +Assert::true($a->getFlagged()); +Assert::false($b->getFlagged()); +Assert::false($c->getFlagged()); + +$table->deleteBy( + $table->flagged()->is(true), +); + +$all = $table->getAll([$table->value()->ascending()]); +Assert::count(2, $all); +Assert::same(0, $all[0]->getValue()); +Assert::same(42, $all[1]->getValue()); diff --git a/tests/Fixtures/.definition.php b/tests/Fixtures/.definition.php index 155eba0013b2416eaa2b2634d37c2c72653c0a57..515a14db9d0580fcc50ccf83765dd9033c6d643c 100644 --- a/tests/Fixtures/.definition.php +++ b/tests/Fixtures/.definition.php @@ -48,4 +48,12 @@ return [ GeneratedTable::class, GeneratedPrimaryKey::class, ), + ...$tableDefinitions->for( + 'public', + 'bulk', + BulkRow::class, + BulkModifications::class, + BulkTable::class, + BulkPrimaryKey::class, + ), ]; diff --git a/tests/Fixtures/BulkModifications.php b/tests/Fixtures/BulkModifications.php new file mode 100644 index 0000000000000000000000000000000000000000..21ae1810bc8cf35bca36530c12fbe1b2789dc43e --- /dev/null +++ b/tests/Fixtures/BulkModifications.php @@ -0,0 +1,56 @@ +<?php + +/** + * Do not edit. This is generated file. Modify definition file instead. + */ + +declare(strict_types=1); + +namespace Grifart\Tables\Tests\Fixtures; + +use Grifart\Tables\Modifications; +use Grifart\Tables\ModificationsTrait; + +/** + * @implements Modifications<BulkTable> + */ +final class BulkModifications implements Modifications +{ + /** @use ModificationsTrait<BulkTable> */ + use ModificationsTrait; + + public static function update(BulkPrimaryKey $primaryKey): self + { + return self::_update($primaryKey); + } + + + public static function new(): self + { + return self::_new(); + } + + + public static function forTable(): string + { + return BulkTable::class; + } + + + public function modifyId(Uuid $id): void + { + $this->modifications['id'] = $id; + } + + + public function modifyValue(int $value): void + { + $this->modifications['value'] = $value; + } + + + public function modifyFlagged(bool $flagged): void + { + $this->modifications['flagged'] = $flagged; + } +} diff --git a/tests/Fixtures/BulkPrimaryKey.php b/tests/Fixtures/BulkPrimaryKey.php new file mode 100644 index 0000000000000000000000000000000000000000..38f4abae608298b7fe9e96cbd704451d98ecf9ae --- /dev/null +++ b/tests/Fixtures/BulkPrimaryKey.php @@ -0,0 +1,61 @@ +<?php + +/** + * Do not edit. This is generated file. Modify definition file instead. + */ + +declare(strict_types=1); + +namespace Grifart\Tables\Tests\Fixtures; + +use Grifart\Tables\Conditions\Composite; +use Grifart\Tables\Conditions\Condition; +use Grifart\Tables\PrimaryKey; +use Grifart\Tables\Table; +use function Grifart\Tables\Conditions\equalTo; + +/** + * @implements PrimaryKey<BulkTable> + */ +final class BulkPrimaryKey implements PrimaryKey +{ + private function __construct( + private Uuid $id, + ) { + } + + + public static function from(Uuid $id): self + { + return new self($id); + } + + + public static function fromRow(BulkRow $row): self + { + return self::from($row->getId()); + } + + + /** + * @return string[] + */ + public static function getColumnNames(): array + { + return ['id']; + } + + + public function getCondition(Table $table): Condition + { + return Composite::and( + $table->id()->is(equalTo($this->id)), + ); + } + + + public function getId(): Uuid + { + return $this->id; + } +} diff --git a/tests/Fixtures/BulkRow.php b/tests/Fixtures/BulkRow.php new file mode 100644 index 0000000000000000000000000000000000000000..30853abc487f98ecf529f323c029951da2f2fa7c --- /dev/null +++ b/tests/Fixtures/BulkRow.php @@ -0,0 +1,46 @@ +<?php + +/** + * Do not edit. This is generated file. Modify definition file instead. + */ + +declare(strict_types=1); + +namespace Grifart\Tables\Tests\Fixtures; + +use Grifart\Tables\Row; + +final class BulkRow implements Row +{ + private function __construct( + private Uuid $id, + private int $value, + private bool $flagged, + ) { + } + + + public function getId(): Uuid + { + return $this->id; + } + + + public function getValue(): int + { + return $this->value; + } + + + public function getFlagged(): bool + { + return $this->flagged; + } + + + public static function reconstitute(array $values): static + { + /** @var array{id: Uuid, value: int, flagged: bool} $values */ + return new static($values['id'], $values['value'], $values['flagged']); + } +} diff --git a/tests/Fixtures/BulkTable.php b/tests/Fixtures/BulkTable.php new file mode 100644 index 0000000000000000000000000000000000000000..6d1038025f2464e2e23a2f4529b7a626a42bd804 --- /dev/null +++ b/tests/Fixtures/BulkTable.php @@ -0,0 +1,378 @@ +<?php + +/** + * Do not edit. This is generated file. Modify definition file instead. + */ + +declare(strict_types=1); + +namespace Grifart\Tables\Tests\Fixtures; + +use Grifart\Tables\Column; +use Grifart\Tables\ColumnMetadata; +use Grifart\Tables\ColumnNotFound; +use Grifart\Tables\Conditions\Condition; +use Grifart\Tables\DefaultOrExistingValue; +use Grifart\Tables\Expression; +use Grifart\Tables\GivenSearchCriteriaHaveNotMatchedAnyRows; +use Grifart\Tables\OrderBy\OrderBy; +use Grifart\Tables\RowNotFound; +use Grifart\Tables\RowWithGivenPrimaryKeyAlreadyExists; +use Grifart\Tables\Table; +use Grifart\Tables\TableManager; +use Grifart\Tables\TooManyRowsFound; +use Grifart\Tables\Type; +use Grifart\Tables\TypeResolver; +use Nette\Utils\Paginator; + +final class BulkTable implements Table +{ + public const ID = 'id'; + public const VALUE = 'value'; + public const FLAGGED = 'flagged'; + + /** @var array{id: Column<self, Uuid>, value: Column<self, int>, flagged: Column<self, bool>} */ + private array $columns; + + + public static function getSchema(): string + { + return 'public'; + } + + + public static function getTableName(): string + { + return 'bulk'; + } + + + public static function getPrimaryKeyClass(): string + { + return BulkPrimaryKey::class; + } + + + public static function getRowClass(): string + { + return BulkRow::class; + } + + + public static function getModificationClass(): string + { + return BulkModifications::class; + } + + + /** + * @return ColumnMetadata[] + */ + public static function getDatabaseColumns(): array + { + return [ + 'id' => new ColumnMetadata('id', 'uuid', false, false, false), + 'value' => new ColumnMetadata('value', 'integer', false, false, false), + 'flagged' => new ColumnMetadata('flagged', 'boolean', false, true, false) + ]; + } + + + public function find(BulkPrimaryKey $primaryKey): ?BulkRow + { + $row = $this->tableManager->find($this, $primaryKey, required: false); + \assert($row instanceof BulkRow || $row === null); + return $row; + } + + + /** + * @throws RowNotFound + */ + public function get(BulkPrimaryKey $primaryKey): BulkRow + { + $row = $this->tableManager->find($this, $primaryKey, required: true); + \assert($row instanceof BulkRow); + return $row; + } + + + /** + * @param OrderBy[] $orderBy + * @return BulkRow[] + */ + public function getAll(array $orderBy = [], ?Paginator $paginator = null): array + { + /** @var BulkRow[] $result */ + $result = $this->tableManager->getAll($this, $orderBy, $paginator); + return $result; + } + + + /** + * @param Condition|Condition[] $conditions + * @param array<OrderBy|Expression<mixed>> $orderBy + * @return BulkRow[] + */ + public function findBy(Condition|array $conditions, array $orderBy = [], ?Paginator $paginator = null): array + { + /** @var BulkRow[] $result */ + $result = $this->tableManager->findBy($this, $conditions, $orderBy, $paginator); + return $result; + } + + + /** + * @param Condition|Condition[] $conditions + * @return BulkRow + * @throws RowNotFound + */ + public function getUniqueBy(Condition|array $conditions): BulkRow + { + $row = $this->tableManager->findOneBy($this, $conditions, required: true, unique: true); + \assert($row instanceof BulkRow); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + * @return BulkRow|null + * @throws RowNotFound + */ + public function findUniqueBy(Condition|array $conditions): ?BulkRow + { + $row = $this->tableManager->findOneBy($this, $conditions, required: false, unique: true); + \assert($row instanceof BulkRow || $row === null); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + * @param array<OrderBy|Expression<mixed>> $orderBy + * @return BulkRow + * @throws RowNotFound + */ + public function getFirstBy(Condition|array $conditions, array $orderBy = []): BulkRow + { + $row = $this->tableManager->findOneBy($this, $conditions, $orderBy, required: true, unique: false); + \assert($row instanceof BulkRow); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + * @param array<OrderBy|Expression<mixed>> $orderBy + * @return BulkRow|null + */ + public function findFirstBy(Condition|array $conditions, array $orderBy = []): ?BulkRow + { + $row = $this->tableManager->findOneBy($this, $conditions, $orderBy, required: false, unique: false); + \assert($row instanceof BulkRow || $row === null); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + * @return BulkRow + * @throws RowNotFound + */ + #[\Deprecated('Use getUniqueBy() instead.')] + public function getBy(Condition|array $conditions): BulkRow + { + return $this->getUniqueBy($conditions); + } + + + public function new( + Uuid $id, + int $value, + bool|DefaultOrExistingValue $flagged = \Grifart\Tables\DefaultValue, + ): BulkModifications + { + $modifications = BulkModifications::new(); + $modifications->modifyId($id); + $modifications->modifyValue($value); + if (!$flagged instanceof DefaultOrExistingValue) { + $modifications->modifyFlagged($flagged); + } + return $modifications; + } + + + public function edit( + BulkRow|BulkPrimaryKey $rowOrKey, + Uuid|DefaultOrExistingValue $id = \Grifart\Tables\Unchanged, + int|DefaultOrExistingValue $value = \Grifart\Tables\Unchanged, + bool|DefaultOrExistingValue $flagged = \Grifart\Tables\Unchanged, + ): BulkModifications + { + $primaryKey = $rowOrKey instanceof BulkPrimaryKey ? $rowOrKey : BulkPrimaryKey::fromRow($rowOrKey); + $modifications = BulkModifications::update($primaryKey); + if (!$id instanceof DefaultOrExistingValue) { + $modifications->modifyId($id); + } + if (!$value instanceof DefaultOrExistingValue) { + $modifications->modifyValue($value); + } + if (!$flagged instanceof DefaultOrExistingValue) { + $modifications->modifyFlagged($flagged); + } + return $modifications; + } + + + /** + * @throws RowWithGivenPrimaryKeyAlreadyExists + * @throws GivenSearchCriteriaHaveNotMatchedAnyRows + */ + public function save(BulkModifications $changes): void + { + $this->tableManager->save($this, $changes); + } + + + /** + * @throws RowWithGivenPrimaryKeyAlreadyExists + */ + public function insert(BulkModifications $changes): void + { + $this->tableManager->insert($this, $changes); + } + + + /** + * @throws RowWithGivenPrimaryKeyAlreadyExists + */ + public function insertAndGet(BulkModifications $changes): BulkRow + { + $row = $this->tableManager->insertAndGet($this, $changes); + \assert($row instanceof BulkRow); + return $row; + } + + + /** + * @throws GivenSearchCriteriaHaveNotMatchedAnyRows + */ + public function update(BulkModifications $changes): void + { + $this->tableManager->update($this, $changes); + } + + + /** + * @throws GivenSearchCriteriaHaveNotMatchedAnyRows + */ + public function updateAndGet(BulkModifications $changes): BulkRow + { + $row = $this->tableManager->updateAndGet($this, $changes); + \assert($row instanceof BulkRow); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + */ + public function updateBy(Condition|array $conditions, BulkModifications $changes): void + { + $this->tableManager->updateBy($this, $conditions, $changes); + } + + + public function upsert(BulkModifications $changes): void + { + $this->tableManager->upsert($this, $changes); + } + + + public function upsertAndGet(BulkModifications $changes): BulkRow + { + $row = $this->tableManager->upsertAndGet($this, $changes); + \assert($row instanceof BulkRow); + return $row; + } + + + public function delete(BulkRow|BulkPrimaryKey $rowOrKey): void + { + $primaryKey = $rowOrKey instanceof BulkPrimaryKey ? $rowOrKey : BulkPrimaryKey::fromRow($rowOrKey); + $this->tableManager->delete($this, $primaryKey); + } + + + public function deleteAndGet(BulkRow|BulkPrimaryKey $rowOrKey): BulkRow + { + $primaryKey = $rowOrKey instanceof BulkPrimaryKey ? $rowOrKey : BulkPrimaryKey::fromRow($rowOrKey); + $row = $this->tableManager->deleteAndGet($this, $primaryKey); + \assert($row instanceof BulkRow); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + */ + public function deleteBy(Condition|array $conditions): void + { + $this->tableManager->deleteBy($this, $conditions); + } + + + public function __construct( + private TableManager $tableManager, + private TypeResolver $typeResolver, + ) { + /** @var Column<self, Uuid> $id */ + $id = Column::from($this, self::getDatabaseColumns()['id'], $this->typeResolver); + /** @var Column<self, int> $value */ + $value = Column::from($this, self::getDatabaseColumns()['value'], $this->typeResolver); + /** @var Column<self, bool> $flagged */ + $flagged = Column::from($this, self::getDatabaseColumns()['flagged'], $this->typeResolver); + $this->columns = ['id' => $id, 'value' => $value, 'flagged' => $flagged]; + } + + + /** + * @return Column<self, Uuid> + */ + public function id(): Column + { + return $this->columns['id']; + } + + + /** + * @return Column<self, int> + */ + public function value(): Column + { + return $this->columns['value']; + } + + + /** + * @return Column<self, bool> + */ + public function flagged(): Column + { + return $this->columns['flagged']; + } + + + /** + * @internal + * @return Type<mixed> + */ + public function getTypeOf(string $columnName): Type + { + $column = $this->columns[$columnName] ?? throw ColumnNotFound::of($columnName, \get_class($this)); + /** @var Type<mixed> $type */ + $type = $column->getType(); + return $type; + } +} diff --git a/tests/Fixtures/ConfigPrimaryKey.php b/tests/Fixtures/ConfigPrimaryKey.php index 1200f310109a73c4b305556838112b90938bc8c4..b6067a1160f3dd7304224321a075ee932fccfeef 100644 --- a/tests/Fixtures/ConfigPrimaryKey.php +++ b/tests/Fixtures/ConfigPrimaryKey.php @@ -37,6 +37,15 @@ final class ConfigPrimaryKey implements PrimaryKey } + /** + * @return string[] + */ + public static function getColumnNames(): array + { + return ['id']; + } + + public function getCondition(Table $table): Condition { return Composite::and( diff --git a/tests/Fixtures/ConfigTable.php b/tests/Fixtures/ConfigTable.php index 8ab3b8bcaa6c9aa727d2ed187cd2eeb681dab589..5c5f4a2955c12f8632b78aac9d111f49b83cf78b 100644 --- a/tests/Fixtures/ConfigTable.php +++ b/tests/Fixtures/ConfigTable.php @@ -238,6 +238,17 @@ final class ConfigTable implements Table } + /** + * @throws RowWithGivenPrimaryKeyAlreadyExists + */ + public function insertAndGet(ConfigModifications $changes): ConfigRow + { + $row = $this->tableManager->insertAndGet($this, $changes); + \assert($row instanceof ConfigRow); + return $row; + } + + /** * @throws GivenSearchCriteriaHaveNotMatchedAnyRows */ @@ -247,6 +258,40 @@ final class ConfigTable implements Table } + /** + * @throws GivenSearchCriteriaHaveNotMatchedAnyRows + */ + public function updateAndGet(ConfigModifications $changes): ConfigRow + { + $row = $this->tableManager->updateAndGet($this, $changes); + \assert($row instanceof ConfigRow); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + */ + public function updateBy(Condition|array $conditions, ConfigModifications $changes): void + { + $this->tableManager->updateBy($this, $conditions, $changes); + } + + + public function upsert(ConfigModifications $changes): void + { + $this->tableManager->upsert($this, $changes); + } + + + public function upsertAndGet(ConfigModifications $changes): ConfigRow + { + $row = $this->tableManager->upsertAndGet($this, $changes); + \assert($row instanceof ConfigRow); + return $row; + } + + public function delete(ConfigRow|ConfigPrimaryKey $rowOrKey): void { $primaryKey = $rowOrKey instanceof ConfigPrimaryKey ? $rowOrKey : ConfigPrimaryKey::fromRow($rowOrKey); @@ -254,6 +299,24 @@ final class ConfigTable implements Table } + public function deleteAndGet(ConfigRow|ConfigPrimaryKey $rowOrKey): ConfigRow + { + $primaryKey = $rowOrKey instanceof ConfigPrimaryKey ? $rowOrKey : ConfigPrimaryKey::fromRow($rowOrKey); + $row = $this->tableManager->deleteAndGet($this, $primaryKey); + \assert($row instanceof ConfigRow); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + */ + public function deleteBy(Condition|array $conditions): void + { + $this->tableManager->deleteBy($this, $conditions); + } + + public function __construct( private TableManager $tableManager, private TypeResolver $typeResolver, diff --git a/tests/Fixtures/GeneratedPrimaryKey.php b/tests/Fixtures/GeneratedPrimaryKey.php index d11b63f45c8f328ea51f2e5edc4eba7d0bbb3d80..472ce03af5ec63fac0fd6a0442e27ca8754902f8 100644 --- a/tests/Fixtures/GeneratedPrimaryKey.php +++ b/tests/Fixtures/GeneratedPrimaryKey.php @@ -37,6 +37,15 @@ final class GeneratedPrimaryKey implements PrimaryKey } + /** + * @return string[] + */ + public static function getColumnNames(): array + { + return ['id']; + } + + public function getCondition(Table $table): Condition { return Composite::and( diff --git a/tests/Fixtures/GeneratedTable.php b/tests/Fixtures/GeneratedTable.php index 71a7011bdb90daaf246e93a8e1d1c37bc676431c..f5631b36750ae0be52333980e3050f0df6ec68fb 100644 --- a/tests/Fixtures/GeneratedTable.php +++ b/tests/Fixtures/GeneratedTable.php @@ -228,6 +228,17 @@ final class GeneratedTable implements Table } + /** + * @throws RowWithGivenPrimaryKeyAlreadyExists + */ + public function insertAndGet(GeneratedModifications $changes): GeneratedRow + { + $row = $this->tableManager->insertAndGet($this, $changes); + \assert($row instanceof GeneratedRow); + return $row; + } + + /** * @throws GivenSearchCriteriaHaveNotMatchedAnyRows */ @@ -237,6 +248,40 @@ final class GeneratedTable implements Table } + /** + * @throws GivenSearchCriteriaHaveNotMatchedAnyRows + */ + public function updateAndGet(GeneratedModifications $changes): GeneratedRow + { + $row = $this->tableManager->updateAndGet($this, $changes); + \assert($row instanceof GeneratedRow); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + */ + public function updateBy(Condition|array $conditions, GeneratedModifications $changes): void + { + $this->tableManager->updateBy($this, $conditions, $changes); + } + + + public function upsert(GeneratedModifications $changes): void + { + $this->tableManager->upsert($this, $changes); + } + + + public function upsertAndGet(GeneratedModifications $changes): GeneratedRow + { + $row = $this->tableManager->upsertAndGet($this, $changes); + \assert($row instanceof GeneratedRow); + return $row; + } + + public function delete(GeneratedRow|GeneratedPrimaryKey $rowOrKey): void { $primaryKey = $rowOrKey instanceof GeneratedPrimaryKey ? $rowOrKey : GeneratedPrimaryKey::fromRow($rowOrKey); @@ -244,6 +289,24 @@ final class GeneratedTable implements Table } + public function deleteAndGet(GeneratedRow|GeneratedPrimaryKey $rowOrKey): GeneratedRow + { + $primaryKey = $rowOrKey instanceof GeneratedPrimaryKey ? $rowOrKey : GeneratedPrimaryKey::fromRow($rowOrKey); + $row = $this->tableManager->deleteAndGet($this, $primaryKey); + \assert($row instanceof GeneratedRow); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + */ + public function deleteBy(Condition|array $conditions): void + { + $this->tableManager->deleteBy($this, $conditions); + } + + public function __construct( private TableManager $tableManager, private TypeResolver $typeResolver, diff --git a/tests/Fixtures/PackagePrimaryKey.php b/tests/Fixtures/PackagePrimaryKey.php index 931dec420a653b76f74ca7610dc3047d55db47ea..6430ee5f1cbd53f1edc4235d34aa9916347128fb 100644 --- a/tests/Fixtures/PackagePrimaryKey.php +++ b/tests/Fixtures/PackagePrimaryKey.php @@ -37,6 +37,15 @@ final class PackagePrimaryKey implements PrimaryKey } + /** + * @return string[] + */ + public static function getColumnNames(): array + { + return ['name']; + } + + public function getCondition(Table $table): Condition { return Composite::and( diff --git a/tests/Fixtures/PackagesTable.php b/tests/Fixtures/PackagesTable.php index c60e1ebd157b8d78fd979aeb6da1a7a7731706c9..640f5aa2397cde5ffec11ba61311c6a2a79fe3ea 100644 --- a/tests/Fixtures/PackagesTable.php +++ b/tests/Fixtures/PackagesTable.php @@ -246,6 +246,17 @@ final class PackagesTable implements Table } + /** + * @throws RowWithGivenPrimaryKeyAlreadyExists + */ + public function insertAndGet(PackageModifications $changes): PackageRow + { + $row = $this->tableManager->insertAndGet($this, $changes); + \assert($row instanceof PackageRow); + return $row; + } + + /** * @throws GivenSearchCriteriaHaveNotMatchedAnyRows */ @@ -255,6 +266,40 @@ final class PackagesTable implements Table } + /** + * @throws GivenSearchCriteriaHaveNotMatchedAnyRows + */ + public function updateAndGet(PackageModifications $changes): PackageRow + { + $row = $this->tableManager->updateAndGet($this, $changes); + \assert($row instanceof PackageRow); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + */ + public function updateBy(Condition|array $conditions, PackageModifications $changes): void + { + $this->tableManager->updateBy($this, $conditions, $changes); + } + + + public function upsert(PackageModifications $changes): void + { + $this->tableManager->upsert($this, $changes); + } + + + public function upsertAndGet(PackageModifications $changes): PackageRow + { + $row = $this->tableManager->upsertAndGet($this, $changes); + \assert($row instanceof PackageRow); + return $row; + } + + public function delete(PackageRow|PackagePrimaryKey $rowOrKey): void { $primaryKey = $rowOrKey instanceof PackagePrimaryKey ? $rowOrKey : PackagePrimaryKey::fromRow($rowOrKey); @@ -262,6 +307,24 @@ final class PackagesTable implements Table } + public function deleteAndGet(PackageRow|PackagePrimaryKey $rowOrKey): PackageRow + { + $primaryKey = $rowOrKey instanceof PackagePrimaryKey ? $rowOrKey : PackagePrimaryKey::fromRow($rowOrKey); + $row = $this->tableManager->deleteAndGet($this, $primaryKey); + \assert($row instanceof PackageRow); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + */ + public function deleteBy(Condition|array $conditions): void + { + $this->tableManager->deleteBy($this, $conditions); + } + + public function __construct( private TableManager $tableManager, private TypeResolver $typeResolver, diff --git a/tests/Fixtures/TestFixtures.php b/tests/Fixtures/TestFixtures.php index a0f18aa620c31a3d26fe0508b4a65f506a6e7487..0e946f587e6eb9be19e81cc4c33748c9487b4e7e 100644 --- a/tests/Fixtures/TestFixtures.php +++ b/tests/Fixtures/TestFixtures.php @@ -26,6 +26,7 @@ final class TestFixtures $typeResolver = new TypeResolver($connection); $typeResolver->addResolutionByLocation(new Identifier('public', 'test', 'id'), new UuidType()); $typeResolver->addResolutionByLocation(new Identifier('public', 'test', 'score'), IntType::integer()); + $typeResolver->addResolutionByLocation(new Identifier('public', 'bulk', 'id'), new UuidType()); $typeResolver->addResolutionByLocation(new Identifier('public', 'config', 'id'), new UuidType()); $typeResolver->addResolutionByLocation(new Identifier('public', 'package', 'version'), new TupleVersionType()); $typeResolver->addResolutionByLocation(new Identifier('public', 'package', 'previousVersions'), ArrayType::of(new VersionType())); diff --git a/tests/Fixtures/TestPrimaryKey.php b/tests/Fixtures/TestPrimaryKey.php index 7e6569881ddef5b9d431d62936e4850d78f5dd05..ace89d418e60ba72e0096b89be5bea64043603dc 100644 --- a/tests/Fixtures/TestPrimaryKey.php +++ b/tests/Fixtures/TestPrimaryKey.php @@ -37,6 +37,15 @@ final class TestPrimaryKey implements PrimaryKey } + /** + * @return string[] + */ + public static function getColumnNames(): array + { + return ['id']; + } + + public function getCondition(Table $table): Condition { return Composite::and( diff --git a/tests/Fixtures/TestsTable.php b/tests/Fixtures/TestsTable.php index a5660bc9bb02944620646285ff7f18753410786a..0359c0d94d5e78b933bf4daaa279b950af66f61c 100644 --- a/tests/Fixtures/TestsTable.php +++ b/tests/Fixtures/TestsTable.php @@ -244,6 +244,17 @@ final class TestsTable implements Table } + /** + * @throws RowWithGivenPrimaryKeyAlreadyExists + */ + public function insertAndGet(TestModifications $changes): TestRow + { + $row = $this->tableManager->insertAndGet($this, $changes); + \assert($row instanceof TestRow); + return $row; + } + + /** * @throws GivenSearchCriteriaHaveNotMatchedAnyRows */ @@ -253,6 +264,40 @@ final class TestsTable implements Table } + /** + * @throws GivenSearchCriteriaHaveNotMatchedAnyRows + */ + public function updateAndGet(TestModifications $changes): TestRow + { + $row = $this->tableManager->updateAndGet($this, $changes); + \assert($row instanceof TestRow); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + */ + public function updateBy(Condition|array $conditions, TestModifications $changes): void + { + $this->tableManager->updateBy($this, $conditions, $changes); + } + + + public function upsert(TestModifications $changes): void + { + $this->tableManager->upsert($this, $changes); + } + + + public function upsertAndGet(TestModifications $changes): TestRow + { + $row = $this->tableManager->upsertAndGet($this, $changes); + \assert($row instanceof TestRow); + return $row; + } + + public function delete(TestRow|TestPrimaryKey $rowOrKey): void { $primaryKey = $rowOrKey instanceof TestPrimaryKey ? $rowOrKey : TestPrimaryKey::fromRow($rowOrKey); @@ -260,6 +305,24 @@ final class TestsTable implements Table } + public function deleteAndGet(TestRow|TestPrimaryKey $rowOrKey): TestRow + { + $primaryKey = $rowOrKey instanceof TestPrimaryKey ? $rowOrKey : TestPrimaryKey::fromRow($rowOrKey); + $row = $this->tableManager->deleteAndGet($this, $primaryKey); + \assert($row instanceof TestRow); + return $row; + } + + + /** + * @param Condition|Condition[] $conditions + */ + public function deleteBy(Condition|array $conditions): void + { + $this->tableManager->deleteBy($this, $conditions); + } + + public function __construct( private TableManager $tableManager, private TypeResolver $typeResolver, diff --git a/tests/Scaffolding/ScaffoldingTest.phpt b/tests/Scaffolding/ScaffoldingTest.phpt index c484dcbca5bf23ba1b7c0ebba4b35f3ba2994385..bf13af98d27b8af98b3403ad5090a3888a9c5e0b 100644 --- a/tests/Scaffolding/ScaffoldingTest.phpt +++ b/tests/Scaffolding/ScaffoldingTest.phpt @@ -34,5 +34,5 @@ $results = $fileProcessor->processFile( }, ); -Assert::count(17, $results->getDefinitions()); +Assert::count(21, $results->getDefinitions()); Assert::true($results->isSuccessful()); diff --git a/tests/TableTest.php b/tests/TableTest.php index 791cbeec8118e15e62bebf8b7e00f49033b4b21f..d28a618dac2ee9e7db367b19f8ee6c5b9a1ebe80 100644 --- a/tests/TableTest.php +++ b/tests/TableTest.php @@ -6,6 +6,8 @@ namespace Grifart\Tables\Tests; use Grifart\Tables\Expression; use Grifart\Tables\OrderBy\Nulls; +use Grifart\Tables\RowNotFound; +use Grifart\Tables\RowWithGivenPrimaryKeyAlreadyExists; use Grifart\Tables\Tests\Fixtures\TestFixtures; use Grifart\Tables\Tests\Fixtures\TestPrimaryKey; use Grifart\Tables\Tests\Fixtures\TestsTable; @@ -127,3 +129,25 @@ Assert::same('nada', $updatedZero->getDetails()); $table->delete(TestPrimaryKey::fromRow($updatedZero)); Assert::null($table->find(TestPrimaryKey::fromRow($updatedZero))); + +$newRow = $table->insertAndGet($table->new($id = new Uuid('7ec810dd-4d52-4bb9-ae96-6f558ee4890f'), 7)); +Assert::same(7, $newRow->getScore()); + +$updatedRow = $table->updateAndGet($table->edit($newRow, score: -7)); +Assert::same(-7, $updatedRow->getScore()); + +// upsert + +Assert::throws(fn() => $table->insert($table->new($id, 11)), RowWithGivenPrimaryKeyAlreadyExists::class); + +$table->upsert($table->new($id, 17)); +Assert::same(17, $table->get(TestPrimaryKey::from($id))->getScore()); + +$upsertedRow = $table->upsertAndGet($table->new($id, 11)); +Assert::same(11, $upsertedRow->getScore()); + +// deleteAndGet + +$deleted = $table->deleteAndGet(TestPrimaryKey::fromRow($upsertedRow)); +Assert::same(11, $deleted->getScore()); +Assert::throws(fn() => $table->get(TestPrimaryKey::fromRow($deleted)), RowNotFound::class); diff --git a/tests/initializeDatabase.php b/tests/initializeDatabase.php index 7f388be4f30b7bc835b452ca41176609d2e15772..0b195964b996f7d685dc923700fa8c7b59db127b 100644 --- a/tests/initializeDatabase.php +++ b/tests/initializeDatabase.php @@ -55,3 +55,11 @@ CREATE TABLE IF NOT EXISTS public.generated ( direct INT NOT NULL ) SQL); + +$connection->nativeQuery(<<<SQL +CREATE TABLE IF NOT EXISTS public.bulk ( + id uuid NOT NULL PRIMARY KEY, + value int NOT NULL, + flagged boolean NOT NULL DEFAULT false +) +SQL);