diff --git a/src/Common/EntityField.php b/src/Common/EntityField.php index c0f72c9..a9ce096 100644 --- a/src/Common/EntityField.php +++ b/src/Common/EntityField.php @@ -53,8 +53,6 @@ class EntityField { $definition = new FieldDefinition($field); - # column_name data_type(length) [NOT NULL] [DEFAULT value] [AUTO_INCREMENT] column_constraint; - return implode(" ", [ $definition->getSqlName(), $definition->getSqlType(), diff --git a/src/Common/EntityResolver.php b/src/Common/EntityResolver.php index 3dc23fc..95e90f9 100644 --- a/src/Common/EntityResolver.php +++ b/src/Common/EntityResolver.php @@ -83,13 +83,23 @@ class EntityResolver { throw new \InvalidArgumentException("Given `fieldKey` is unknown to the EntityResolver"); } + if ($escape) { + if ( isset($tag['object']->name) ) { + $tag['object']->name = 2; + } + + if ( isset($item['name']) ) { + $item['name'] = 2; + } + } + $fieldList[$key] = $item; break; } } } - + return $fieldList; } diff --git a/src/EntityCollection.php b/src/EntityCollection.php index cb17ff8..2fb8120 100644 --- a/src/EntityCollection.php +++ b/src/EntityCollection.php @@ -25,12 +25,12 @@ class EntityCollection extends \ArrayObject { } } } - - public function filtersCollection(Callable $callback, bool $replaceCollection = false) : self + + public function filtersCollection(Callable $callback, bool $yieldValueOnly = false, bool $replaceCollection = false) : self { $collection = new static(); - - foreach($this->filters($callback, true) as $item) { + + foreach($this->filters($callback, $yieldValueOnly) as $item) { $collection->append($item); } @@ -86,15 +86,8 @@ class EntityCollection extends \ArrayObject { return $removed; } - - public function clear() : self - { - $this->exchangeArray([]); - - return $this; - } - - public function search($value, string $field, bool $strict = true) : Generator + + public function search($value, string $field, bool $strict = true) : Generator { foreach($this->filters(fn($v) => $strict ? $v->$field === $value : $v->$field == $value) as $key => $item) { yield $key => $item; @@ -121,12 +114,7 @@ class EntityCollection extends \ArrayObject { return $obj; } - - public function searchInstances(string $className) : self - { - return $this->filtersCollection(fn($obj) => is_a($obj, $className)); - } - + public function column($field, bool $unique = false) : array { $list = []; @@ -138,9 +126,9 @@ class EntityCollection extends \ArrayObject { else { $value = $item->$field; } - - if ($unique && in_array($value, $list, true)) { - continue; + + if ($unique && in_array($value, $list)) { + break; } $list[] = $value; @@ -269,22 +257,15 @@ class EntityCollection extends \ArrayObject { parent::append($value); } } - - public function mergeWith(... $datasets) : self + + public function mergeWith( /*array|EntityCollection*/ $datasets ) : self { - $list = []; - - foreach($datasets as $dataset) { - if ( is_object($dataset) ) { - $list = array_merge($dataset->getArrayCopy(), $list); - } - else { - $list = array_merge($dataset, $list); - } + if ( is_object($datasets) ) { + $datasets = $datasets->getArrayCopy(); } - - $this->exchangeArray( array_merge( $this->getArrayCopy(), $list ) ); - + + $this->exchangeArray( array_merge( $this->getArrayCopy(), $datasets ) ); + return $this; } @@ -329,11 +310,32 @@ class EntityCollection extends \ArrayObject { return $this; } - public function rsort(callable $callback, $function = "uasort") : self + public function pop() /* : mixed */ { - $this->sort(...func_get_args()); + $arr = $this->getArrayCopy(); + $pop = array_pop($arr); + $this->exchangeArray($arr); - return $this->reverse(); + return $pop; + } + + public function shift() /* : mixed */ + { + $arr = $this->getArrayCopy(); + $shift = array_shift($arr); + $this->exchangeArray($arr); + + return $shift; + } + + public function reverse() : self + { + return $this->replaceWith(array_reverse($this->getArrayCopy())); + } + + public function slice(int $offset, ? int $length = null) : self + { + return new self(array_slice($this->getArrayCopy(), $offset, $length)); } public function sortField(? string $field = null) : self @@ -345,14 +347,4 @@ class EntityCollection extends \ArrayObject { { return $this->sort(fn($e1, $e2) => $field ? $e2->$field <=> $e1->$field : $e2 <=> $e1); } - - public function reverse() : self - { - return $this->replaceWith(array_reverse($this->getArrayCopy()));; - } - - public function slice(int $offset, ? int $length = null) : self - { - return new self(array_slice($this->getArrayCopy(), $offset, $length)); - } } diff --git a/src/Migration/FieldDefinition.php b/src/Migration/FieldDefinition.php index d888512..ad35d5d 100644 --- a/src/Migration/FieldDefinition.php +++ b/src/Migration/FieldDefinition.php @@ -11,6 +11,8 @@ class FieldDefinition { public bool $builtIn; + public string $name; + public string $type; public array $tags; @@ -115,9 +117,7 @@ class FieldDefinition { protected function getFieldTag() : ? Field { - $field = array_filter($this->tags, function($item) { - return $item['object'] instanceof Field; - }); + $field = array_filter($this->tags, fn($item) => $item['object'] instanceof Field); return array_pop($field)['object']; } diff --git a/src/Query/Create.php b/src/Query/Create.php index ed7ca4b..b884339 100644 --- a/src/Query/Create.php +++ b/src/Query/Create.php @@ -22,14 +22,13 @@ class Create extends Fragment { public function render() : string { return $this->renderSegments([ - static::SQL_TOKEN, $this->renderTables($this->table), - $this->renderFields(), + static::SQL_TOKEN, $this->renderTables($this->table), $this->renderFields(), ]); } 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::generateCreateColumn($field); }, $this->fieldList)) . PHP_EOL . ")"; } diff --git a/src/Query/Set.php b/src/Query/Set.php index 2004919..a3ddce4 100644 --- a/src/Query/Set.php +++ b/src/Query/Set.php @@ -9,7 +9,9 @@ class Set extends Fragment { public int $order = 0; public array $dataset; - + + public ? array $escapedFields; + public QueryBuilderInterface $queryBuilder; public function __construct(QueryBuilderInterface $queryBuilder) @@ -17,10 +19,11 @@ class Set extends Fragment { $this->queryBuilder = $queryBuilder; } - public function set(array $dataset) : self + public function set(array $dataset, ? array $escapedFields = null) : self { $this->dataset = $dataset; - + $this->escapedFields = $escapedFields; + return $this; } @@ -34,10 +37,13 @@ class Set extends Fragment { public function renderParameterPlaceholders() : string { $keys = []; - + foreach($this->dataset as $key =>$value) { $this->queryBuilder->addParameter($value, ":v_$key"); - $keys[] = "$key=:v_$key"; + + $field = $this->escapedFields[$key] ?? $key; + + $keys[] = "{$field}=:v_{$key}"; } return implode(",", $keys); diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 7d30901..6134203 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -119,7 +119,7 @@ class QueryBuilder implements Query\QueryBuilderInterface return $this; } - public function set(array $dataset) : self + public function set(array $dataset, ? array $escapedFields = null) : self { if ( null === ( $set = $this->getFragment(Query\Set::class) ) ) { @@ -127,7 +127,7 @@ class QueryBuilder implements Query\QueryBuilderInterface $this->push($set); } - $set->set($dataset); + $set->set($dataset, $escapedFields); return $this; } diff --git a/src/Repository.php b/src/Repository.php index 00ab211..8ce8bc6 100644 --- a/src/Repository.php +++ b/src/Repository.php @@ -2,7 +2,7 @@ namespace Ulmus; -use Ulmus\Annotation\Property\{ Where, Having, Relation, Join, WithJoin, Relation\Ignore as RelationIgnore }; +use Ulmus\Annotation\Property\{Field, Where, Having, Relation, Join, WithJoin, Relation\Ignore as RelationIgnore}; use Ulmus\Common\EntityResolver; class Repository @@ -175,64 +175,6 @@ class Repository } } - public function loadCollectionRelation(EntityCollection $collection, /*array|string*/ $fields) : void - { - foreach((array) $fields as $name) { - if ( null !== ( $relation = $this->entityResolver->searchFieldAnnotation($name, new Annotation\Property\Relation() ) ) ) { - $relationType = strtolower(str_replace(['-', '_', ' '], '', $relation->type)); - - $order = $this->entityResolver->searchFieldAnnotationList($name, new Annotation\Property\OrderBy() ); - $where = $this->entityResolver->searchFieldAnnotationList($name, new Annotation\Property\Where() ); - - $baseEntity = $relation->entity ?? $relation->bridge ?? $this->entityResolver->properties[$name]['type']; - $baseEntityResolver = $baseEntity::resolveEntity(); - - $property = ( $baseEntityResolver->field($relation->foreignKey, 01, false) ?: $baseEntityResolver->field($relation->foreignKey, 02) )['name']; - $entityProperty = ( $this->entityResolver->field($relation->key, 01, false) ?: $this->entityResolver->field($relation->key, 02) )['name']; - - $repository = $baseEntity::repository(); - - foreach($where as $condition) { - $repository->where($condition->field, is_callable($condition->value) ? call_user_func_array($condition->value, [ $this ]) : $condition->value, $condition->operator, $condition->condition); - } - - foreach($order as $item) { - $repository->orderBy($item->field, $item->order); - } - - $field = $relation->key; - - $values = []; - - $key = is_object($relation->foreignKey) ? $relation->foreignKey : $baseEntity::field($relation->foreignKey); - - foreach($collection as $item) { - $values[] = is_callable($field) ? $field($item) : $item->$entityProperty; - } - - $repository->where($key, $values); - - switch( $relationType ) { - case 'onetoone': - $results = call_user_func([ $repository, "loadOne" ]); - $item->$name = $results ?: new $baseEntity(); - - break; - - case 'onetomany': - $results = call_user_func([ $repository, $relation->function ]); - - foreach($collection as $item) { - $item->$name = $baseEntity::entityCollection(); - $item->$name->mergeWith( $results->filtersCollection(fn($e) => $e->$property === $item->$entityProperty ) ); - } - - break; - } - } - } - } - public function truncate(? string $table = null, ? string $alias = null, ? string $schema = null) : self { $schema = $schema ?: $this->entityResolver->schemaName(); @@ -347,7 +289,10 @@ class Repository public function set(array $dataset) : self { - $this->queryBuilder->set($dataset); + $keys = array_keys($dataset); + $escapedFields = array_combine($keys, array_map([ $this, 'escapeField' ], $keys)); + + $this->queryBuilder->set($dataset, $escapedFields); return $this; } @@ -732,7 +677,7 @@ class Repository public function createSqlQuery() : self { if ( null === $this->queryBuilder->getFragment(Query\Create::class) ) { - $this->queryBuilder->create($this->entityResolver->fieldList(), $this->escapeTable($this->entityResolver->tableName()), $this->entityResolver->schemaName()); + $this->queryBuilder->create($this->escapeFieldList($this->entityResolver->fieldList()), $this->escapeTable($this->entityResolver->tableName()), $this->entityResolver->schemaName()); } if ( null === $this->queryBuilder->getFragment(Query\Engine::class) ) { @@ -778,6 +723,21 @@ class Repository return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_FIELD); } + public function escapeFieldList(array $fieldList) : array + { + foreach($fieldList as & $list) { + $list['name'] = $this->escapeField($list['name']); + + $fieldTag = array_filter($list['tags'] ?? [], fn($item) => $item['object'] instanceof Field)[0]['object'] ?? null; + + if ( $fieldTag && isset($fieldTag->name) ) { + $fieldTag->name = $this->escapeField($fieldTag->name); + } + } + + return $fieldList; + } + public function escapeTable(string $identifier) : string { return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_TABLE);