- Field mapper is now removed from Adapters and set into it's own object

- Added a PK to Column (IS)
This commit is contained in:
Dave Mc Nicoll 2024-10-14 12:53:15 +00:00
parent 9977a25cf5
commit 3c2ae86653
7 changed files with 168 additions and 57 deletions

View File

@ -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;
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Ulmus\Adapter;
use Ulmus\Migration\FieldDefinition;
class MySQLFieldMapper extends SqlFieldMapper
{
public readonly string $type;
public readonly string $length;
public function map() : void
{
$type = $this->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();
}
}
}

View File

@ -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:

View File

@ -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;
}

View File

@ -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

View File

@ -0,0 +1,87 @@
<?php
namespace Ulmus\Adapter;
use Ulmus\Migration\FieldDefinition;
class SqlFieldMapper
{
public readonly string $type;
public readonly string $length;
public function __construct(
public FieldDefinition $field,
) {
$this->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)" : "" );
}
}

View File

@ -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;
}
}