From 2ad52aba852629e88ff065b4df6a52eb4077ab19 Mon Sep 17 00:00:00 2001 From: Dave M Date: Thu, 30 Mar 2023 15:24:48 +0000 Subject: [PATCH 1/2] - WIP on Alter table for lean-console --- src/Adapter/AdapterInterface.php | 1 + src/Adapter/DefaultAdapterTrait.php | 18 ++++++++++++++++++ src/Adapter/SQLite.php | 10 ++++++++++ src/Attribute/Property/Field/ForeignKey.php | 7 ++++++- src/Common/EntityField.php | 7 +++++++ src/Entity/Sqlite/Schema.php | 12 +++++++++--- src/Entity/Sqlite/Table.php | 1 + src/Query/Alter.php | 4 ++-- src/QueryBuilder.php | 2 +- src/Repository.php | 10 +++++++--- src/Repository/SqliteRepository.php | 2 +- 11 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/Adapter/AdapterInterface.php b/src/Adapter/AdapterInterface.php index f383caa..5694ef7 100644 --- a/src/Adapter/AdapterInterface.php +++ b/src/Adapter/AdapterInterface.php @@ -29,4 +29,5 @@ interface AdapterInterface { public function queryBuilderClass() : string; public function tableSyntax() : array; public function whitelistAttributes(array &$parameters) : void; + public function generateAlterColumn(FieldDefinition $definition, array $field) : string|\Stringable; } diff --git a/src/Adapter/DefaultAdapterTrait.php b/src/Adapter/DefaultAdapterTrait.php index a46b25a..9c4cf8b 100644 --- a/src/Adapter/DefaultAdapterTrait.php +++ b/src/Adapter/DefaultAdapterTrait.php @@ -100,4 +100,22 @@ trait DefaultAdapterTrait { $parameters = array_intersect_key($parameters, array_flip(static::ALLOWED_ATTRIBUTES)); } + + public function generateAlterColumn(FieldDefinition $definition, array $field) : string|\Stringable + { + if ($field['previous']) { + $position = sprintf('AFTER %s', $adapter->escapeIdentifier($field['previous']['field'], AdapterInterface::IDENTIFIER_FIELD)); + } + else { + $position = "FIRST"; + } + + return implode(" ", [ + strtoupper($field['action']), + $this->escapeIdentifier($definition->getSqlName(), AdapterInterface::IDENTIFIER_FIELD), + $definition->getSqlType(), + $definition->getSqlParams(), + $position, + ]); + } } diff --git a/src/Adapter/SQLite.php b/src/Adapter/SQLite.php index d07ca0d..03f354e 100644 --- a/src/Adapter/SQLite.php +++ b/src/Adapter/SQLite.php @@ -203,4 +203,14 @@ class SQLite implements AdapterInterface { $pdo->sqliteCreateFunction('month', fn($date) => ( new \DateTime($date) )->format('m'), 1); $pdo->sqliteCreateFunction('year', fn($date) => ( new \DateTime($date) )->format('Y'), 1); } + + public function generateAlterColumn(FieldDefinition $definition, array $field) : string|\Stringable + { + return implode(" ", [ + strtoupper($field['action']), + $this->escapeIdentifier($definition->getSqlName(), static::IDENTIFIER_FIELD), + $definition->getSqlType(), + $definition->getSqlParams(), + ]); + } } diff --git a/src/Attribute/Property/Field/ForeignKey.php b/src/Attribute/Property/Field/ForeignKey.php index 9152736..a2098a8 100644 --- a/src/Attribute/Property/Field/ForeignKey.php +++ b/src/Attribute/Property/Field/ForeignKey.php @@ -2,6 +2,8 @@ namespace Ulmus\Attribute\Property\Field; +use Ulmus\Attribute\Attribute; + /** * Since we need consistancy between the declaration of our ID and FK fields, it * was decided to extend the PK class instead of Field for this case. @@ -20,5 +22,8 @@ class ForeignKey extends PrimaryKey { public bool $nullable = false, public mixed $default = null, public bool $readonly = false, - ) {} + public null|array|string|\Stringable $references = null, + ) { + $this->references = Attribute::handleArrayField($this->references); + } } diff --git a/src/Common/EntityField.php b/src/Common/EntityField.php index 4e84344..2cdddb7 100644 --- a/src/Common/EntityField.php +++ b/src/Common/EntityField.php @@ -62,6 +62,13 @@ class EntityField implements WhereRawParameter ]); } + public static function generateAlterColumn(AdapterInterface $adapter, array $field) : string + { + $definition = new FieldDefinition($adapter, $field['definition']); + + return $adapter->generateAlterColumn($definition, $field); + } + public static function isObjectType($type) : bool { # @Should be fixed with isBuiltIn() instead, it won't be correct based only on name diff --git a/src/Entity/Sqlite/Schema.php b/src/Entity/Sqlite/Schema.php index 491a1c2..04122da 100644 --- a/src/Entity/Sqlite/Schema.php +++ b/src/Entity/Sqlite/Schema.php @@ -3,8 +3,8 @@ namespace Ulmus\Entity\Sqlite; use Ulmus\EntityCollection; - -use Ulmus\{Attribute\Obj\Table}; +use Ulmus\Query\{From, Select}; +use Ulmus\{Attribute\Obj\Table, Repository}; use Ulmus\Attribute\Property\{Field, Filter, FilterJoin, Relation, Join, Virtual, Where}; #[Table(name: "sqlite_master")] @@ -27,6 +27,12 @@ class Schema #[Field] public ? string $sql; - #[Relation("oneToMany", key: "tableName", foreignKey: "tableName", entity: Column::class)] + ##[Relation("oneToMany", key: "tableName", foreignKey: "tableName", entity: Column::class)] + #[Virtual(method: "filterColumns")] public EntityCollection $columns; + + public function filterColumns() : EntityCollection + { + return ( new Repository\SqliteRepository(Column::class) )->pragma('table_info', $this->tableName)->collectionFromQuery(); + } } \ No newline at end of file diff --git a/src/Entity/Sqlite/Table.php b/src/Entity/Sqlite/Table.php index 9ae8f4c..25b5470 100644 --- a/src/Entity/Sqlite/Table.php +++ b/src/Entity/Sqlite/Table.php @@ -5,6 +5,7 @@ namespace Ulmus\Entity\Sqlite; use Ulmus\ConnectionAdapter; use Ulmus\Repository; +#[\Ulmus\Attribute\Obj\Table(name: "sqlite_master")] class Table extends Schema { public static function repository(string $alias = Repository::DEFAULT_ALIAS, ConnectionAdapter $adapter = null): Repository diff --git a/src/Query/Alter.php b/src/Query/Alter.php index 8963035..4fa0f9a 100644 --- a/src/Query/Alter.php +++ b/src/Query/Alter.php @@ -35,8 +35,8 @@ class Alter extends Fragment { public function renderFields() : string { - return "(" . PHP_EOL . implode("," . PHP_EOL, array_map(function($field) { + return PHP_EOL . implode("," . PHP_EOL, array_map(function($field) { return " " . EntityField::generateAlterColumn($this->adapter, $field); - }, $this->fieldList)) . PHP_EOL . ")"; + }, $this->fieldList)); } } diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 90d7a1f..2b9de87 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -392,7 +392,7 @@ class QueryBuilder implements Query\QueryBuilderInterface public function alter(Adapter\AdapterInterface $adapter, array $fieldlist, string $table, ? string $database = null, ? string $schema = null) : self { - if ( null === $this->getFragment(Query\Create::class) ) { + if ( null === $this->getFragment(Query\Alter::class) ) { if ( $schema ) { $table = "$schema.$table"; } diff --git a/src/Repository.php b/src/Repository.php index 1f19b63..d45ab8a 100644 --- a/src/Repository.php +++ b/src/Repository.php @@ -51,7 +51,7 @@ class Repository public function loadOne() : ? object { - return $this->limit(1)->selectSqlQuery()->collectionFromQuery()[0]; + return $this->limit(1)->selectSqlQuery()->collectionFromQuery()[0] ?? null; } public function loadOneFromField($field, $value) : ? object @@ -301,6 +301,11 @@ class Repository return $this->createSqlQuery()->runQuery(); } + public function alterTable(array $fields) + { + return $this->alterSqlQuery($fields)->runQuery(); + } + public function listTables(? string $database = null) { return $this->showTablesSqlQuery($database)->runQuery(); @@ -953,10 +958,9 @@ class Repository public function alterSqlQuery(array $fields) : self { if ( null === $this->queryBuilder->getFragment(Query\Alter::class) ) { - $this->queryBuilder->create($this->adapter->adapter(), $this->escapeFieldList($this->entityResolver->fieldList(EntityResolver::KEY_ENTITY_NAME, true)), $this->escapeTable($this->entityResolver->tableName()), $this->entityResolver->schemaName()); + $this->queryBuilder->alter($this->adapter->adapter(), $fields, $this->escapeTable($this->entityResolver->tableName()), $this->entityResolver->schemaName()); } - return $this; } diff --git a/src/Repository/SqliteRepository.php b/src/Repository/SqliteRepository.php index c8800d7..ca21f5e 100644 --- a/src/Repository/SqliteRepository.php +++ b/src/Repository/SqliteRepository.php @@ -6,7 +6,7 @@ use Ulmus\{ConnectionAdapter, QueryBuilder, Repository, Query, Ulmus, Entity}; class SqliteRepository extends Repository { - public function pragma(/*object|Stringable*/ $pragma, $argument = null, bool $callable = false) : self + public function pragma(\Stringable|string $pragma, $argument = null, bool $callable = false) : self { $this->queryBuilder->pragma($pragma, $argument, $callable); From cfaebbecc404fdfd3973810cff5762b0bebf6726 Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Wed, 5 Apr 2023 17:42:02 +0000 Subject: [PATCH 2/2] - Minor enhancements --- src/Common/Sql.php | 3 ++- src/EntityCollection.php | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Common/Sql.php b/src/Common/Sql.php index cd02dc3..7ee3a98 100644 --- a/src/Common/Sql.php +++ b/src/Common/Sql.php @@ -56,7 +56,8 @@ abstract class Sql { $this->identifier = $identifier; } - public function __toString() { + public function __toString() : string + { return $this->identifier; } }; diff --git a/src/EntityCollection.php b/src/EntityCollection.php index 1802787..7c6216e 100644 --- a/src/EntityCollection.php +++ b/src/EntityCollection.php @@ -222,6 +222,11 @@ class EntityCollection extends \ArrayObject { return $list; } + public function sum($field) : float|int + { + return array_sum($this->column($field)); + } + public function unique(\Stringable|callable|string $field, bool $strict = false) : self { $list = [];