diff --git a/Database.php b/Database.php index 27ab225..63b65d1 100644 --- a/Database.php +++ b/Database.php @@ -2,34 +2,36 @@ namespace DatabaseHelper; use DatabaseHelper\interfaces\MigrationBlueprint; -use DatabaseHelper\interfaces\QueryBuilder; use DatabaseHelper\interfaces\TableBlueprint; +use DatabaseHelper\interfaces\DeletionBuilder; use DatabaseHelper\interfaces\UpdateBuilder; +use DatabaseHelper\interfaces\InsertionBuilder; +use DatabaseHelper\interfaces\QueryBuilder; class Database implements interfaces\DatabaseHelper { - public static function makeTable(string $tableName): TableBlueprint - { - // TODO: Implement makeTable() method. + public static function makeTable(string $tableName): TableBlueprint { + return new Table($tableName); } - public static function makeMigration(TableBlueprint $table): MigrationBlueprint - { + public static function makeMigration(TableBlueprint $table): MigrationBlueprint { // TODO: Implement makeMigration() method. } - public static function makeQuery(TableBlueprint ...$tables): QueryBuilder - { + public static function makeQuery(TableBlueprint ...$tables): QueryBuilder { // TODO: Implement makeQuery() method. } - public static function makeInsertion(TableBlueprint ...$tables): QueryBuilder - { + + public static function makeDeletion(TableBlueprint ...$tables): DeletionBuilder { + // TODO: Implement makeDeletion() method. + } + + public static function makeInsertion(TableBlueprint ...$tables): InsertionBuilder { // TODO: Implement makeInsertion() method. } - public static function makeUpdate(TableBlueprint ...$tables): UpdateBuilder - { + public static function makeUpdate(TableBlueprint ...$tables): UpdateBuilder { // TODO: Implement makeUpdate() method. } diff --git a/Table.php b/Table.php new file mode 100644 index 0000000..157c5ce --- /dev/null +++ b/Table.php @@ -0,0 +1,138 @@ +tableName = $tableName; + } + + public function column(string $name, ColumnTypes $type, mixed $default = null, bool $nullable = false, bool $unique = false): TableBlueprint { + if(isset($this->columns[$name])) + throw new InvalidArgumentException('Column name already exists.'); + + $this->columns[$name] = [ + 'colName' => $name, + 'defaultVal' => $default, + 'colType' => $type, + 'nullable' => $nullable, + 'unique' => $unique + ]; + + return $this; + } + + public function primaryColumn(string $name, ColumnTypes $type, bool $autoincrement = false): TableBlueprint { + if(isset($this->primaryKey)) + throw new InvalidArgumentException('Primary column already exists.'); + + $this->primaryKey = [ + 'colName' => $name, + 'colType' => $type, + 'autoincrement' => $autoincrement + ]; + + return $this->column($name, $type); + } + + public function foreignColumn(TableBlueprint $foreignTable, CascadeTypes $onDelete, CascadeTypes $onUpdate): TableBlueprint { + $colName = $foreignTable->primaryKey['colName']; + if(isset($this->columns[$colName])) + throw new InvalidArgumentException('Column name already exists.'); + + $this->foreignKeys[] = [ + 'colName' => $colName, + 'tableName' => $foreignTable->tableName, + 'onDelete' => $onDelete, + 'onUpdate' => $onUpdate + ]; + + $this->columns[$colName] = $foreignTable->columns[$colName]; + return $this; + } + + public function engine(EngineTypes $engine): TableBlueprint { + $this->engine = $engine; + return $this; + } + + public function charset(CharsetTypes $charset): TableBlueprint { + $this->charset = $charset; + return $this; + } + + public function collation(CollationTypes $collation): TableBlueprint { + $this->collation = $collation; + return $this; + } + + public function toSql(): string { + $sql = "CREATE TABLE `{$this->tableName}` (\n"; + + if (!empty($this->primaryKey)) { + $sql .= " `{$this->primaryKey['colName']}` {$this->primaryKey['colType']->value}"; + if ($this->primaryKey['autoincrement']) + $sql .= " AUTO_INCREMENT"; + $sql .= " NOT NULL,\n"; + } + + foreach ($this->columns as $column) { + if ($column['colName'] !== $this->primaryKey['colName']) { + $sql .= " `{$column['colName']}` {$column['colType']->value}"; + + if (!$column['nullable']) + $sql .= " NOT NULL"; + + if ($column['unique']) + $sql .= " UNIQUE"; + + if ($column['defaultVal'] !== null) { + $default = is_string($column['defaultVal']) ? "'{$column['defaultVal']}'" : $column['defaultVal']; + $sql .= " DEFAULT $default"; + } + + $sql .= ",\n"; + } + } + + if (!empty($this->primaryKey)) + $sql .= " PRIMARY KEY (`{$this->primaryKey['colName']}`),\n"; + + foreach ($this->foreignKeys as $foreignKey) { + $sql .= " FOREIGN KEY (`{$foreignKey['colName']}`) REFERENCES `{$foreignKey['tableName']}` (`{$foreignKey['colName']}`)"; + $sql .= " ON DELETE {$foreignKey['onDelete']->type()} ON UPDATE {$foreignKey['onUpdate']->type()},\n"; + } + + $sql = rtrim($sql, ",\n") . "\n"; + $sql .= ") ENGINE={$this->engine->type()} CHARSET={$this->charset->type()} COLLATE={$this->collation->type()};"; + + return $sql; + } + + public function create(): void { + $sql = $this->toSql(); + require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); + dbDelta($sql); + } +} diff --git a/build.php b/build.php new file mode 100644 index 0000000..e80519d --- /dev/null +++ b/build.php @@ -0,0 +1,21 @@ + 'ascii', self::UTF16 => 'utf16', self::UTF32 => 'utf32', + self::LATIN2 => 'latin2', + self::LATIN5 => 'latin5', + self::CP1251 => 'cp1251', + self::CP850 => 'cp850', + self::TIS620 => 'tis620', + self::GREEK => 'greek', + self::HEBREW => 'hebrew', + self::BINARY => 'binary', }; } } + diff --git a/enums/CollationTypes.php b/enums/CollationTypes.php index 32cf020..446d223 100644 --- a/enums/CollationTypes.php +++ b/enums/CollationTypes.php @@ -1,24 +1,32 @@ 'utf8_general_ci', + self::UTF8_UNICODE_CI => 'utf8_unicode_ci', + self::UTF8_SPANISH_CI => 'utf8_spanish_ci', self::UTF8MB4_UNICODE_CI => 'utf8mb4_unicode_ci', + self::UTF8MB4_GENERAL_CI => 'utf8mb4_general_ci', + self::UTF8MB4_UNICODE_520_CI => 'utf8mb4_unicode_520_ci', self::LATIN1_SWEDISH_CI => 'latin1_swedish_ci', self::UTF8MB4_BIN => 'utf8mb4_bin', self::UTF8_BIN => 'utf8_bin', - self::UTF8_GENERAL_BIN => 'utf8_general_bin', + self::LATIN1_BIN => 'latin1_bin', }; } } + diff --git a/enums/ColumnTypes.php b/enums/ColumnTypes.php index d098897..747a76f 100644 --- a/enums/ColumnTypes.php +++ b/enums/ColumnTypes.php @@ -1,6 +1,5 @@ is_numeric($value), + self::STRING => is_scalar($value) || (is_object($value) && method_exists($value, '__toString')), + self::BOOL => is_bool($value) || in_array($value, [0, 1, '0', '1'], true), + self::ARRAY => is_array($value) || is_string($value) && json_decode($value, true) !== null, + self::DATE => $value instanceof \DateTime || (is_string($value) && strtotime($value) !== false), + }; + } catch (\Throwable $e) { + return false; + } + } + public function type(): string { return match ($this) { self::INT => 'INTEGER', diff --git a/interfaces/DatabaseHelper.php b/interfaces/DatabaseHelper.php index fcbf2ab..1547124 100644 --- a/interfaces/DatabaseHelper.php +++ b/interfaces/DatabaseHelper.php @@ -34,9 +34,18 @@ interface DatabaseHelper * When multiple tables are provided, they will be joined automatically for the insertion. * * @param TableBlueprint ...$tables One or more Table instances. - * @return QueryBuilder Generated Query instance. + * @return InsertionBuilder Generated Query instance. */ - public static function makeInsertion(TableBlueprint ...$tables): QueryBuilder; + public static function makeInsertion(TableBlueprint ...$tables): InsertionBuilder; + + /** + * Initializes a Deletion instance to delete records from the specified tables. + * When multiple tables are provided, they will be joined automatically. + * + * @param TableBlueprint ...$tables One or more Table instances. + * @return DeletionBuilder Generated Deletion instance. + */ + public static function makeDeletion(TableBlueprint ...$tables): DeletionBuilder; /** * Initializes an Update instance to perform updates on the specified tables. diff --git a/interfaces/DeletionBuilder.php b/interfaces/DeletionBuilder.php new file mode 100644 index 0000000..9e43065 --- /dev/null +++ b/interfaces/DeletionBuilder.php @@ -0,0 +1,8 @@ +