Skip to content

Commit

Permalink
Added class CreateTableBlueprint to create statements for generatin…
Browse files Browse the repository at this point in the history
…g tables in MySQL/MariaDB and SQLite

Fixed #61
  • Loading branch information
MasterZydra committed Apr 15, 2024
1 parent e4e7b80 commit 9bf746b
Show file tree
Hide file tree
Showing 4 changed files with 309 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Types of changes: `Added`, `Changed`, `Deprecate`, `Removed`, `Fixed`, `Secruity

## [Unreleased]

### Added
- Added class `CreateTableBlueprint` to create statements for generating tables in MySQL/MariaDB and SQLite

## v2.5.0 - 17.03.2024 - Added statistic for amount and price development

### Added
Expand Down
8 changes: 8 additions & 0 deletions framework/Database/BlueprintInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Framework\Database;

interface BlueprintInterface
{
public function build(): array;
}
111 changes: 111 additions & 0 deletions framework/Database/CreateTableBlueprint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

namespace Framework\Database;

use Framework\Config\Config;
use Framework\Facades\Convert;
use RuntimeException;

class CreateTableBlueprint implements BlueprintInterface
{
private array $sql = [];
private array $afterSql = [];

public function __construct(
private string $table
){
}

public function id(): void
{
switch (Config::env('DB_CONNECTION')) {
case 'mysql':
$this->sql[] = 'id INT auto_increment';
$this->sql[] = 'PRIMARY KEY (id)';
break;

case 'sqlite':
$this->sql[] = 'id INTEGER PRIMARY KEY';
break;

default:
throw new RuntimeException('The database connection "' . Config::env('DB_CONNECTION') . '" is not supported');
}
}

public function bool(string $column, bool $nullable = false, bool $default = false): void
{
switch (Config::env('DB_CONNECTION')) {
case 'mysql':
case 'sqlite':
$this->sql[] = $column . ' TINYINT(1) ' . ($nullable ? '' : 'NOT ') . 'NULL DEFAULT ' . Convert::boolToInt($default);
break;

default:
throw new RuntimeException('The database connection "' . Config::env('DB_CONNECTION') . '" is not supported');
}
}

public function int(string $column, bool $nullable = false): void
{
switch (Config::env('DB_CONNECTION')) {
case 'mysql':
$this->sql[] = $column . ' INT ' . ($nullable ? '' : 'NOT ') . 'NULL';
break;

case 'sqlite':
$this->sql[] = $column . ' INTEGER ' . ($nullable ? '' : 'NOT ') . 'NULL';
break;

default:
throw new RuntimeException('The database connection "' . Config::env('DB_CONNECTION') . '" is not supported');
}
}

public function string(string $column, string $length, bool $nullable = false): void
{
switch (Config::env('DB_CONNECTION')) {
case 'mysql':
case 'sqlite':
$this->sql[] = $column . ' VARCHAR(' . $length . ') ' . ($nullable ? '' : 'NOT ') . 'NULL';
break;

default:
throw new RuntimeException('The database connection "' . Config::env('DB_CONNECTION') . '" is not supported');
}
}

public function timestamps(): void
{
switch (Config::env('DB_CONNECTION')) {
case 'mysql':
$this->sql[] = 'createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP';
$this->sql[] = 'updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP';
break;

case 'sqlite':
$this->sql[] = 'createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP';
$this->sql[] = 'updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP';
// Add "on update" trigger
$this->afterSql[] = 'CREATE TRIGGER ' . $this->table . '_updateAt AFTER UPDATE ON `' . $this->table . '` BEGIN UPDATE ' . $this->table . ' SET updatedAt=CURRENT_TIMESTAMP WHERE id = NEW.id; END;';
break;

default:
throw new RuntimeException('The database connection "' . Config::env('DB_CONNECTION') . '" is not supported');
}
}

public function build(): array
{
switch (Config::env('DB_CONNECTION')) {
case 'mysql':
return ['CREATE TABLE `' . $this->table . '` (' . implode(',', $this->sql) . ') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;'];

case 'sqlite':
return ['CREATE TABLE `' . $this->table . '` (' . implode(',', $this->sql) . ');', ...$this->afterSql];

default:
throw new RuntimeException('The database connection "' . Config::env('DB_CONNECTION') . '" is not supported');
}
}
}
187 changes: 187 additions & 0 deletions framework/tests/Unit/Database/TestCreateTableBlueprint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<?php

use Framework\Config\Config;
use Framework\Database\CreateTableBlueprint;
use Framework\Test\TestCase;

class TestCreateTableBlueprint extends TestCase
{
public function testId(): void
{
Config::useTestMode();

// MySQL

Config::setTestValues('DB_CONNECTION', 'mysql');

$blueprint = new CreateTableBlueprint('user');
$blueprint->id();
$this->assertEquals(['CREATE TABLE `user` (id INT auto_increment,PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;'], $blueprint->build());

// SQLite

Config::setTestValues('DB_CONNECTION', 'sqlite');

$blueprint = new CreateTableBlueprint('user');
$blueprint->id();
$this->assertEquals(['CREATE TABLE `user` (id INTEGER PRIMARY KEY);'], $blueprint->build());
}

public function testBool(): void
{
Config::useTestMode();

// MySQL

Config::setTestValues('DB_CONNECTION', 'mysql');

$blueprint = new CreateTableBlueprint('user');
$blueprint->bool('isLocked');
$this->assertEquals(['CREATE TABLE `user` (isLocked TINYINT(1) NOT NULL DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;'], $blueprint->build());

$blueprint = new CreateTableBlueprint('user');
$blueprint->bool('isLocked', nullable: true);
$this->assertEquals(['CREATE TABLE `user` (isLocked TINYINT(1) NULL DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;'], $blueprint->build());

$blueprint = new CreateTableBlueprint('user');
$blueprint->bool('isLocked', default: true);
$this->assertEquals(['CREATE TABLE `user` (isLocked TINYINT(1) NOT NULL DEFAULT 1) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;'], $blueprint->build());

// SQLite

Config::setTestValues('DB_CONNECTION', 'sqlite');

$blueprint = new CreateTableBlueprint('user');
$blueprint->bool('isLocked');
$this->assertEquals(['CREATE TABLE `user` (isLocked TINYINT(1) NOT NULL DEFAULT 0);'], $blueprint->build());

$blueprint = new CreateTableBlueprint('user');
$blueprint->bool('isLocked', nullable: true);
$this->assertEquals(['CREATE TABLE `user` (isLocked TINYINT(1) NULL DEFAULT 0);'], $blueprint->build());

$blueprint = new CreateTableBlueprint('user');
$blueprint->bool('isLocked', default: true);
$this->assertEquals(['CREATE TABLE `user` (isLocked TINYINT(1) NOT NULL DEFAULT 1);'], $blueprint->build());
}

public function testInt(): void
{
Config::useTestMode();

// MySQL

Config::setTestValues('DB_CONNECTION', 'mysql');

$blueprint = new CreateTableBlueprint('user');
$blueprint->int('age');
$this->assertEquals(['CREATE TABLE `user` (age INT NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;'], $blueprint->build());

$blueprint = new CreateTableBlueprint('user');
$blueprint->int('age', true);
$this->assertEquals(['CREATE TABLE `user` (age INT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;'], $blueprint->build());

// SQLite

Config::setTestValues('DB_CONNECTION', 'sqlite');

$blueprint = new CreateTableBlueprint('user');
$blueprint->int('age');
$this->assertEquals(['CREATE TABLE `user` (age INTEGER NOT NULL);'], $blueprint->build());

$blueprint = new CreateTableBlueprint('user');
$blueprint->int('age', true);
$this->assertEquals(['CREATE TABLE `user` (age INTEGER NULL);'], $blueprint->build());
}

public function testString(): void
{
Config::useTestMode();

// MySQL

Config::setTestValues('DB_CONNECTION', 'mysql');

$blueprint = new CreateTableBlueprint('user');
$blueprint->string('firstname', 30);
$this->assertEquals(['CREATE TABLE `user` (firstname VARCHAR(30) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;'], $blueprint->build());

$blueprint = new CreateTableBlueprint('user');
$blueprint->string('firstname', 30, true);
$this->assertEquals(['CREATE TABLE `user` (firstname VARCHAR(30) NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;'], $blueprint->build());

// SQLite

Config::setTestValues('DB_CONNECTION', 'sqlite');

$blueprint = new CreateTableBlueprint('user');
$blueprint->string('firstname', 30);
$this->assertEquals(['CREATE TABLE `user` (firstname VARCHAR(30) NOT NULL);'], $blueprint->build());

$blueprint = new CreateTableBlueprint('user');
$blueprint->string('firstname', 30, true);
$this->assertEquals(['CREATE TABLE `user` (firstname VARCHAR(30) NULL);'], $blueprint->build());
}

public function testTimestamps(): void
{
Config::useTestMode();

// MySQL

Config::setTestValues('DB_CONNECTION', 'mysql');

$blueprint = new CreateTableBlueprint('user');
$blueprint->timestamps();
$this->assertEquals(['CREATE TABLE `user` (createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;'], $blueprint->build());

// SQLite

Config::setTestValues('DB_CONNECTION', 'sqlite');

$blueprint = new CreateTableBlueprint('user');
$blueprint->timestamps();
$this->assertEquals(
[
'CREATE TABLE `user` (createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP);',
'CREATE TRIGGER user_updateAt AFTER UPDATE ON `user` BEGIN UPDATE user SET updatedAt=CURRENT_TIMESTAMP WHERE id = NEW.id; END;'
],
$blueprint->build()
);
}

public function testCombined(): void
{
Config::useTestMode();

// MySQL

Config::setTestValues('DB_CONNECTION', 'mysql');

$blueprint = new CreateTableBlueprint('user');
$blueprint->id();
$blueprint->string('firstname', 30);
$blueprint->int('age', true);
$blueprint->bool('isLocked', default: true);
$blueprint->timestamps();
$this->assertEquals(['CREATE TABLE `user` (id INT auto_increment,PRIMARY KEY (id),firstname VARCHAR(30) NOT NULL,age INT NULL,isLocked TINYINT(1) NOT NULL DEFAULT 1,createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;'], $blueprint->build());

// SQLite

Config::setTestValues('DB_CONNECTION', 'sqlite');

$blueprint = new CreateTableBlueprint('user');
$blueprint->id();
$blueprint->string('firstname', 30);
$blueprint->int('age', true);
$blueprint->bool('isLocked', default: true);
$blueprint->timestamps();
$this->assertEquals(
[
'CREATE TABLE `user` (id INTEGER PRIMARY KEY,firstname VARCHAR(30) NOT NULL,age INTEGER NULL,isLocked TINYINT(1) NOT NULL DEFAULT 1,createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP);',
'CREATE TRIGGER user_updateAt AFTER UPDATE ON `user` BEGIN UPDATE user SET updatedAt=CURRENT_TIMESTAMP WHERE id = NEW.id; END;'
],
$blueprint->build()
);
}

}

0 comments on commit 9bf746b

Please # to comment.