From 7b0e74b7b9b25586638d08556e08942b0d6bb599 Mon Sep 17 00:00:00 2001 From: Jakub Kulhan Date: Wed, 29 May 2024 17:11:07 +0200 Subject: [PATCH] persistence insert database test --- data-access-kit/src/Persistence.php | 76 +++++++++++++++++------- data-access-kit/test/PersistenceTest.php | 62 +++++++++++++++++-- 2 files changed, 111 insertions(+), 27 deletions(-) diff --git a/data-access-kit/src/Persistence.php b/data-access-kit/src/Persistence.php index ca33f76..2f5d0b3 100644 --- a/data-access-kit/src/Persistence.php +++ b/data-access-kit/src/Persistence.php @@ -2,7 +2,11 @@ namespace DataAccessKit; +use DataAccessKit\Attribute\Column; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Platforms\MariaDBPlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use LogicException; use function array_merge; use function count; @@ -77,27 +81,41 @@ private function insertUpsertAll(array $objects, ?array $upsertColumns = null): $table = $this->registry->get($objects[0], true); $platform = $this->connection->getDatabasePlatform(); - $columns = []; + $columnNames = []; $rows = []; $values = []; $update = []; - $primaryKey = null; + $generatedColumnNames = []; + /** @var Column[] $generatedColumns */ + $generatedColumns = []; + /** @var Column|null $primaryKeyColumn */ + $primaryKeyColumn = null; + $supportsReturning = match (true) { + $platform instanceof MariaDBPlatform => true, + $platform instanceof PostgreSQLPlatform => true, + $platform instanceof SQLitePlatform => true, + default => false, + }; foreach ($table->columns as $column) { if ($column->generated) { if ($column->primary) { - if ($primaryKey !== null) { + if ($primaryKeyColumn !== null) { throw new LogicException("Multiple generated primary columns."); } - $primaryKey = $column; + $primaryKeyColumn = $column; } - continue; - } - $columns[] = $platform->quoteSingleIdentifier($column->name); + if ($supportsReturning) { + $generatedColumnNames[] = $platform->quoteSingleIdentifier($column->name); + $generatedColumns[] = $column; + } + } else { + $columnNames[] = $platform->quoteSingleIdentifier($column->name); - if ($upsertColumns === null || in_array($column->name, $upsertColumns, true)) { - $update[] = $platform->quoteSingleIdentifier($column->name) . " = VALUES(" . $platform->quoteSingleIdentifier($column->name) . ")"; + if ($upsertColumns === null || in_array($column->name, $upsertColumns, true)) { + $update[] = $platform->quoteSingleIdentifier($column->name) . " = VALUES(" . $platform->quoteSingleIdentifier($column->name) . ")"; + } } } @@ -123,22 +141,34 @@ private function insertUpsertAll(array $objects, ?array $upsertColumns = null): $rows[] = "(" . implode(", ", $row) . ")"; } - $this->connection->executeStatement( - sprintf( - "INSERT INTO %s (%s) VALUES (%s)%s", - $platform->quoteSingleIdentifier($table->name), - implode(", ", $columns), - implode(", ", $rows), - match (count($update)) { - 0 => "", - default => sprintf(" ON DUPLICATE KEY UPDATE %s", implode(", ", $update)), - }, - ), - $values, + $sql = sprintf( + "INSERT INTO %s (%s) VALUES %s%s%s", + $platform->quoteSingleIdentifier($table->name), + implode(", ", $columnNames), + implode(", ", $rows), + match (count($update)) { + 0 => "", + default => sprintf(" ON DUPLICATE KEY UPDATE %s", implode(", ", $update)), + }, + match ($supportsReturning) { + true => sprintf(" RETURNING %s", implode(", ", $generatedColumnNames)), + default => "", + }, ); - if (count($objects) === 1 && $primaryKey !== null) { - $primaryKey->reflection->setValue($objects[0], $this->connection->lastInsertId()); + $result = $this->connection->executeQuery($sql, $values); + if ($supportsReturning && count($generatedColumns) > 0) { + foreach ($result->iterateAssociative() as $index => $row) { + $object = $objects[$index]; + foreach ($generatedColumns as $column) { + $column->reflection->setValue( + $object, + $this->valueConverter->databaseToObject($table, $column, $row[$column->name]), + ); + } + } + } else if (count($objects) === 1 && $primaryKeyColumn !== null) { + $primaryKeyColumn->reflection->setValue($objects[0], $this->connection->lastInsertId()); } } diff --git a/data-access-kit/test/PersistenceTest.php b/data-access-kit/test/PersistenceTest.php index 4e837b2..d4ce710 100644 --- a/data-access-kit/test/PersistenceTest.php +++ b/data-access-kit/test/PersistenceTest.php @@ -5,12 +5,19 @@ use DataAccessKit\Fixture\User; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; +use Doctrine\DBAL\Platforms\MySQLPlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Tools\DsnParser; +use LogicException; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; +use ReflectionProperty; +use function get_class; use function getenv; use function iterator_to_array; -use function var_dump; +use function sprintf; #[Group("database")] class PersistenceTest extends TestCase @@ -33,9 +40,20 @@ public function setUp(): void private function setUpUsersTable(): void { $this->connection->executeStatement("DROP TABLE IF EXISTS users"); - $this->connection->executeStatement("CREATE TABLE users (user_id INT PRIMARY KEY, first_name VARCHAR(255))"); - $this->connection->executeStatement("INSERT INTO users (user_id, first_name) VALUES (1, 'Alice')"); - $this->connection->executeStatement("INSERT INTO users (user_id, first_name) VALUES (2, 'Bob')"); + + $platform = $this->connection->getDatabasePlatform(); + if ($platform instanceof AbstractMySQLPlatform) { + $this->connection->executeStatement("CREATE TABLE users (user_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, first_name VARCHAR(255))"); + } else if ($platform instanceof PostgreSQLPlatform) { + $this->connection->executeStatement("CREATE TABLE users (user_id SERIAL PRIMARY KEY, first_name VARCHAR(255))"); + } else if ($platform instanceof SQLitePlatform) { + $this->connection->executeStatement("CREATE TABLE users (user_id INTEGER PRIMARY KEY AUTOINCREMENT, first_name TEXT)"); + } else { + throw new LogicException(sprintf("Unsupported database platform [%s].", get_class($platform))); + } + + $this->connection->executeStatement("INSERT INTO users (first_name) VALUES ('Alice')"); + $this->connection->executeStatement("INSERT INTO users (first_name) VALUES ('Bob')"); } public function testSelect(): void @@ -64,4 +82,40 @@ public function testExecute(): void $this->assertEquals(1, $count); } + public function testInsert(): void + { + $this->setUpUsersTable(); + $user = new User(); + $user->firstName = "Charlie"; + $this->persistence->insert($user); + $this->assertEquals(3, $user->id); + + $users = iterator_to_array($this->persistence->select(User::class, "SELECT user_id, first_name FROM users WHERE user_id = ?", [$user->id])); + $this->assertCount(1, $users); + $this->assertEquals($user->id, $users[0]->id); + $this->assertEquals($user->firstName, $users[0]->firstName); + } + + public function testInsertAll(): void + { + $this->setUpUsersTable(); + + $user1 = new User(); + $user1->firstName = "Charlie"; + + $user2 = new User(); + $user2->firstName = "David"; + + $this->persistence->insertAll([$user1, $user2]); + + if ($this->connection->getDatabasePlatform() instanceof MySQLPlatform) { + $rp = new ReflectionProperty(User::class, "id"); + $this->assertFalse($rp->isInitialized($user1)); + $this->assertFalse($rp->isInitialized($user2)); + } else { + $this->assertEquals(3, $user1->id); + $this->assertEquals(4, $user2->id); + } + } + }