diff --git a/src/Adapter/DefaultAdapterTrait.php b/src/Adapter/DefaultAdapterTrait.php index 99aa9d0..2de4f7b 100644 --- a/src/Adapter/DefaultAdapterTrait.php +++ b/src/Adapter/DefaultAdapterTrait.php @@ -2,7 +2,7 @@ namespace Ulmus\Adapter; -use Ulmus\{ConnectionAdapter, Entity\InformationSchema\Table, Migration\FieldDefinition, Repository, QueryBuilder\MysqlQueryBuilder}; +use Ulmus\{ConnectionAdapter, Entity\InformationSchema\Table, Migration\FieldDefinition, Repository, QueryBuilder\Sql\MysqlQueryBuilder}; trait DefaultAdapterTrait { diff --git a/src/Common/PdoObject.php b/src/Common/PdoObject.php index 3a2cf66..e1d1f59 100644 --- a/src/Common/PdoObject.php +++ b/src/Common/PdoObject.php @@ -25,13 +25,13 @@ class PdoObject extends PDO { if (false !== ( $statement = $this->prepare($sql) )) { $statement->setFetchMode(\PDO::FETCH_ASSOC); $this->execute($statement, $parameters, false); - - return $statement; } - } + } catch (\Throwable $e) { throw new \PdoException($e->getMessage() . " `$sql` with data:" . json_encode($parameters)); } + + return $statement; } public function __destruct() diff --git a/src/Entity/EntityInterface.php b/src/Entity/EntityInterface.php index c935ba4..8d57c29 100644 --- a/src/Entity/EntityInterface.php +++ b/src/Entity/EntityInterface.php @@ -3,7 +3,7 @@ namespace Ulmus\Entity; use Ulmus\Common\{ EntityField, EntityResolver }; -use Ulmus\{ ConnectionAdapter, EntityCollection, Query\QueryBuilderInterface, Repository }; +use Ulmus\{ ConnectionAdapter, EntityCollection, QueryBuilder\QueryBuilderInterface, Repository }; interface EntityInterface /* extends \JsonSerializable */ { diff --git a/src/EntityTrait.php b/src/EntityTrait.php index 6cd0772..7ac223d 100644 --- a/src/EntityTrait.php +++ b/src/EntityTrait.php @@ -4,7 +4,7 @@ namespace Ulmus; use Notes\Attribute\Ignore; use Psr\Http\Message\ServerRequestInterface; -use Ulmus\{Common\EntityResolver, Common\EntityField, Entity\EntityInterface, Query\QueryBuilderInterface}; +use Ulmus\{Common\EntityResolver, Common\EntityField, Entity\EntityInterface, QueryBuilder\QueryBuilderInterface}; use Ulmus\SearchRequest\{Attribute\SearchParameter, SearchMethodEnum, SearchRequestInterface, @@ -348,7 +348,7 @@ trait EntityTrait { # public ? string $username = null; # #[SearchParameter(method: SearchMethodEnum::Where, toggle: true)] - # public ? string $hidden = null; + # public ? bool $hidden = null; # #[SearchParameter(method: SearchMethodEnum::Like)] # public ? string $word = null; diff --git a/src/Query/Fragment.php b/src/Query/Fragment.php index 0c55b2e..6ed2c36 100644 --- a/src/Query/Fragment.php +++ b/src/Query/Fragment.php @@ -2,11 +2,11 @@ namespace Ulmus\Query; -abstract class Fragment { +abstract class Fragment implements QueryFragmentInterface{ public int $order = 0; - public abstract function render() /*: mixed*/; + public abstract function render() : mixed; public function order() : float { diff --git a/src/Query/GroupBy.php b/src/Query/GroupBy.php index f1d31f9..0b20dd8 100644 --- a/src/Query/GroupBy.php +++ b/src/Query/GroupBy.php @@ -9,10 +9,10 @@ class GroupBy extends Fragment { const SQL_TOKEN = "GROUP BY"; - public function set(array $order) : self + public function set(array $fields) : self { - $this->groupBy = [ $order ]; - + $this->groupBy = $fields; + return $this; } diff --git a/src/Query/Having.php b/src/Query/Having.php index d584501..6b03fc0 100644 --- a/src/Query/Having.php +++ b/src/Query/Having.php @@ -2,8 +2,6 @@ namespace Ulmus\Query; -use Ulmus\MysqlQueryBuilder; - use Ulmus\Common\EntityField, Ulmus\Common\Sql; diff --git a/src/Query/Join.php b/src/Query/Join.php index 18b7c7b..bf48920 100644 --- a/src/Query/Join.php +++ b/src/Query/Join.php @@ -2,7 +2,6 @@ namespace Ulmus\Query; -use Ulmus\MysqlQueryBuilder; use Ulmus\Repository\ConditionTrait; class Join extends Fragment diff --git a/src/Query/QueryBuilderInterface.php b/src/Query/QueryBuilderInterface.php deleted file mode 100644 index 6ac6e7e..0000000 --- a/src/Query/QueryBuilderInterface.php +++ /dev/null @@ -1,15 +0,0 @@ -queryBuilder = $queryBuilder; } diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 4d37678..a20af9e 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -2,7 +2,7 @@ namespace Ulmus; -class QueryBuilder extends QueryBuilder\MysqlQueryBuilder +class QueryBuilder extends QueryBuilder\Sql\MysqlQueryBuilder { # Backward compatibility defaulting on MySQL/MariaDB query builder } \ No newline at end of file diff --git a/src/QueryBuilder/QueryBuilderInterface.php b/src/QueryBuilder/QueryBuilderInterface.php new file mode 100644 index 0000000..cbaa69f --- /dev/null +++ b/src/QueryBuilder/QueryBuilderInterface.php @@ -0,0 +1,15 @@ +where ?? null) && $this->where->parent ) { - # if an enclosure was opened, and nothing done, we must remove the unused node if ( empty($this->where->conditionList) && (count($this->where->parent->conditionList) === 1) ) { unset($this->where->parent->conditionList); @@ -198,22 +196,14 @@ class MysqlQueryBuilder extends SqlQueryBuilder public function where(string|\Stringable $field, mixed $value, string $operator = Query\Where::OPERATOR_EQUAL, string $condition = Query\Where::CONDITION_AND, bool $not = false) : self { - # Empty IN case - # if ( [] === $value ) { - # return $this; - # } - - if ( $this->where ?? false ) { - $where = $this->where; - } - elseif ( null === ( $where = $this->getFragment(Query\Where::class) ) ) { - $this->where = $where = new Query\Where($this); - $this->push($where); + if ( empty($this->where) && ! $this->getFragment(Query\Where::class) ) { + $this->where = new Query\Where($this); + $this->push($this->where); } $this->whereConditionOperator = $operator; - $where->add($field, $value, $operator, $condition, $not); + $this->where->add($field, $value, $operator, $condition, $not); return $this; } @@ -225,16 +215,13 @@ class MysqlQueryBuilder extends SqlQueryBuilder public function having(string|\Stringable $field, mixed $value, string $operator = Query\Where::OPERATOR_EQUAL, string $condition = Query\Where::CONDITION_AND, bool $not = false) : self { - if ( $this->having ?? false ) { - $having = $this->having; - } - elseif ( null === ( $having = $this->getFragment(Query\Having::class) ) ) { - $this->having = $having = new Query\Having($this); - $this->push($having); + if ( empty($this->having) && ! $this->getFragment(Query\Having::class) ) { + $this->having = new Query\Having($this); + $this->push($this->having); } $this->havingConditionOperator = $operator; - $having->add($field, $value, $operator, $condition, $not); + $this->having->add($field, $value, $operator, $condition, $not); return $this; } @@ -262,7 +249,7 @@ class MysqlQueryBuilder extends SqlQueryBuilder $this->push($offset); # A limit is required to match an offset - if ( null === $limit = $this->getFragment(Query\Limit::class) ) { + if ( null === $this->getFragment(Query\Limit::class) ) { $this->limit(\PHP_INT_MAX); } } @@ -284,14 +271,14 @@ class MysqlQueryBuilder extends SqlQueryBuilder return $this; } - public function groupBy(string|object $field, ? string $direction = null) : self + public function groupBy(string|object $field) : self { if ( null === $groupBy = $this->getFragment(Query\GroupBy::class) ) { $groupBy = new Query\GroupBy(); $this->push($groupBy); } - $groupBy->add($field, $direction); + $groupBy->add($field); return $this; } @@ -432,14 +419,14 @@ class MysqlQueryBuilder extends SqlQueryBuilder return $this; } - public function push(Query\Fragment $queryFragment) : self + public function push(QueryFragmentInterface $queryFragment) : self { $this->queryStack[] = $queryFragment; return $this; } - public function pull(Query\Fragment $queryFragment) : self + public function pull(QueryFragmentInterface $queryFragment) : self { return array_shift($this->queryStack); } @@ -469,7 +456,7 @@ class MysqlQueryBuilder extends SqlQueryBuilder unset($this->where, $this->having); } - public function getFragment(string $class, int $index = 0) : ? Query\Fragment + public function getFragment(string $class, int $index = 0) : ? QueryFragmentInterface { foreach($this->queryStack as $item) { if ( is_a($item, $class, true) ) { @@ -482,7 +469,7 @@ class MysqlQueryBuilder extends SqlQueryBuilder return null; } - public function removeFragment(Query\Fragment|array|\Stringable|string $fragment) : void + public function removeFragment(QueryFragmentInterface|array|\Stringable|string $fragment) : void { is_object($fragment) && $fragment = get_class($fragment); @@ -500,7 +487,7 @@ class MysqlQueryBuilder extends SqlQueryBuilder } } - public function getFragments(Query\Fragment|array|\Stringable|string $fragment = null) : array + public function getFragments(QueryFragmentInterface|array|\Stringable|string $fragment = null) : array { return $fragment !== null ? array_filter($this->queryStack, fn($e) => is_a($e, $fragment, true) ) : $this->queryStack; } diff --git a/src/QueryBuilder/Sql/SqliteQueryBuilder.php b/src/QueryBuilder/Sql/SqliteQueryBuilder.php index f989b0c..fe7cf5c 100644 --- a/src/QueryBuilder/Sql/SqliteQueryBuilder.php +++ b/src/QueryBuilder/Sql/SqliteQueryBuilder.php @@ -3,10 +3,9 @@ namespace Ulmus\QueryBuilder\Sql; use Ulmus\Query; -use Ulmus\QueryBuilder\SqlQueryBuilder; # Soon to extends SqlQueryBuilder -class SqliteQueryBuilder extends MysqlQueryBuilder implements Query\QueryBuilderInterface +class SqliteQueryBuilder extends MysqlQueryBuilder { public function pragma(/*object|Stringable*/ $name, $value = null, bool $callable = false) : self { diff --git a/src/QueryBuilder/SqlQueryBuilder.php b/src/QueryBuilder/SqlQueryBuilder.php index 987e4b5..13254bf 100644 --- a/src/QueryBuilder/SqlQueryBuilder.php +++ b/src/QueryBuilder/SqlQueryBuilder.php @@ -2,20 +2,18 @@ namespace Ulmus\QueryBuilder; -use Ulmus\Query; -use Ulmus\Query\Fragment; -use Ulmus\Query\QueryBuilderInterface; +use Ulmus\Query\QueryFragmentInterface; # TODO -> Extract from MysqlQueryBuilder to build an ISO/IEC 9075:2023 compatible layer for a basic SQL QueryBuilder -class SqlQueryBuilder implements Query\QueryBuilderInterface +class SqlQueryBuilder implements QueryBuilderInterface { - public function push(Fragment $queryFragment): Query\QueryBuilderInterface + public function push(QueryFragmentInterface $queryFragment): QueryBuilderInterface { // TODO: Implement push() method. } - public function pull(Fragment $queryFragment): Query\QueryBuilderInterface + public function pull(QueryFragmentInterface $queryFragment): QueryBuilderInterface { // TODO: Implement pull() method. } @@ -30,12 +28,12 @@ class SqlQueryBuilder implements Query\QueryBuilderInterface // TODO: Implement reset() method. } - public function getFragment(string $class, int $index = 0): ?Fragment + public function getFragment(string $class, int $index = 0): ?QueryFragmentInterface { // TODO: Implement getFragment() method. } - public function removeFragment(Fragment|\Stringable|array|string $fragment): void + public function removeFragment(QueryFragmentInterface|\Stringable|array|string $fragment): void { // TODO: Implement removeFragment() method. } diff --git a/src/Repository.php b/src/Repository.php index 7deb446..953b73a 100644 --- a/src/Repository.php +++ b/src/Repository.php @@ -29,7 +29,7 @@ class Repository public array $events = []; - protected Query\QueryBuilderInterface $queryBuilder; + protected QueryBuilder\QueryBuilderInterface $queryBuilder; protected EntityResolver $entityResolver; @@ -383,7 +383,7 @@ class Repository } } - public function removeQueryFragment(null|Query\Fragment|string|array $fragment) : self + public function removeQueryFragment(null|Query\QueryFragmentInterface|string|array $fragment) : self { foreach((array) $fragment as $item) { $this->queryBuilder->removeFragment($item); diff --git a/src/SearchRequest/SearchRequestFromRequestTrait.php b/src/SearchRequest/SearchRequestFromRequestTrait.php index 811439a..fda4aa4 100644 --- a/src/SearchRequest/SearchRequestFromRequestTrait.php +++ b/src/SearchRequest/SearchRequestFromRequestTrait.php @@ -46,38 +46,43 @@ trait SearchRequestFromRequestTrait $field = (string) $fieldName; - switch ($attribute->method) { - case SearchMethodEnum::Where: - $this->wheres[$field] = $this->$propertyName; - break; - - case SearchMethodEnum::Like: - $this->likes[$field] = "%{$this->$propertyName}%"; - break; - - case SearchMethodEnum::LikeLeft: - $this->likes[$field] = "%{$this->$propertyName}"; - break; - - case SearchMethodEnum::LikeRight: - $this->likes[$field] = "{$this->$propertyName}%"; - break; - - case SearchMethodEnum::OrderByAsc: - $this->orders[$field] = "ASC"; - break; - - case SearchMethodEnum::OrderByDesc: - $this->orders[$field] = "DESC"; - break; - - case SearchMethodEnum::GroupBy: - $this->groups[$field] = "DESC"; - break; - } + $this->parseAttributeMethod($attribute, $field, $propertyName); } } return $this; } + + protected function parseAttributeMethod(object $attribute, string $field, string $propertyName,) : void + { + switch ($attribute->method) { + case SearchMethodEnum::Where: + $this->wheres[$field] = $this->$propertyName; + break; + + case SearchMethodEnum::Like: + $this->likes[$field] = "%{$this->$propertyName}%"; + break; + + case SearchMethodEnum::LikeLeft: + $this->likes[$field] = "%{$this->$propertyName}"; + break; + + case SearchMethodEnum::LikeRight: + $this->likes[$field] = "{$this->$propertyName}%"; + break; + + case SearchMethodEnum::OrderByAsc: + $this->orders[$field] = "ASC"; + break; + + case SearchMethodEnum::OrderByDesc: + $this->orders[$field] = "DESC"; + break; + + case SearchMethodEnum::GroupBy: + $this->groups[$field] = "DESC"; + break; + } + } } \ No newline at end of file diff --git a/src/Ulmus.php b/src/Ulmus.php index 1e9527a..3753635 100644 --- a/src/Ulmus.php +++ b/src/Ulmus.php @@ -8,8 +8,10 @@ use Psr\SimpleCache\CacheInterface; abstract class Ulmus { + # @todo Remove, unused, now defined by the adapter (logically, right ?) public static string $repositoryClass = "\\Ulmus\\Repository"; + # @todo Remove, unused, now defined by the adapter (logically, right ?) public static string $queryBuilderClass = "\\Ulmus\\QueryBuilder"; public static array $registeredAdapters = []; @@ -22,19 +24,16 @@ abstract class Ulmus public static array $resolved = []; - public static function iterateQueryBuilder(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) : Generator + public static function iterateQueryBuilder(QueryBuilder\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) : Generator { - $rendered = $queryBuilder->render(); - $statement = ( $adapter ?: static::$defaultAdapter )->connector()->select($rendered, $queryBuilder->parameters ?? []); - - $i = 0; + $statement = ( $adapter ?: static::$defaultAdapter )->connector()->select($queryBuilder->render(), $queryBuilder->parameters ?? []); while ( $row = $statement->fetch() ) { yield $row; } $statement->closeCursor(); - + $queryBuilder->reset(); return [ @@ -42,13 +41,11 @@ abstract class Ulmus ]; } - public static function datasetQueryBuilder(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) : array + public static function datasetQueryBuilder(QueryBuilder\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) : array { $rows = []; - $rendered = $queryBuilder->render(); - - $statement = ( $adapter ?: static::$defaultAdapter )->connector()->select($rendered, $queryBuilder->parameters ?? []); + $statement = ( $adapter ?: static::$defaultAdapter )->connector()->select($queryBuilder->render(), $queryBuilder->parameters ?? []); while ( $row = $statement->fetch() ) { $rows[] = $row; @@ -66,7 +63,7 @@ abstract class Ulmus return ( $adapter ?: static::$defaultAdapter )->connector(); } - public static function runSelectQuery(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) + public static function runSelectQuery(QueryBuilder\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) { $dataset = static::connector($adapter)->select($queryBuilder->render(), array_merge($queryBuilder->values ?? [], $queryBuilder->parameters ?? [])); $queryBuilder->reset(); @@ -74,15 +71,16 @@ abstract class Ulmus return $dataset; } - public static function runQuery(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) + public static function runQuery(QueryBuilder\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) { $return = static::connector($adapter)->runQuery($queryBuilder->render(), array_merge($queryBuilder->values ?? [], $queryBuilder->parameters ?? [])); + $queryBuilder->reset(); return $return; } - public static function runInsertQuery(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) + public static function runInsertQuery(QueryBuilder\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) { $return = static::connector($adapter)->runInsertQuery($queryBuilder->render(),$queryBuilder->values ?? []); $queryBuilder->reset(); @@ -90,7 +88,7 @@ abstract class Ulmus return $return; } - public static function runUpdateQuery(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) + public static function runUpdateQuery(QueryBuilder\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) { $return = static::connector($adapter)->runUpdateQuery($queryBuilder->render(), array_merge($queryBuilder->values ?? [], $queryBuilder->parameters ?? [])); $queryBuilder->reset(); @@ -98,7 +96,7 @@ abstract class Ulmus return $return; } - public static function runDeleteQuery(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) + public static function runDeleteQuery(QueryBuilder\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) { $return = static::connector($adapter)->runDeleteQuery($queryBuilder->render(), array_merge($queryBuilder->values ?? [], $queryBuilder->parameters ?? [])); $queryBuilder->reset(); @@ -119,7 +117,7 @@ abstract class Ulmus return new $cls($entityClass, $alias, $adapter); } - public static function queryBuilder($entityClass, ...$arguments) : Query\QueryBuilderInterface + public static function queryBuilder($entityClass, ...$arguments) : QueryBuilder\QueryBuilderInterface { $cls = $entityClass::resolveEntity()->sqlAdapter()->adapter()->queryBuilderClass(); @@ -155,7 +153,7 @@ abstract class Ulmus static::$registeredAdapters[$adapter->name] = $adapter; } - protected static function fetchQueryBuilder(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) : array + protected static function fetchQueryBuilder(QueryBuilder\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) : array { $result = ( $adapter ?: static::$defaultAdapter )->connector->select($queryBuilder->render(), $queryBuilder->parameters ?? [])->fetchAll(); $queryBuilder->reset();