- Started working on decoupling QueryBuilder from SqlQueryBuilder

- WIP on SearchRequest attributes
- Began working on removing 'Ulmus' class
- WIP on removing SQL query building from Repository (which will become SqlRepository)
This commit is contained in:
Dave Mc Nicoll 2024-04-17 15:54:48 -04:00
parent 4571517dc8
commit 9fdebf454d
21 changed files with 114 additions and 127 deletions

View File

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

View File

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

View File

@ -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 */
{

View File

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

View File

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

View File

@ -9,9 +9,9 @@ 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;
}

View File

@ -2,8 +2,6 @@
namespace Ulmus\Query;
use Ulmus\MysqlQueryBuilder;
use Ulmus\Common\EntityField,
Ulmus\Common\Sql;

View File

@ -2,7 +2,6 @@
namespace Ulmus\Query;
use Ulmus\MysqlQueryBuilder;
use Ulmus\Repository\ConditionTrait;
class Join extends Fragment

View File

@ -1,15 +0,0 @@
<?php
namespace Ulmus\Query;
use Ulmus\Query;
interface QueryBuilderInterface
{
public function push(Fragment $queryFragment) : self;
public function pull(Fragment $queryFragment) : self;
public function render(bool $skipToken = false) /* mixed */;
public function reset() : void;
public function getFragment(string $class, int $index = 0) : ? Fragment;
public function removeFragment(Query\Fragment|array|\Stringable|string $fragment) : void;
}

View File

@ -0,0 +1,8 @@
<?php
namespace Ulmus\Query;
interface QueryFragmentInterface
{
public function render() : mixed;
}

View File

@ -2,8 +2,6 @@
namespace Ulmus\Query;
use Ulmus\MysqlQueryBuilder;
class Set extends Fragment {
public int $order = 0;

View File

@ -2,8 +2,6 @@
namespace Ulmus\Query;
use Ulmus\MysqlQueryBuilder;
class Values extends Fragment {
public int $order = 0;
@ -12,9 +10,9 @@ class Values extends Fragment {
public array $rows;
public MysqlQueryBuilder $queryBuilder;
public QueryBuilderInterface $queryBuilder;
public function __construct(MysqlQueryBuilder $queryBuilder)
public function __construct(QueryBuilderInterface $queryBuilder)
{
$this->queryBuilder = $queryBuilder;
}

View File

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

View File

@ -0,0 +1,15 @@
<?php
namespace Ulmus\QueryBuilder;
use Ulmus\Query\QueryFragmentInterface;
interface QueryBuilderInterface
{
public function push(QueryFragmentInterface $queryFragment) : self;
public function pull(QueryFragmentInterface $queryFragment) : self;
public function render(bool $skipToken = false) /* mixed */;
public function reset() : void;
public function getFragment(string $class, int $index = 0) : ? QueryFragmentInterface;
public function removeFragment(QueryFragmentInterface|array|\Stringable|string $fragment) : void;
}

View File

@ -3,10 +3,9 @@
namespace Ulmus\QueryBuilder\Sql;
use Ulmus\{ Query };
use Ulmus\QueryBuilder\SqlQueryBuilder;
# Soon to extends SqlQueryBuilder
class MssqlQueryBuilder extends MysqlQueryBuilder implements Query\QueryBuilderInterface
class MssqlQueryBuilder extends MysqlQueryBuilder
{
public function limit(int $value) : self
{

View File

@ -2,8 +2,7 @@
namespace Ulmus\QueryBuilder\Sql;
use Ulmus\{Query, Adapter, QueryBuilder\SqlQueryBuilder};
use Ulmus\Query\QueryBuilderInterface;
use Ulmus\{Query, Adapter, QueryBuilder\SqlQueryBuilder, QueryBuilder\QueryBuilderInterface, Query\QueryFragmentInterface, };
class MysqlQueryBuilder extends SqlQueryBuilder
{
@ -184,7 +183,6 @@ class MysqlQueryBuilder extends SqlQueryBuilder
public function close() : self
{
if ( null !== ($this->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;
}

View File

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

View File

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

View File

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

View File

@ -46,6 +46,15 @@ trait SearchRequestFromRequestTrait
$field = (string) $fieldName;
$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;
@ -76,8 +85,4 @@ trait SearchRequestFromRequestTrait
break;
}
}
}
return $this;
}
}

View File

@ -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,12 +24,9 @@ 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;
@ -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();