Merge remote-tracking branch 'origin' into dev-sql-escape
This commit is contained in:
commit
def8decb4e
|
@ -30,7 +30,11 @@ class Relation implements \Ulmus\Annotation\Annotation {
|
|||
}
|
||||
|
||||
public function entity() {
|
||||
$e = $this->entity;
|
||||
try {
|
||||
$e = $this->entity;
|
||||
} catch (\Throwable $ex) {
|
||||
throw new \Exception("Your @Relation annotation seems to be missing an `entity` entry.");
|
||||
}
|
||||
|
||||
return new $e();
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ class EntityField
|
|||
{
|
||||
$name = $this->entityResolver->searchFieldAnnotation($this->name, new Field() )->name ?? $this->name;
|
||||
|
||||
#return $useAlias ? "{$this->alias}.`{$this->name}`" : "`{$this->name}`"; <-SQL
|
||||
return $useAlias ? "{$this->alias}.\"{$name}\"" : "\"{$name}\"";
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,13 @@ class PdoObject extends PDO {
|
|||
return $statement;
|
||||
}
|
||||
} catch (\PDOException $e) {
|
||||
debogueur($sql, $parameters);
|
||||
throw $e;
|
||||
switch ( $e->getCode() ) {
|
||||
case 42000:
|
||||
throw new \PdoException($e->getMessage() . " `$sql` with data:" . json_encode($parameters));
|
||||
|
||||
default:
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,8 +32,13 @@ class PdoObject extends PDO {
|
|||
return $this->execute($statement, $parameters, true);
|
||||
}
|
||||
} catch (\PDOException $e) {
|
||||
debogueur($sql, $parameters);
|
||||
throw $e;
|
||||
switch ( $e->getCode() ) {
|
||||
case 42000:
|
||||
throw new \PdoException($e->getMessage() . " `$sql` with data:" . json_encode($parameters));
|
||||
|
||||
default:
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -196,8 +196,21 @@ trait EntityTrait {
|
|||
foreach($entityResolver->fieldList() as $key => $field) {
|
||||
$annotation = $entityResolver->searchFieldAnnotation($key, new Field() );
|
||||
|
||||
if ( isset($this->$key) ) {
|
||||
$dataset[ $annotation->name ?? $key ] = is_object($this->$key) ? Ulmus::convertObject($this->$key) : $this->$key;
|
||||
if ( isset($this->$key) ) {
|
||||
$realKey = $annotation->name ?? $key;
|
||||
|
||||
switch (true) {
|
||||
case is_object($this->$key):
|
||||
$dataset[$realKey] = Ulmus::convertObject($this->$key);
|
||||
break;
|
||||
|
||||
case is_array($this->$key):
|
||||
$dataset[$realKey] = Ulmus::encodeArray($this->$key);
|
||||
break;
|
||||
|
||||
default:
|
||||
$dataset[$realKey] = $this->$key;
|
||||
}
|
||||
}
|
||||
elseif ( $field['nullable'] ) {
|
||||
$dataset[ $annotation->name ?? $key ] = null;
|
||||
|
|
|
@ -10,6 +10,6 @@ abstract class Fragment {
|
|||
|
||||
protected function renderSegments(array $segments, string $glue = " ") : string
|
||||
{
|
||||
return implode($glue, array_filter($segments));
|
||||
return implode($glue, array_filter($segments, function($i) { return ! is_null($i) && $i !== false && $i !== ""; }));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,6 @@ class Insert extends Fragment {
|
|||
|
||||
protected function renderTable() : string
|
||||
{
|
||||
return $this->table;
|
||||
return "`$this->table`";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,12 @@ class Limit extends Fragment {
|
|||
|
||||
public function render() : string
|
||||
{
|
||||
if ( $this->limit < 0 ) {
|
||||
throw new \Exception("An error occured trying to render the LIMIT fragment ; given value has to be > 0. Received {$this->limit}");
|
||||
}
|
||||
|
||||
return $this->renderSegments([
|
||||
static::SQL_TOKEN, $this->limit
|
||||
static::SQL_TOKEN, abs($this->limit)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,16 @@ class Offset extends Fragment {
|
|||
public function set($offset) : self
|
||||
{
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render() : string
|
||||
{
|
||||
if ( $this->offset < 0 ) {
|
||||
throw new \Exception("An error occured trying to render the OFFSET fragment ; given value has to be > 0. Received {$this->offset}");
|
||||
}
|
||||
|
||||
return $this->renderSegments([
|
||||
static::SQL_TOKEN, $this->offset,
|
||||
]);
|
||||
|
|
|
@ -12,12 +12,14 @@ class OrderBy extends Fragment {
|
|||
public function set(array $order) : self
|
||||
{
|
||||
$this->orderBy = $order;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function add(string $field, ? string $direction = null) : self
|
||||
{
|
||||
$this->orderBy[] = [ $field, $direction ];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -25,6 +27,7 @@ class OrderBy extends Fragment {
|
|||
{
|
||||
$list = array_map(function($item) {
|
||||
list($field, $direction) = $item;
|
||||
|
||||
return $field . ( $direction ? " $direction" : "" );
|
||||
}, $this->orderBy);
|
||||
|
||||
|
|
|
@ -24,12 +24,12 @@ class Update extends Fragment {
|
|||
'UPDATE',
|
||||
( $this->priority ?? false ),
|
||||
( $this->ignore ? 'IGNORE' : false ),
|
||||
$this->table,
|
||||
$this->renderTable(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function renderTable() : string
|
||||
{
|
||||
return $this->table;
|
||||
return "`$this->table`";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class Values extends Fragment {
|
|||
public function render() : string
|
||||
{
|
||||
$this->queryBuilder->addValues($this->flattenRowsArray());
|
||||
|
||||
|
||||
return $this->renderSegments([
|
||||
'VALUES', $this->renderParameterPlaceholders(),
|
||||
]);
|
||||
|
|
|
@ -53,7 +53,7 @@ class Where extends Fragment {
|
|||
public function render() : string
|
||||
{
|
||||
$stack = [];
|
||||
|
||||
|
||||
foreach ($this->conditionList ?? [] as $key => $item) {
|
||||
if ( $item instanceof Where ) {
|
||||
if ( $item->conditionList ?? false ) {
|
||||
|
|
|
@ -143,7 +143,13 @@ class QueryBuilder
|
|||
|
||||
public function open(string $condition = Query\Where::CONDITION_AND) : self
|
||||
{
|
||||
if ( null !== ($this->where ?? false) ) {
|
||||
if ( null !== ($this->where ?? null) ) {
|
||||
$this->where->conditionList[] = $new = new Query\Where($this, $condition);
|
||||
$this->where = $new;
|
||||
}
|
||||
else {
|
||||
$this->where = new Query\Where($this, $condition);
|
||||
$this->push($this->where);
|
||||
$this->where->conditionList[] = $new = new Query\Where($this, $condition);
|
||||
$this->where = $new;
|
||||
}
|
||||
|
@ -153,7 +159,13 @@ class QueryBuilder
|
|||
|
||||
public function close() : self
|
||||
{
|
||||
if ( null !== ($this->where ?? false) && $this->where->parent ) {
|
||||
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);
|
||||
}
|
||||
|
||||
$this->where = $this->where->parent;
|
||||
}
|
||||
|
||||
|
@ -171,6 +183,7 @@ class QueryBuilder
|
|||
}
|
||||
|
||||
$this->whereConditionOperator = $operator;
|
||||
|
||||
$where->add($field, $value, $operator, $condition, $not);
|
||||
|
||||
return $this;
|
||||
|
@ -200,7 +213,6 @@ class QueryBuilder
|
|||
|
||||
public function groupBy() : self
|
||||
{
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -242,8 +254,6 @@ class QueryBuilder
|
|||
|
||||
public function join(string $type, /*string | QueryBuilder*/ $table, $field, $value, bool $outer = false) : self
|
||||
{
|
||||
#if ( null === $join = $this->getFragment(Query\Join::class) ) {#}
|
||||
|
||||
$join = new Query\Join($this);
|
||||
$this->push($join);
|
||||
$join->set($type, $table, $field, $value);
|
||||
|
|
|
@ -214,6 +214,32 @@ class Repository
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function wheres(array $fieldValues, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
foreach($fieldValues as $field => $value) {
|
||||
if ( is_array($value) ) {
|
||||
switch ($value[1]) {
|
||||
case Query\Where::CONDITION_AND:
|
||||
$this->where($field, $value[0], $operator);
|
||||
break;
|
||||
|
||||
case Query\Where::CONDITION_OR:
|
||||
$this->or($field, $value[0], $operator);
|
||||
break;
|
||||
|
||||
case Query\Where::CONDITION_NOT:
|
||||
$this->notWhere($field, $value[0], $operator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->where($field, $value, $operator);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function and($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
return $this->where($field, $value, $operator);
|
||||
|
@ -233,9 +259,9 @@ class Repository
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function orNot($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
public function orNot($field, $value, string $operator = Query\Where::OPERATOR_NOT_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_OR, true);
|
||||
$this->queryBuilder->notWhere($field, $value, $operator, Query\Where::CONDITION_OR, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -296,18 +322,51 @@ class Repository
|
|||
|
||||
public function like($field, $value) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, Query\Where::OPERATOR_LIKE, Query\Where::CONDITION_AND);
|
||||
$this->where($field, $value, Query\Where::OPERATOR_LIKE);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orLike($field, $value) : self
|
||||
{
|
||||
$this->or($field, $value, Query\Where::OPERATOR_LIKE);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function notLike($field, $value) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, Query\Where::OPERATOR_LIKE, Query\Where::CONDITION_AND, true);
|
||||
$this->notWhere($field, $value, Query\Where::OPERATOR_LIKE);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function likes(array $fieldValues, string $condition = Query\Where::CONDITION_AND) : self
|
||||
{
|
||||
foreach($fieldValues as $field => $value) {
|
||||
if ( is_array($value) ) {
|
||||
switch ($value[1]) {
|
||||
case Query\Where::CONDITION_AND:
|
||||
$this->like($field, $value[0]);
|
||||
break;
|
||||
|
||||
case Query\Where::CONDITION_OR:
|
||||
$this->orLike($field, $value[0]);
|
||||
break;
|
||||
|
||||
case Query\Where::CONDITION_NOT:
|
||||
$this->notLike($field, $value[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->like($field, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function match() : self
|
||||
{
|
||||
|
||||
|
@ -330,10 +389,15 @@ class Repository
|
|||
|
||||
public function groupBy() : self
|
||||
{
|
||||
#$this->queryBuilder->groupBy();
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function groups(array $groups) : self
|
||||
{
|
||||
# foreach($this->groups)
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orderBy($field, ? string $direction = null) : self
|
||||
{
|
||||
$this->queryBuilder->orderBy($field, $direction);
|
||||
|
@ -341,6 +405,16 @@ class Repository
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function orders(array $orderList) : self
|
||||
{
|
||||
foreach($orderList as $field => $direction) {
|
||||
$this->queryBuilder->orderBy($field, $direction);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function limit(int $value) : self
|
||||
{
|
||||
$this->queryBuilder->limit($value);
|
||||
|
@ -375,7 +449,25 @@ class Repository
|
|||
|
||||
return $this->where($primaryKeyField[$pkField]->name ?? $pkField, $value);
|
||||
}
|
||||
|
||||
|
||||
public function filterServerRequest(SearchRequest\SearchRequestInterface $searchRequest) : self
|
||||
{
|
||||
$searchRequest->count = $searchRequest->filter( clone $this )
|
||||
->wheres($searchRequest->wheres(), Query\Where::OPERATOR_EQUAL, Query\Where::CONDITION_AND)
|
||||
->likes($searchRequest->likes(), Query\Where::CONDITION_OR)
|
||||
->orders($searchRequest->orders())
|
||||
->groups($searchRequest->groups())
|
||||
->count();
|
||||
|
||||
return $searchRequest->filter($this)
|
||||
->wheres($searchRequest->wheres(), Query\Where::OPERATOR_EQUAL, Query\Where::CONDITION_AND)
|
||||
->likes($searchRequest->likes(), Query\Where::CONDITION_OR)
|
||||
->orders($searchRequest->orders())
|
||||
->groups($searchRequest->groups())
|
||||
->offset($searchRequest->offset())
|
||||
->limit($searchRequest->limit());
|
||||
}
|
||||
|
||||
public function collectionFromQuery(? string $entityClass = null) : EntityCollection
|
||||
{
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\SearchRequest;
|
||||
|
||||
use Ulmus\Repository;
|
||||
|
||||
interface SearchRequestInterface {
|
||||
public function filter(Repository $repository) : Repository;
|
||||
|
||||
public function wheres() : iterable;
|
||||
|
||||
public function likes() : iterable;
|
||||
|
||||
public function orders() : iterable;
|
||||
|
||||
public function groups() : iterable;
|
||||
|
||||
public function limit() : int;
|
||||
|
||||
public function offset() : int;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\SearchRequest;
|
||||
|
||||
trait SearchRequestPaginationTrait {
|
||||
|
||||
public int $count = 0;
|
||||
|
||||
public int $page = 1;
|
||||
|
||||
public int $pageCount = 0;
|
||||
|
||||
public int $limit = 25;
|
||||
|
||||
public function limit(): int
|
||||
{
|
||||
return $this->limit;
|
||||
}
|
||||
|
||||
public function offset(): int
|
||||
{
|
||||
return abs( ( $this->page - 1 ) * $this->limit() );
|
||||
}
|
||||
|
||||
public function pagination(int $page, int $itemCount) : void
|
||||
{
|
||||
$this->count = $itemCount;
|
||||
$this->page = $page;
|
||||
}
|
||||
|
||||
public function pageCount() : int
|
||||
{
|
||||
return ceil($this->count / $this->limit());
|
||||
}
|
||||
|
||||
public function hasPagination() : int
|
||||
{
|
||||
return $this->pageCount() > 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\SearchRequest;
|
||||
|
||||
use \Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
interface SearchableInterface {
|
||||
|
||||
public static function searchRequest(...$arguments) : SearchRequestInterface;
|
||||
|
||||
}
|
|
@ -29,6 +29,7 @@ abstract class Ulmus
|
|||
}
|
||||
|
||||
$statement->closeCursor();
|
||||
|
||||
$queryBuilder->reset();
|
||||
|
||||
return [
|
||||
|
@ -70,12 +71,17 @@ abstract class Ulmus
|
|||
{
|
||||
return ( static::$objectInstanciator ?? static::$objectInstanciator = new Entity\ObjectInstanciator() )->instanciate($type, $arguments);
|
||||
}
|
||||
|
||||
|
||||
public static function convertObject(object $obj)
|
||||
{
|
||||
return ( static::$objectInstanciator ?? static::$objectInstanciator = new Entity\ObjectInstanciator() )->convert($obj);
|
||||
}
|
||||
|
||||
public static function encodeArray(array $array)
|
||||
{
|
||||
return json_encode($array);
|
||||
}
|
||||
|
||||
public static function registerAdapter(ConnectionAdapter $adapter, bool $default = false) : void
|
||||
{
|
||||
if ($default) {
|
||||
|
|
Loading…
Reference in New Issue