From 3c2ae86653d574dd9333e20e3301a915a5a5495d Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Mon, 14 Oct 2024 12:53:15 +0000 Subject: [PATCH] - Field mapper is now removed from Adapters and set into it's own object - Added a PK to Column (IS) --- src/Adapter/MySQL.php | 11 +++- src/Adapter/MySQLFieldMapper.php | 31 +++++++++ src/Adapter/SQLite.php | 2 +- src/Adapter/SqlAdapterInterface.php | 2 +- src/Adapter/SqlAdapterTrait.php | 55 +--------------- src/Adapter/SqlFieldMapper.php | 87 +++++++++++++++++++++++++ src/Entity/InformationSchema/Column.php | 37 +++++++++++ 7 files changed, 168 insertions(+), 57 deletions(-) create mode 100644 src/Adapter/MySQLFieldMapper.php create mode 100644 src/Adapter/SqlFieldMapper.php diff --git a/src/Adapter/MySQL.php b/src/Adapter/MySQL.php index 53d440b..d020889 100644 --- a/src/Adapter/MySQL.php +++ b/src/Adapter/MySQL.php @@ -2,6 +2,7 @@ namespace Ulmus\Adapter; +use Ulmus\Migration\FieldDefinition; use Ulmus\Migration\MigrateInterface; use Ulmus\QueryBuilder\Sql; use Ulmus\Common\PdoObject; @@ -136,7 +137,14 @@ class MySQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface { } } - public function escapeIdentifier(string $segment, int $type) : string + public function mapFieldType(FieldDefinition $field, bool $typeOnly = false) : string + { + $mapper = new MySQLFieldMapper($field); + + return $typeOnly ? $mapper->type : $mapper->render(); + } + + public static function escapeIdentifier(string $segment, int $type) : string { switch($type) { case static::IDENTIFIER_DATABASE: @@ -158,5 +166,4 @@ class MySQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface { { return Sql\MysqlQueryBuilder::class; } - } diff --git a/src/Adapter/MySQLFieldMapper.php b/src/Adapter/MySQLFieldMapper.php new file mode 100644 index 0000000..e080083 --- /dev/null +++ b/src/Adapter/MySQLFieldMapper.php @@ -0,0 +1,31 @@ +field->type; + $length = $this->field->length; + + if ( enum_exists($type) ) { + # Haven't found a better way yet to check for BackendEnum without an instance of the object + if ( ! method_exists($type, 'tryFrom') ) { + throw new \Ulmus\Exception\BackedEnumRequired(sprintf("You must define your enum as a BackedEnum instead of an UnitEnum for field '%s'.", $this->field->getColumnName())); + } + + $this->length = implode(',', array_map(fn($e) => MySQL::escapeIdentifier($e->value, MySQL::IDENTIFIER_VALUE) , $type::cases())); + $this->type = "ENUM"; + } + else { + parent::map(); + } + } +} \ No newline at end of file diff --git a/src/Adapter/SQLite.php b/src/Adapter/SQLite.php index 3c1732a..c890648 100644 --- a/src/Adapter/SQLite.php +++ b/src/Adapter/SQLite.php @@ -61,7 +61,7 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface } # https://sqlite.org/lang_keywords.html - public function escapeIdentifier(string $segment, int $type) : string + public static function escapeIdentifier(string $segment, int $type) : string { switch($type) { default: diff --git a/src/Adapter/SqlAdapterInterface.php b/src/Adapter/SqlAdapterInterface.php index 3a5bfea..6d0cbd3 100644 --- a/src/Adapter/SqlAdapterInterface.php +++ b/src/Adapter/SqlAdapterInterface.php @@ -4,6 +4,6 @@ namespace Ulmus\Adapter; interface SqlAdapterInterface { - public function escapeIdentifier(string $segment, int $type) : string; + public static function escapeIdentifier(string $segment, int $type) : string; public function defaultEngine() : ? string; } \ No newline at end of file diff --git a/src/Adapter/SqlAdapterTrait.php b/src/Adapter/SqlAdapterTrait.php index 04f7288..2e8b896 100644 --- a/src/Adapter/SqlAdapterTrait.php +++ b/src/Adapter/SqlAdapterTrait.php @@ -46,60 +46,9 @@ trait SqlAdapterTrait public function mapFieldType(FieldDefinition $field, bool $typeOnly = false) : string { - $type = $field->type; + $mapper = new SqlFieldMapper($field); - $length = $field->length; - - if ( is_a($type, Entity\Field\Date::class, true) ) { - $type = "DATE"; - } - elseif ( is_a($type, Entity\Field\Time::class, true) ) { - $type = "TIME"; - } - elseif ( is_a($type, \DateTime::class, true) ) { - $type = "DATETIME"; - } - - switch($type) { - case "bool": - $type = "TINYINT"; - $length = 1; - break; - - case "array": - case "string": - if ($length && $length <= 255) { - $type = "VARCHAR"; - break; - } - elseif (! $length || ( $length <= 65535 ) ) { - $type = "TEXT"; - } - elseif ( $length <= 16777215 ) { - $type = "MEDIUMTEXT"; - } - elseif ($length <= 4294967295) { - $type = "LONGTEXT"; - } - else { - throw new \Exception("A column with size bigger than 4GB cannot be created."); - } - - # Length is unnecessary on TEXT fields - unset($length); - - break; - - case "float": - $type = "DOUBLE"; - break; - - default: - $type = strtoupper($type); - break; - } - - return $typeOnly ? $type : $type . ( isset($length) ? "($length" . ( ! empty($precision) ? ",$precision" : "" ) . ")" : "" ); + return $typeOnly ? $mapper->type : $mapper->render(); } public function whitelistAttributes(array &$parameters) : void diff --git a/src/Adapter/SqlFieldMapper.php b/src/Adapter/SqlFieldMapper.php new file mode 100644 index 0000000..e06447e --- /dev/null +++ b/src/Adapter/SqlFieldMapper.php @@ -0,0 +1,87 @@ +map(); + } + + public function map() : void + { + $type = $this->field->type; + $length = $this->field->length; + + if ( is_a($type, Entity\Field\Date::class, true) ) { + $type = "DATE"; + } + elseif ( is_a($type, Entity\Field\Time::class, true) ) { + $type = "TIME"; + } + elseif ( is_a($type, \DateTime::class, true) ) { + $type = "DATETIME"; + } + elseif ( enum_exists($type) ) { + # Haven't found a better way yet to check for BackendEnum without an instance of the object + if ( ! method_exists($type, 'tryFrom') ) { + throw new \Ulmus\Exception\BackedEnumRequired(sprintf("You must define your enum as a BackedEnum instead of an UnitEnum for field '%s'.", $this->field->getColumnName())); + } + + $type = "string"; + } + + switch($type) { + case "bool": + $this->type = "TINYINT"; + $length = 1; + break; + + case "array": + case "string": + if ($length && $length <= 255) { + $this->type = "VARCHAR"; + break; + } + elseif (! $length || ( $length <= 65535 ) ) { + $this->type = "TEXT"; + } + elseif ( $length <= 16777215 ) { + $this->type = "MEDIUMTEXT"; + } + elseif ($length <= 4294967295) { + $this->type = "LONGTEXT"; + } + else { + throw new \Exception("A column with a length bigger than 4GB cannot be created."); + } + + # Length is unnecessary on TEXT fields + unset($length); + + break; + + case "float": + $this->type = "DOUBLE"; + break; + + default: + $this->type = strtoupper($type); + + break; + } + } + + public function render() : string + { + return $this->type . ( isset($this->length) ? "($this->length)" : "" ); + } +} \ No newline at end of file diff --git a/src/Entity/InformationSchema/Column.php b/src/Entity/InformationSchema/Column.php index 57ce2f8..b141e20 100644 --- a/src/Entity/InformationSchema/Column.php +++ b/src/Entity/InformationSchema/Column.php @@ -2,6 +2,7 @@ namespace Ulmus\Entity\InformationSchema; +use Notes\Common\ReflectedProperty; use Ulmus\Entity\Field\Datetime; use Ulmus\{Attribute\Obj\Table}; @@ -12,6 +13,9 @@ class Column { use \Ulmus\EntityTrait; + #[Field\Id] + public ? id $srs_id; + #[Field(name: "TABLE_CATALOG", length: 512)] public string $tableCatalog; @@ -78,4 +82,37 @@ class Column #[Field(name: "GENERATION_EXPRESSION", type: "longtext")] public ? string $generationExpression; + public function matchFieldDefinition(ReflectedProperty $definition) : bool + { + if ($this->nullable === $definition->allowsNull()) { + return false; + } + + if (isset($definition->value)) { + if ( $definition->value !== $this->defaultValue()) { + return false; + } + } + elseif (! isset($definition->value)) { + if ( ! $this->defaultValueIsNull() ) { + return false; + } + } + + return true; + } + + protected function defaultValueIsNull() : bool + { + return $this->defaultValue() === null; + } + + protected function defaultValue() : mixed + { + if (is_numeric($this->default)) { + return (int) $this->default; + } + + return $this->default; + } } \ No newline at end of file