diff --git a/composer.json b/composer.json index 7507b3d..70c90d3 100644 --- a/composer.json +++ b/composer.json @@ -40,5 +40,5 @@ "require-dev": { "phpunit/phpunit": "^11.2" }, - "minimum-stability": "dev" + "minimum-stability": "stable" } diff --git a/example/AuthCode.php b/example/AuthCode.php deleted file mode 100644 index d73af28..0000000 --- a/example/AuthCode.php +++ /dev/null @@ -1,22 +0,0 @@ -code = $code; - } -} diff --git a/example/Hr.php b/example/Hr.php deleted file mode 100644 index ca08371..0000000 --- a/example/Hr.php +++ /dev/null @@ -1,10 +0,0 @@ -nickname = $nickname; - $this->email = $email; - $this->firstName = $firstName; - $this->lastName = $lastName; - $this->password = $password; - $this->active = $active; - $this->createdIn = $createdIn; - } -} diff --git a/maestro.php b/maestro.php deleted file mode 100644 index fdef15e..0000000 --- a/maestro.php +++ /dev/null @@ -1,30 +0,0 @@ -createDatabase($agrofastDB, true)) . "\n"; - -$insert = new Insert(Maestro::SQL_NO_PREDICT, PDOConnection::get()); -$user = new User("nickname'-- drop table", 'John', 'Doe', 'email@example.com', 'password', true, new Timestamp()); -$result = $insert->into($user)->values($user)->returning(['id'])->execute(); -var_dump($result); - -$delete = new Delete(Maestro::SQL_NO_PREDICT, PDOConnection::get()); -$delete->from($user)->where(['id' => $result[0]['id']])->execute(); - diff --git a/src/Core/Synchronizer.php b/src/Core/Synchronizer.php deleted file mode 100644 index 375ddf3..0000000 --- a/src/Core/Synchronizer.php +++ /dev/null @@ -1,159 +0,0 @@ -manager = new Manager(); - $this->pdo = PDOConnection::get(); - } - - public function synchronize(Database $ormDb): void - { - $ormVector = $this->vectorizeORM($ormDb); - $dbVector = $this->vectorizeDatabase($this->pdo); - $differences = $this->compareVectors($ormVector, $dbVector); - $queries = $this->generateSQLQueries($differences); - - echo var_dump($ormVector, $dbVector, $differences, $queries); - return; - - // $this->executeQueries($queries, $this->pdo); - } - - private function vectorizeORM(Database $ormDb): array - { - $vector = []; - - foreach ($ormDb::getSchemas() as $schemaName => $schema) { - foreach ($schema::getTables() as $tableName => $table) { - $columns = $table::tableColumns(); - foreach ($columns as $columnName => $column) { - $vector[$schemaName][$tableName][$columnName] = [ - 'type' => $column['type'], - 'default' => $column['default'], - 'not_null' => $column['not_null'], - 'is_unique' => $column['is_unique'], - ]; - } - } - } - - return $vector; - } - - private function vectorizeDatabase(PDO $pdo): array - { - $vector = []; - - $query = "SELECT c.table_schema, c.table_name, c.column_name, c.data_type, c.column_default, c.is_nullable, tc.constraint_type - FROM information_schema.columns c - LEFT JOIN information_schema.key_column_usage kcu - ON c.table_schema = kcu.table_schema - AND c.table_name = kcu.table_name - AND c.column_name = kcu.column_name - LEFT JOIN information_schema.table_constraints tc - ON kcu.constraint_name = tc.constraint_name - AND kcu.table_schema = tc.table_schema - AND kcu.table_name = tc.table_name - AND tc.constraint_type = 'UNIQUE' - WHERE c.table_schema NOT IN ('information_schema', 'pg_catalog') - ORDER BY c.table_schema, c.table_name, c.ordinal_position;"; - $stmt = $pdo->query($query); - - while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { - $schemaName = $row['table_schema']; - $tableName = $row['table_name']; - $columnName = $row['column_name']; - - $vector[$schemaName][$tableName][$columnName] = [ - 'type' => $row['data_type'], - 'default' => $row['column_default'], - 'not_null' => $row['is_nullable'] === 'NO', - 'is_unique' => $row['constraint_type'] === 'UNIQUE', - ]; - } - - return $vector; - } - - private function compareVectors(array $ormVector, array $dbVector): array - { - $differences = []; - - foreach ($ormVector as $schemaName => $schema) { - foreach ($schema as $tableName => $table) { - if (!isset($dbVector[$schemaName][$tableName])) { - $differences[] = [ - 'action' => 'create_table', - 'table' => $tableName, - ]; - continue; - } - - $dbTable = $dbVector[$schemaName][$tableName]; - foreach ($table as $columnName => $column) { - if (!isset($dbTable[$columnName])) { - $differences[] = [ - 'action' => 'add_column', - 'schema' => $schemaName, - 'table' => $tableName, - 'column' => $columnName, - 'definition' => $column - ]; - } - } - } - } - - return $differences; - } - - private function generateSQLQueries(array $differences): array - { - $queries = []; - - foreach ($differences as $difference) { - switch ($difference['action']) { - case 'create_table': - $queries[] = $this->manager->createTable($difference['table']); - break; - case 'add_column': - $queries[] = $this->generateAddColumnSQL( - $difference['schema'], - $difference['table'], - $difference['column'], - $difference['definition'] - ); - break; - } - } - - return $queries; - } - - private function generateAddColumnSQL(string $schema, string $table, string $column, array $definition): string - { - $type = Utils::getPostgresType(is_array($definition['type']) ? $definition['type'][0] : $definition['type']); - $notNull = $definition['not_null'] ? 'NOT NULL' : 'NULL'; - $default = $definition['default'] ? "DEFAULT {$definition['default']}" : ''; - - return "ALTER TABLE \"$schema\".\"$table\" ADD COLUMN \"$column\" $type $notNull $default;"; - } - - private function executeQueries(array $queries, PDO $pdo): void - { - foreach ($queries as $query) { - $this->manager->executeQuery($pdo, $query); - } - } -} diff --git a/tests/integration/.gitkeep b/tests/integration/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/unit/DeleteTest.php b/tests/unit/DeleteTest.php deleted file mode 100644 index 2bbdaf7..0000000 --- a/tests/unit/DeleteTest.php +++ /dev/null @@ -1,112 +0,0 @@ - 'email@example.com']; - - $delete->from($table::tableName())->where($conditions); - - $expectedSql = "DELETE FROM user WHERE email = :where_email"; - $expectedParams = [':where_email' => "'email@example.com'"]; - - $this->assertEquals($expectedSql, $delete->getSql()); - $this->assertEquals($expectedParams, $delete->getParameters()); - } - - public function testDeleteWithInClause() - { - $delete = new Delete(Maestro::SQL_NO_PREDICT); - $table = User::class; - $conditions = ['id' => [1, 2, 3]]; - - $delete->from($table::tableName())->in($conditions); - - $expectedSql = "DELETE FROM user WHERE id IN(:in_id_0,:in_id_1,:in_id_2)"; - $expectedParams = [':in_id_0' => 1, ':in_id_1' => 2, ':in_id_2' => 3]; - - $this->assertEquals($expectedSql, $delete->getSql()); - $this->assertEquals($expectedParams, $delete->getParameters()); - } - - public function testDeleteWithMultipleConditions() - { - $delete = new Delete(Maestro::SQL_NO_PREDICT); - $table = User::class; - $conditions = ['email' => 'email@example.com', 'active' => false]; - - $delete->from($table::tableName())->where($conditions); - - $expectedSql = "DELETE FROM user WHERE email = :where_email AND active = :where_active"; - $expectedParams = [':where_email' => "'email@example.com'", ':where_active' => 'FALSE']; - - $this->assertEquals($expectedSql, $delete->getSql()); - $this->assertEquals($expectedParams, $delete->getParameters()); - } - - public function testDeleteWithoutConditions() - { - $delete = new Delete(Maestro::SQL_NO_PREDICT); - $table = User::class; - - $delete->from($table::tableName()); - - $expectedSql = "DELETE FROM user"; - $expectedParams = []; - - $this->assertEquals($expectedSql, $delete->getSql()); - $this->assertEquals($expectedParams, $delete->getParameters()); - } - - public function testMultipleInConditions() - { - $delete = new Delete(Maestro::SQL_NO_PREDICT); - $table = User::class; - $conditions = [ - 'id' => [1, 2, 3], - 'group_id' => [10, 20, 30] - ]; - - $delete->from($table::tableName())->in($conditions); - - $expectedSql = "DELETE FROM user WHERE id IN(:in_id_0,:in_id_1,:in_id_2) AND group_id IN(:in_group_id_0,:in_group_id_1,:in_group_id_2)"; - $expectedParams = [ - ':in_id_0' => 1, - ':in_id_1' => 2, - ':in_id_2' => 3, - ':in_group_id_0' => 10, - ':in_group_id_1' => 20, - ':in_group_id_2' => 30 - ]; - - $this->assertEquals($expectedSql, $delete->getSql()); - $this->assertEquals($expectedParams, $delete->getParameters()); - } - - public function testDeleteWithTimestampCondition() - { - $delete = new Delete(Maestro::SQL_NO_PREDICT); - $table = User::class; - $date = new Timestamp(); - $conditions = ['created_at' => $date]; - - $delete->from($table::tableName())->where($conditions); - - $expectedSql = "DELETE FROM user WHERE created_at = :where_created_at"; - $expectedParams = [':where_created_at' => "'{$date}'"]; - - $this->assertEquals($expectedSql, $delete->getSql()); - $this->assertEquals($expectedParams, $delete->getParameters()); - } -} diff --git a/tests/unit/InsertTest.php b/tests/unit/InsertTest.php deleted file mode 100644 index 8fbc1e1..0000000 --- a/tests/unit/InsertTest.php +++ /dev/null @@ -1,105 +0,0 @@ - 'nickname', 'email' => 'email@example.com', 'password' => 'password']; - - $insert->into($table::tableName())->values($data); - - $expectedSql = "INSERT INTO user (nickname, email, password) VALUES (:nickname, :email, :password)"; - $expectedParams = [':nickname' => "'nickname'", ':email' => "'email@example.com'", ':password' => "'password'"]; - - $this->assertEquals($expectedSql, $insert->getSql()); - $this->assertEquals($expectedParams, $insert->getParameters()); - } - - public function testInsertWithMissingFields() - { - $insert = new Insert(Maestro::SQL_NO_PREDICT); - $table = User::class; - $data = ['nickname' => 'nickname', 'email' => 'email@example.com']; - - $insert->into($table::tableName())->values($data); - - $expectedSql = "INSERT INTO user (nickname, email) VALUES (:nickname, :email)"; - $expectedParams = [':nickname' => "'nickname'", ':email' => "'email@example.com'"]; - - $this->assertEquals($expectedSql, $insert->getSql()); - $this->assertEquals($expectedParams, $insert->getParameters()); - } - - public function testInsertWithAllFields() - { - $insert = new Insert(Maestro::SQL_NO_PREDICT); - $table = User::class; - $data = [ - 'nickname' => 'nickname', - 'email' => 'email@example.com', - 'password' => 'password', - 'active' => true, - 'created_in' => new Expression(Expression::CURRENT_TIMESTAMP), - 'updated_in' => new Timestamp('2022-01-01 00:00:00'), - 'inactivated_in' => null - ]; - - $insert->into($table::tableName())->values($data); - - $expectedSql = "INSERT INTO user (nickname, email, password, active, created_in, updated_in, inactivated_in) VALUES (:nickname, :email, :password, :active, :created_in, :updated_in, :inactivated_in)"; - $expectedParams = [ - ':nickname' => "'nickname'", - ':email' => "'email@example.com'", - ':password' => "'password'", - ':active' => 'TRUE', - ':created_in' => 'CURRENT_TIMESTAMP', - ':updated_in' => "'2022-01-01 00:00:00'", - ':inactivated_in' => 'NULL' - ]; - - $this->assertEquals($expectedSql, $insert->getSql()); - $this->assertEquals($expectedParams, $insert->getParameters()); - } - - public function testInsertWithTimestamp() - { - $insert = new Insert(Maestro::SQL_NO_PREDICT); - $table = User::class; - $date = new Timestamp(); - $data = ['nickname' => 'nickname', 'email' => 'email@example.com', 'created_at' => $date]; - - $insert->into($table::tableName())->values($data); - - $expectedSql = "INSERT INTO user (nickname, email, created_at) VALUES (:nickname, :email, :created_at)"; - $expectedParams = [':nickname' => "'nickname'", ':email' => "'email@example.com'", ':created_at' => "'{$date}'"]; - - $this->assertEquals($expectedSql, $insert->getSql()); - $this->assertEquals($expectedParams, $insert->getParameters()); - } - - public function testInsertWithNullValue() - { - $insert = new Insert(Maestro::SQL_NO_PREDICT); - $table = User::class; - $data = ['nickname' => 'nickname', 'email' => null, 'password' => 'password']; - - $insert->into($table::tableName())->values($data); - - $expectedSql = "INSERT INTO user (nickname, email, password) VALUES (:nickname, :email, :password)"; - $expectedParams = [':nickname' => "'nickname'", ':email' => 'NULL', ':password' => "'password'"]; - - $this->assertEquals($expectedSql, $insert->getSql()); - $this->assertEquals($expectedParams, $insert->getParameters()); - } -} diff --git a/tests/unit/ManagerTest.php b/tests/unit/ManagerTest.php deleted file mode 100644 index 616b649..0000000 --- a/tests/unit/ManagerTest.php +++ /dev/null @@ -1,31 +0,0 @@ -pdoMock = $this->createMock(\PDO::class); - PDOConnection::get(pdoMock: $this->pdoMock); - $this->manager = new Manager(); - } - - public function testCreateTable() - { - $tableClass = Hr::getTables()['user']; - $createTableSql = $this->manager->createTable($tableClass); - - $expectedSql = 'CREATE TABLE IF NOT EXISTS "hr"."user" ( "id" SERIAL NOT NULL PRIMARY KEY UNIQUE, "uuid" UUID NOT NULL DEFAULT gen_random_uuid() UNIQUE, "first_name" TEXT NOT NULL, "last_name" TEXT NOT NULL, "nickname" TEXT NOT NULL UNIQUE, "email" TEXT NOT NULL UNIQUE, "password" TEXT NOT NULL, "active" BOOLEAN NOT NULL DEFAULT TRUE, "created_in" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_in" TIMESTAMP NULL, "inactivated_in" TIMESTAMP NULL );'; - - $this->assertEquals(trim(preg_replace('/\s+/', ' ', $expectedSql)), trim(preg_replace('/\s+/', ' ', $createTableSql))); - } -} diff --git a/tests/unit/SelectTest.php b/tests/unit/SelectTest.php deleted file mode 100644 index 8b86bea..0000000 --- a/tests/unit/SelectTest.php +++ /dev/null @@ -1,485 +0,0 @@ -from(['user' => 'user'], ['nickname', 'email'])->where(['active' => true]); - - $expectedSql = "SELECT user.nickname, user.email FROM user WHERE active = :where_active"; - $expectedParams = [':where_active' => true]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithInClause() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'email'])->in(['id' => [1, 2, 3]]); - - $expectedSql = "SELECT user.nickname, user.email FROM user WHERE id IN(:in_id_0,:in_id_1,:in_id_2)"; - $expectedParams = [':in_id_0' => 1, ':in_id_1' => 2, ':in_id_2' => 3]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithMultipleConditions() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'email'])->where(['active' => true, 'verified' => true]); - - $expectedSql = "SELECT user.nickname, user.email FROM user WHERE active = :where_active AND verified = :where_verified"; - $expectedParams = [':where_active' => true, ':where_verified' => true]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testMultipleInConditions() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $table = User::class; - $columns = ['nickname', 'email']; - $conditions = [ - 'id' => [1, 2, 3], - 'group_id' => [10, 20, 30] - ]; - - $select->from([$table::tableName() => 'user'], $columns)->in($conditions); - - $expectedSql = "SELECT user.nickname, user.email FROM user WHERE id IN(:in_id_0,:in_id_1,:in_id_2) AND group_id IN(:in_group_id_0,:in_group_id_1,:in_group_id_2)"; - $expectedParams = [ - ':in_id_0' => 1, - ':in_id_1' => 2, - ':in_id_2' => 3, - ':in_group_id_0' => 10, - ':in_group_id_1' => 20, - ':in_group_id_2' => 30 - ]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithoutConditions() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'email']); - - $expectedSql = "SELECT user.nickname, user.email FROM user"; - $expectedParams = []; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithFunctions() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $table = User::class; - $columns = ['nickname', 'email', 'count' => new Expression('COUNT(*)')]; - $conditions = ['active' => true]; - - $select->from([$table::tableName() => 'user'], $columns)->where($conditions); - - $expectedSql = "SELECT user.nickname, user.email, COUNT(*) AS count FROM user WHERE active = :where_active"; - $expectedParams = [':where_active' => true]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithJoin() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $table = User::class; - $columns = ['nickname', 'email']; - $conditions = ['user.active' => true]; - - $select->from(['user' => $table::tableName()], $columns) - ->join(['profile' => 'profile'], 'user.id = profile.user_id', ['bio']) - ->where($conditions); - - $expectedSql = "SELECT user.nickname, user.email, profile.bio FROM user AS user INNER JOIN profile AS profile ON user.id = profile.user_id WHERE user.active = :where_user_active"; - $expectedParams = [':where_user_active' => 'TRUE']; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithLeftJoin() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $table = User::class; - $columns = ['nickname', 'email']; - $conditions = ['user.active' => true]; - - $select->from(['user' => $table::tableName()], $columns) - ->join(['profile' => 'profile'], 'user.id = profile.user_id', ['bio'], 'LEFT') - ->where($conditions); - - $expectedSql = "SELECT user.nickname, user.email, profile.bio FROM user AS user LEFT JOIN profile AS profile ON user.id = profile.user_id WHERE user.active = :where_user_active"; - $expectedParams = [':where_user_active' => true]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithMultipleJoins() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $table = User::class; - $columns = ['nickname', 'email']; - $conditions = ['user.active' => true]; - - $select->from(['user' => $table::tableName()], $columns) - ->join(['profile' => 'profile'], 'user.id = profile.user_id', ['bio']) - ->join(['account' => 'account'], 'user.id = account.user_id', ['balance']) - ->where($conditions); - - $expectedSql = "SELECT user.nickname, user.email, profile.bio, account.balance FROM user AS user INNER JOIN profile AS profile ON user.id = profile.user_id INNER JOIN account AS account ON user.id = account.user_id WHERE user.active = :where_user_active"; - $expectedParams = [':where_user_active' => true]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithOrderBy() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $table = User::class; - $columns = ['nickname', 'email']; - $conditions = ['active' => true]; - - $select->from([$table::tableName() => 'user'], $columns)->where($conditions)->order('nickname', 'ASC'); - - $expectedSql = "SELECT user.nickname, user.email FROM user WHERE active = :where_active ORDER BY nickname ASC"; - $expectedParams = [':where_active' => true]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithGroupByAndHaving() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'count' => new Expression('COUNT(*)')]) - ->group(['nickname']) - ->having(['count' => 1]); - - $expectedSql = "SELECT user.nickname, COUNT(*) AS count FROM user GROUP BY nickname HAVING count = :having_count"; - $expectedParams = [':having_count' => 1]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithOffsetAndLimit() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'email']) - ->limit(10) - ->offset(5); - - $expectedSql = "SELECT user.nickname, user.email FROM user LIMIT 10 OFFSET 5"; - $expectedParams = []; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testComplexSelectQuery() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'email']) - ->join(['profile' => 'profile'], 'user.id = profile.user_id', ['bio']) - ->where(['user.active' => true]) - ->group(['user.nickname']) - ->having([(string) new Expression('COUNT(profile.id)') => 1]) - ->order('user.nickname', 'ASC') - ->limit(10) - ->offset(5); - - $expectedSql = "SELECT user.nickname, user.email, profile.bio FROM user AS user INNER JOIN profile AS profile ON user.id = profile.user_id WHERE user.active = :where_user_active GROUP BY user.nickname HAVING COUNT(profile.id) = :having_count_profile_id ORDER BY user.nickname ASC LIMIT 10 OFFSET 5"; - $expectedParams = [ - ':where_user_active' => true, - ':having_count_profile_id' => 1 - ]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithDistinct() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->distinct()->from(['user' => 'user'], ['nickname', 'email']); - - $expectedSql = "SELECT DISTINCT user.nickname, user.email FROM user"; - $expectedParams = []; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithRightJoin() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $table = User::class; - $columns = ['nickname', 'email']; - $conditions = ['user.active' => true]; - - $select->from(['user' => $table::tableName()], $columns) - ->join(['profile' => 'profile'], 'user.id = profile.user_id', ['bio'], 'RIGHT') - ->where($conditions); - - $expectedSql = "SELECT user.nickname, user.email, profile.bio FROM user AS user RIGHT JOIN profile AS profile ON user.id = profile.user_id WHERE user.active = :where_user_active"; - $expectedParams = [':where_user_active' => true]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithGroupBy() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'email']) - ->group(['nickname']); - - $expectedSql = "SELECT user.nickname, user.email FROM user GROUP BY nickname"; - $expectedParams = []; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithMultipleHavingConditions() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'count' => new Expression('COUNT(*)')]) - ->group(['nickname']) - ->having(['count' => 1, 'nickname' => 'test']); - - $expectedSql = "SELECT user.nickname, COUNT(*) AS count FROM user GROUP BY nickname HAVING count = :having_count AND nickname = :having_nickname"; - $expectedParams = [':having_count' => 1, ':having_nickname' => "'test'"]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithOrderByDesc() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $table = User::class; - $columns = ['nickname', 'email']; - $conditions = ['active' => true]; - - $select->from([$table::tableName() => 'user'], $columns)->where($conditions)->order('nickname', 'DESC'); - - $expectedSql = "SELECT user.nickname, user.email FROM user WHERE active = :where_active ORDER BY nickname DESC"; - $expectedParams = [':where_active' => true]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithWhereDifferentDataTypes() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'email']) - ->where(['active' => true, 'age' => 30, 'name' => 'John']); - - $expectedSql = "SELECT user.nickname, user.email FROM user WHERE active = :where_active AND age = :where_age AND name = :where_name"; - $expectedParams = [':where_active' => 'TRUE', ':where_age' => 30, ':where_name' => "'John'"]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithInClauseEmptyArray() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'email'])->in(['id' => []]); - - $expectedSql = "SELECT user.nickname, user.email FROM user WHERE id IN()"; - $expectedParams = []; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithOrderByInvalidDirection() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $table = User::class; - $columns = ['nickname', 'email']; - $conditions = ['active' => true]; - - $select->from([$table::tableName() => 'user'], $columns)->where($conditions)->order('nickname', 'INVALID'); - - $expectedSql = "SELECT user.nickname, user.email FROM user WHERE active = :where_active ORDER BY nickname INVALID"; - $expectedParams = [':where_active' => true]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithInvalidLimit() - { - $this->expectException(InvalidArgumentException::class); - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'email']) - ->limit('invalid'); - } - - public function testSelectWithComplexQuery() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'email']) - ->join(['profile' => 'profile'], 'user.id = profile.user_id', ['bio']) - ->where(['user.active' => true]) - ->group(['user.nickname']) - ->having([(string)new Expression('COUNT(profile.id)') => 1]) - ->order('user.nickname', 'DESC') - ->limit(10) - ->offset(5); - - $expectedSql = "SELECT user.nickname, user.email, profile.bio FROM user AS user INNER JOIN profile AS profile ON user.id = profile.user_id WHERE user.active = :where_user_active GROUP BY user.nickname HAVING COUNT(profile.id) = :having_count_profile_id ORDER BY user.nickname DESC LIMIT 10 OFFSET 5"; - $expectedParams = [':where_user_active' => true, ':having_count_profile_id' => 1]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithSqlInjectionPrevention() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'email']) - ->where(['user.nickname' => "'; DROP TABLE users; --"]); - - $expectedSql = "SELECT user.nickname, user.email FROM user WHERE user.nickname = :where_user_nickname"; - $expectedParams = [':where_user_nickname' => "'''; DROP TABLE users; --'"]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithPagination() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'email']) - ->limit(10) - ->offset(20); - - $expectedSql = "SELECT user.nickname, user.email FROM user LIMIT 10 OFFSET 20"; - $expectedParams = []; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithAggregateFunctions() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['user' => 'user'], ['nickname', 'total' => new Expression('SUM(amount)')]) - ->group(['user.nickname']); - - $expectedSql = "SELECT user.nickname, SUM(amount) AS total FROM user GROUP BY user.nickname"; - $expectedParams = []; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithAliases() - { - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['u' => 'user'], ['nickname', 'email']) - ->join(['p' => 'profile'], 'u.id = p.user_id', ['bio']) - ->where(['u.active' => true]); - - $expectedSql = "SELECT u.nickname, u.email, p.bio FROM user AS u INNER JOIN profile AS p ON u.id = p.user_id WHERE u.active = :where_u_active"; - $expectedParams = [':where_u_active' => true]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithSubselect() - { - $subselect = new Select(Maestro::SQL_NO_PREDICT); - $subselect->from(['p' => 'profile'], ['user_id']) - ->where(['p.active' => true]); - - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['u' => 'user'], ['nickname', 'email']) - ->where(['u.id' => $subselect]); - - $expectedSql = "SELECT u.nickname, u.email FROM user WHERE u.id = (SELECT p.user_id FROM profile WHERE p.active = TRUE)"; - - $this->assertEquals($expectedSql, (string)$select); - } - - public function testSelectWithSubselectInJoin() - { - $subselect = new Select(Maestro::SQL_NO_PREDICT); - $subselect->from(['p' => 'profile'], ['user_id']) - ->where(['p.active' => true]); - - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['u' => 'user'], ['nickname', 'email']) - ->join(['sub' => $subselect], 'u.id = sub.user_id', ['user_id']) - ->where(['u.active' => true]); - - $expectedSql = "SELECT u.nickname, u.email, sub.user_id FROM user AS u INNER JOIN (SELECT p.user_id FROM profile WHERE p.active = TRUE) AS sub ON u.id = sub.user_id WHERE u.active = TRUE"; - $expectedParams = [':where_u_active' => 'TRUE']; - - $this->assertEquals($expectedSql, (string)$select); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithSubselectInColumns() - { - $subselect = new Select(Maestro::SQL_NO_PREDICT); - $subselect->from(['p' => 'profile'], [new Expression('COUNT(*)')]) - ->where(['p.user_id' => new Expression('u.id')]); - - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['u' => 'user'], ['nickname', 'email', 'profile_count' => $subselect]) - ->where(['u.active' => true]); - - $expectedSql = "SELECT u.nickname, u.email, (SELECT COUNT(*) FROM profile WHERE p.user_id = u.id) AS profile_count FROM user WHERE u.active = :where_u_active"; - $expectedParams = [':where_u_active' => true]; - - $this->assertEquals($expectedSql, $select->getSql()); - $this->assertEquals($expectedParams, $select->getParameters()); - } - - public function testSelectWithSubselectInHaving() - { - $subselect = new Select(Maestro::SQL_NO_PREDICT); - $subselect->from(['p' => 'profile'], [new Expression('COUNT(*)')]) - ->where(['p.user_id' => new Expression('u.id')]); - - $select = new Select(Maestro::SQL_NO_PREDICT); - $select->from(['u' => 'user'], ['nickname', 'email']) - ->group(['u.nickname']) - ->having(['profile_count' => $subselect]); - - $expectedSql = "SELECT u.nickname, u.email FROM user GROUP BY u.nickname HAVING profile_count = (SELECT COUNT(*) FROM profile WHERE p.user_id = u.id)"; - - $this->assertEquals($expectedSql, (string)$select); - } -} diff --git a/tests/unit/UpdateTest.php b/tests/unit/UpdateTest.php deleted file mode 100644 index ab58d40..0000000 --- a/tests/unit/UpdateTest.php +++ /dev/null @@ -1,131 +0,0 @@ - 'updated_nickname']; - $conditions = ['email' => 'email@example.com']; - - $update->table($table::tableName())->set('nickname', 'updated_nickname')->where($conditions); - - $expectedSql = "UPDATE {$table::tableName()} SET nickname = :nickname WHERE email = :where_email"; - $expectedParams = [':nickname' => 'updated_nickname', ':where_email' => "'email@example.com'"]; - - $this->assertEquals($expectedSql, $update->getSql()); - $this->assertEquals($expectedParams, $update->getParameters()); - } - - public function testUpdateWithInClause() - { - $update = new Update(Maestro::SQL_NO_PREDICT); - $table = User::class; - $data = ['nickname' => 'updated_nickname']; - $conditions = ['id' => [1, 2, 3]]; - - $update->table($table::tableName())->set('nickname', 'updated_nickname')->in($conditions); - - $expectedSql = "UPDATE {$table::tableName()} SET nickname = :nickname WHERE id IN(:in_id_0,:in_id_1,:in_id_2)"; - $expectedParams = [':nickname' => 'updated_nickname', ':in_id_0' => 1, ':in_id_1' => 2, ':in_id_2' => 3]; - - $this->assertEquals($expectedSql, $update->getSql()); - $this->assertEquals($expectedParams, $update->getParameters()); - } - - public function testUpdateWithMultipleConditions() - { - $update = new Update(Maestro::SQL_NO_PREDICT); - $table = User::class; - $data = ['nickname' => 'updated_nickname']; - $conditions = ['email' => 'email@example.com', 'active' => true]; - - $update->table($table::tableName())->set('nickname', 'updated_nickname')->where($conditions); - - $expectedSql = "UPDATE {$table::tableName()} SET nickname = :nickname WHERE email = :where_email AND active = :where_active"; - $expectedParams = [':nickname' => 'updated_nickname', ':where_email' => "'email@example.com'", ':where_active' => true]; - - $this->assertEquals($expectedSql, $update->getSql()); - $this->assertEquals($expectedParams, $update->getParameters()); - } - - public function testMultipleInConditions() - { - $update = new Update(Maestro::SQL_NO_PREDICT); - $table = User::class; - $data = ['nickname' => 'updated_nickname']; - $conditions = [ - 'id' => [1, 2, 3], - 'group_id' => [10, 20, 30] - ]; - - $update->table($table::tableName())->set('nickname', 'updated_nickname')->in($conditions); - - $expectedSql = "UPDATE {$table::tableName()} SET nickname = :nickname WHERE id IN(:in_id_0,:in_id_1,:in_id_2) AND group_id IN(:in_group_id_0,:in_group_id_1,:in_group_id_2)"; - $expectedParams = [ - ':nickname' => 'updated_nickname', - ':in_id_0' => 1, ':in_id_1' => 2, ':in_id_2' => 3, - ':in_group_id_0' => 10, ':in_group_id_1' => 20, ':in_group_id_2' => 30 - ]; - - $this->assertEquals($expectedSql, $update->getSql()); - $this->assertEquals($expectedParams, $update->getParameters()); - } - - public function testUpdateWithoutConditions() - { - $update = new Update(Maestro::SQL_NO_PREDICT); - $table = User::class; - $data = ['nickname' => 'updated_nickname']; - - $update->table($table::tableName())->set('nickname', 'updated_nickname'); - - $expectedSql = "UPDATE {$table::tableName()} SET nickname = :nickname"; - $expectedParams = [':nickname' => 'updated_nickname']; - - $this->assertEquals($expectedSql, $update->getSql()); - $this->assertEquals($expectedParams, $update->getParameters()); - } - - public function testUpdateWithTimestamp() - { - $update = new Update(Maestro::SQL_NO_PREDICT); - $table = User::class; - $date = new Timestamp(); - $data = ['updated_at' => $date]; - $conditions = ['email' => 'email@example.com']; - - $update->table($table::tableName())->set('updated_at', $date)->where($conditions); - - $expectedSql = "UPDATE {$table::tableName()} SET updated_at = :updated_at WHERE email = :where_email"; - $expectedParams = [':updated_at' => $date, ':where_email' => "'email@example.com'"]; - - $this->assertEquals($expectedSql, $update->getSql()); - $this->assertEquals($expectedParams, $update->getParameters()); - } - - public function testUpdateMultipleSetClauses() - { - $update = new Update(Maestro::SQL_NO_PREDICT); - $table = User::class; - $data = ['nickname' => 'updated_nickname', 'active' => false]; - $conditions = ['email' => 'email@example.com']; - - $update->table($table::tableName())->set('nickname', 'updated_nickname')->set('active', false)->where($conditions); - - $expectedSql = "UPDATE {$table::tableName()} SET nickname = :nickname, active = :active WHERE email = :where_email"; - $expectedParams = [':nickname' => 'updated_nickname', ':active' => false, ':where_email' => '\'email@example.com\'']; - - $this->assertEquals($expectedSql, $update->getSql()); - $this->assertEquals($expectedParams, $update->getParameters()); - } -}