- A lot of bugfixes and fitting to accomodate ldap adapter
This commit is contained in:
parent
f14347f01d
commit
16d76470da
|
@ -16,4 +16,5 @@ interface AdapterInterface {
|
|||
public function setup(array $configuration) : void;
|
||||
public function escapeIdentifier(string $segment, int $type) : string;
|
||||
public function defaultEngine() : ? string;
|
||||
public function writableValue(/* mixed */ $value) /*: mixed*/;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Ulmus\Adapter;
|
|||
use Ulmus\Common\PdoObject;
|
||||
|
||||
use Ulmus\Exception\AdapterConfigurationException;
|
||||
use Ulmus\Ulmus;
|
||||
|
||||
class MsSQL implements AdapterInterface {
|
||||
|
||||
|
@ -93,7 +94,7 @@ class MsSQL implements AdapterInterface {
|
|||
$parts[] = "Encrypt=1";
|
||||
}
|
||||
|
||||
if ( $this->failoverPartner ?? false ) {
|
||||
if ( $this->failoverPartner ?? false ) {
|
||||
$parts[] = "Failover_Partner={$this->failoverPartner}";
|
||||
}
|
||||
|
||||
|
@ -200,8 +201,25 @@ class MsSQL implements AdapterInterface {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public function writableValue(/* mixed */ $value) /*: mixed*/
|
||||
{
|
||||
switch (true) {
|
||||
case is_object($value):
|
||||
return Ulmus::convertObject($value);
|
||||
|
||||
case is_array($value):
|
||||
return json_encode($array);
|
||||
|
||||
case is_bool($value):
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function defaultEngine(): ? string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Ulmus\Adapter;
|
|||
use Ulmus\Common\PdoObject;
|
||||
|
||||
use Ulmus\Exception\AdapterConfigurationException;
|
||||
use Ulmus\Ulmus;
|
||||
|
||||
class MySQL implements AdapterInterface {
|
||||
|
||||
|
@ -137,6 +138,22 @@ class MySQL implements AdapterInterface {
|
|||
}
|
||||
}
|
||||
|
||||
public function writableValue(/* mixed */ $value) /*: mixed*/
|
||||
{
|
||||
switch (true) {
|
||||
case is_object($value):
|
||||
return Ulmus::convertObject($value);
|
||||
|
||||
case is_array($value):
|
||||
return json_encode($value);
|
||||
|
||||
case is_bool($value):
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function defaultEngine(): ? string
|
||||
{
|
||||
return "InnoDB";
|
||||
|
|
|
@ -4,4 +4,6 @@ namespace Ulmus\Annotation\Property;
|
|||
|
||||
class Virtual extends Field {
|
||||
public Closure $closure;
|
||||
|
||||
public bool $readonly = true;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ class PdoObject extends PDO {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function runQuery(string $sql, array $parameters = []): ? PDOStatement
|
||||
{
|
||||
static::$dump && call_user_func_array(static::$dump, [ $sql, $parameters ]);
|
||||
|
@ -44,14 +47,14 @@ class PdoObject extends PDO {
|
|||
return null;
|
||||
}
|
||||
|
||||
public function runInsertQuery(array $filter, array $dataset)
|
||||
public function runInsertQuery(string $sql, array $parameters = [])
|
||||
{
|
||||
return $this->runQuery($filter, $dataset);
|
||||
return $this->runQuery($sql, $parameters);
|
||||
}
|
||||
|
||||
public function runUpdateQuery(array $filter, array $dataset)
|
||||
public function runUpdateQuery(string $sql, array $parameters = [])
|
||||
{
|
||||
return $this->runQuery($filter, $dataset);
|
||||
return $this->runQuery($sql, $parameters);
|
||||
}
|
||||
|
||||
public function runDeleteQuery(string $sql, array $parameters = []): ? PDOStatement
|
||||
|
|
|
@ -143,27 +143,10 @@ trait EntityTrait {
|
|||
$annotation = $entityResolver->searchFieldAnnotation($key, new Field() );
|
||||
|
||||
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;
|
||||
|
||||
case is_bool($this->$key):
|
||||
$dataset[$realKey] = (int) $this->$key;
|
||||
break;
|
||||
|
||||
default:
|
||||
$dataset[$realKey] = $this->$key;
|
||||
}
|
||||
$dataset[$annotation->name ?? $key] = static::repository()->adapter->adapter()->writableValue($this->$key);
|
||||
}
|
||||
elseif ( $field['nullable'] ) {
|
||||
$dataset[ $annotation->name ?? $key ] = null;
|
||||
$dataset[$annotation->name ?? $key] = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,9 +180,11 @@ trait EntityTrait {
|
|||
/**
|
||||
* @Ignore
|
||||
*/
|
||||
public function toArray($includeRelations = false) : array
|
||||
public function toArray($includeRelations = false, array $filterFields = null) : array
|
||||
{
|
||||
return $this->entityGetDataset($includeRelations);
|
||||
$dataset = $this->entityGetDataset($includeRelations);
|
||||
|
||||
return $filterFields ? array_intersect_key($dataset, array_flip($filterFields)) : $dataset;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -109,6 +109,7 @@ class FieldDefinition {
|
|||
return implode(' ', array_filter([
|
||||
$this->isUnsigned() ? "UNSIGNED" : null,
|
||||
$this->nullable ? "NULL" : "NOT NULL",
|
||||
$this->update ? "ON UPDATE {$this->update}" : null,
|
||||
$default ? "DEFAULT $default" : null,
|
||||
$this->isAutoIncrement() ? "AUTO_INCREMENT" : null,
|
||||
$this->isPrimaryKey() ? "PRIMARY KEY" : null,
|
||||
|
|
|
@ -8,6 +8,11 @@ abstract class Fragment {
|
|||
|
||||
public abstract function render() /*: mixed*/;
|
||||
|
||||
public function order() : float
|
||||
{
|
||||
return (float) $this->order;
|
||||
}
|
||||
|
||||
protected function renderSegments(array $segments, string $glue = " ") : string
|
||||
{
|
||||
return implode($glue, array_filter($segments, function($i) { return ! is_null($i) && $i !== false && $i !== ""; }));
|
||||
|
|
|
@ -5,10 +5,10 @@ namespace Ulmus\Query;
|
|||
use Ulmus\QueryBuilder;
|
||||
use Ulmus\Repository\ConditionTrait;
|
||||
|
||||
class Join extends Fragment
|
||||
class Join extends Fragment
|
||||
{
|
||||
use ConditionTrait;
|
||||
|
||||
|
||||
const SQL_OUTER = "OUTER";
|
||||
const SQL_TOKEN = "JOIN";
|
||||
|
||||
|
@ -20,32 +20,35 @@ class Join extends Fragment
|
|||
const TYPE_NATURAL = "NATURAL";
|
||||
|
||||
public bool $outer = false;
|
||||
|
||||
|
||||
public array $joins = [];
|
||||
|
||||
|
||||
public string $attachment = "ON";
|
||||
|
||||
|
||||
public string $side;
|
||||
|
||||
|
||||
public /*string|QueryBuilder*/ $table;
|
||||
|
||||
|
||||
public ? string $alias;
|
||||
|
||||
|
||||
public string $field;
|
||||
|
||||
|
||||
public /*string|QueryBuilder*/ $value;
|
||||
|
||||
|
||||
public /* QueryBuilder */ $queryBuilder;
|
||||
|
||||
public int $order = 40;
|
||||
|
||||
public function __construct(QueryBuilderInterface $queryBuilder)
|
||||
public int $joinOrder = 0;
|
||||
|
||||
public function __construct(QueryBuilderInterface $queryBuilder)
|
||||
{
|
||||
$this->queryBuilder = new QueryBuilder();
|
||||
$cls = get_class($queryBuilder);
|
||||
$this->queryBuilder = new $cls();
|
||||
$this->queryBuilder->parent = $queryBuilder;
|
||||
}
|
||||
|
||||
public function set(string $side, /* QueryBuilder|string */ $table, string $field, /* QueryBuilder|string */ $value)
|
||||
|
||||
public function set(string $side, /* QueryBuilder|string */ $table, string $field, /* QueryBuilder|string */ $value)
|
||||
{
|
||||
$this->side = $side;
|
||||
$this->table = $table;
|
||||
|
@ -53,9 +56,14 @@ class Join extends Fragment
|
|||
$this->where($this->field = $field, $this->value = $value);
|
||||
}
|
||||
|
||||
public function order() : float
|
||||
{
|
||||
return $this->order + ( $this->joinOrder / 100 );
|
||||
}
|
||||
|
||||
public function render() : string
|
||||
{
|
||||
return $this->renderSegments([
|
||||
return $this->renderSegments([
|
||||
strtoupper($this->side), $this->outer ? static::SQL_OUTER : "", static::SQL_TOKEN, $this->table, $this->alias ?? "", $this->attachment, $this->queryBuilder->render(true) ?? ""
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -9,45 +9,45 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
public Query\Where $where;
|
||||
|
||||
public Query\Having $having;
|
||||
|
||||
|
||||
public QueryBuilderInterface $parent;
|
||||
|
||||
|
||||
/**
|
||||
* Those are the parameters we are going to bind to PDO.
|
||||
*/
|
||||
public array $parameters = [];
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Those values are to be inserted or updated
|
||||
*/
|
||||
public array $values = [];
|
||||
|
||||
|
||||
public string $whereConditionOperator = Query\Where::CONDITION_AND;
|
||||
|
||||
|
||||
public string $havingConditionOperator = Query\Where::CONDITION_AND;
|
||||
|
||||
|
||||
protected int $parameterIndex = 0;
|
||||
|
||||
protected array $queryStack = [];
|
||||
|
||||
public function __clone()
|
||||
public function __clone()
|
||||
{
|
||||
if ($this->where ?? false) {
|
||||
#$this->where = clone $this->where;
|
||||
#$this->where->queryBuilder = $this;
|
||||
}
|
||||
|
||||
|
||||
if ($this->having ?? false) {
|
||||
#$this->having = clone $this->having;
|
||||
#$this->having->queryBuiler = $this;
|
||||
}
|
||||
|
||||
|
||||
if ($this->parent ?? false) {
|
||||
#$this->parent = clone $this->parent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function select($field, bool $distinct = false) : self
|
||||
{
|
||||
if ( null !== ( $select = $this->getFragment(Query\Select::class) ) ) {
|
||||
|
@ -63,18 +63,18 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function insert(array $fieldlist, string $table, ? string $alias = null, ? string $database = null, ? string $schema = null, bool $replace = false) : self
|
||||
{
|
||||
if ( null === $this->getFragment(Query\Insert::class) ) {
|
||||
if ( null === $this->getFragment(Query\Insert::class) ) {
|
||||
if ( $schema ) {
|
||||
$table = "$schema.$table";
|
||||
}
|
||||
|
||||
|
||||
if ( $database ) {
|
||||
$table = "$database.$table";
|
||||
}
|
||||
|
||||
|
||||
$insert = new Query\Insert();
|
||||
$this->push($insert);
|
||||
|
||||
|
@ -87,32 +87,32 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function values(array $dataset) : self
|
||||
public function values(array $dataset) : self
|
||||
{
|
||||
if ( null === ( $values = $this->getFragment(Query\Values::class) ) ) {
|
||||
$values = new Query\Values($this);
|
||||
$this->push($values);
|
||||
}
|
||||
|
||||
|
||||
$values->add($dataset);
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function update(string $table, ? string $alias = null, ? string $database = null, ? string $schema = null) : self
|
||||
{
|
||||
if ( ! $this->getFragment(Query\Update::class) ) {
|
||||
if ( $schema ) {
|
||||
$table = "$schema.$table";
|
||||
}
|
||||
|
||||
|
||||
if ( $database ) {
|
||||
$table = "$database.$table";
|
||||
}
|
||||
|
||||
$update = new Query\Update();
|
||||
$this->push($update);
|
||||
|
||||
|
||||
$update->alias = $alias;
|
||||
$update->table = $table;
|
||||
}
|
||||
|
@ -122,17 +122,16 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
|
||||
public function set(array $dataset, ? array $escapedFields = null) : self
|
||||
{
|
||||
|
||||
if ( null === ( $set = $this->getFragment(Query\Set::class) ) ) {
|
||||
$set = new Query\Set($this);
|
||||
$this->push($set);
|
||||
}
|
||||
|
||||
|
||||
$set->set($dataset, $escapedFields);
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function delete() : self
|
||||
{
|
||||
if ( ! $this->getFragment(Query\Delete::class) ) {
|
||||
|
@ -147,7 +146,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
if ( $schema ) {
|
||||
$table = "$schema.$table";
|
||||
}
|
||||
|
||||
|
||||
if ( $database ) {
|
||||
$table = "$database.$table";
|
||||
}
|
||||
|
@ -158,7 +157,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
else {
|
||||
$from = new Query\From($this);
|
||||
$this->push($from);
|
||||
|
||||
|
||||
$from->set($alias ? [ $alias => $table ] : [ $table ]);
|
||||
}
|
||||
|
||||
|
@ -184,12 +183,12 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
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);
|
||||
}
|
||||
|
||||
|
||||
$this->where = $this->where->parent;
|
||||
}
|
||||
|
||||
|
@ -202,7 +201,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
if ( [] === $value ) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
if ( $this->where ?? false ) {
|
||||
$where = $this->where;
|
||||
}
|
||||
|
@ -214,7 +213,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
$this->whereConditionOperator = $operator;
|
||||
|
||||
$where->add($field, $value, $operator, $condition, $not);
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -235,10 +234,10 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
|
||||
$this->havingConditionOperator = $operator;
|
||||
$having->add($field, $value, $operator, $condition, $not);
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function limit(int $value) : self
|
||||
{
|
||||
if ( null === $limit = $this->getFragment(Query\Limit::class) ) {
|
||||
|
@ -247,7 +246,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
}
|
||||
|
||||
$limit->set($value);
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -259,7 +258,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
}
|
||||
|
||||
$offset->set($value);
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -271,7 +270,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
}
|
||||
|
||||
$orderBy->add($field, $direction);
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -283,48 +282,50 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
}
|
||||
|
||||
$groupBy->add($field, $direction);
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function join(string $type, /*string | QueryBuilder*/ $table, $field, $value, bool $outer = false, ? string $alias = null) : self
|
||||
{
|
||||
$this->withJoin(...func_get_args());
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withJoin(string $type, $table, $field, $value, bool $outer = false, ? string $alias = null) : Query\Join
|
||||
{
|
||||
$join = new Query\Join($this);
|
||||
|
||||
|
||||
$this->push($join);
|
||||
|
||||
|
||||
$join->set($type, $table, $field, $value);
|
||||
|
||||
|
||||
$join->outer = $outer;
|
||||
|
||||
|
||||
$join->alias = $alias;
|
||||
|
||||
|
||||
$join->joinOrder = $this->nextJoinOrder();
|
||||
|
||||
return $join;
|
||||
}
|
||||
|
||||
|
||||
public function truncate(string $table, ? string $alias = null, ? string $database = null, ? string $schema = null) : self
|
||||
{
|
||||
if ( $schema ) {
|
||||
$table = "$schema.$table";
|
||||
}
|
||||
|
||||
|
||||
if ( $database ) {
|
||||
$table = "$database.$table";
|
||||
}
|
||||
|
||||
$truncate = new Query\Truncate($this);
|
||||
|
||||
|
||||
$this->push($truncate);
|
||||
|
||||
$truncate->set($table);
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -334,7 +335,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
if ( $schema ) {
|
||||
$table = "$schema.$table";
|
||||
}
|
||||
|
||||
|
||||
if ( $database ) {
|
||||
$table = "$database.$table";
|
||||
}
|
||||
|
@ -387,14 +388,14 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function push(Query\Fragment $queryFragment) : self
|
||||
{
|
||||
$this->queryStack[] = $queryFragment;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function pull(Query\Fragment $queryFragment) : self
|
||||
{
|
||||
return array_shift($this->queryStack);
|
||||
|
@ -405,7 +406,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
$sql = [];
|
||||
|
||||
usort($this->queryStack, function($q1, $q2) {
|
||||
return $q1->order <=> $q2->order;
|
||||
return $q1->order() <=> $q2->order();
|
||||
});
|
||||
|
||||
foreach($this->queryStack as $fragment) {
|
||||
|
@ -414,7 +415,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
|
||||
return implode(" ", $sql);
|
||||
}
|
||||
|
||||
|
||||
public function reset() : void
|
||||
{
|
||||
$this->parameters = $this->values = $this->queryStack = [];
|
||||
|
@ -456,33 +457,44 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
}
|
||||
}
|
||||
|
||||
public function getFragments() : array
|
||||
public function getFragments(/*? Query\Fragment|array*/ $fragment = null) : array
|
||||
{
|
||||
return $this->queryStack;
|
||||
return $fragment === null ? array_filter($this->queryStack, fn($e) => get_class($e) === $fragment) : $this->queryStack;
|
||||
}
|
||||
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
public function addParameter($value, string $key = null) : string
|
||||
public function addParameter($value, string $key = null) : string
|
||||
{
|
||||
if ( $this->parent ?? false ) {
|
||||
return $this->parent->addParameter($value, $key);
|
||||
}
|
||||
|
||||
|
||||
if ( $key === null ) {
|
||||
$key = ":p" . $this->parameterIndex++;
|
||||
}
|
||||
|
||||
$this->parameters[$key] = $value;
|
||||
$this->parameters[$key] = $value;
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
|
||||
public function addValues(array $values) : void
|
||||
{
|
||||
$this->values = $values;
|
||||
}
|
||||
|
||||
protected function nextJoinOrder() : float
|
||||
{
|
||||
$next = 0;
|
||||
|
||||
foreach($this->getFragments(Query\Join::class) as $join) {
|
||||
$next = max($next, $join->joinOrder);
|
||||
}
|
||||
|
||||
return $next + 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use Ulmus\Common\EntityResolver;
|
|||
|
||||
class Repository
|
||||
{
|
||||
use EventTrait, Repository\ConditionTrait;
|
||||
use EventTrait, Repository\ConditionTrait, Repository\EscapeTrait;
|
||||
|
||||
const DEFAULT_ALIAS = "this";
|
||||
|
||||
|
@ -30,7 +30,7 @@ class Repository
|
|||
$this->adapter = $adapter ?? $this->entityResolver->databaseAdapter();
|
||||
$this->queryBuilder = new QueryBuilder();
|
||||
}
|
||||
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
#$this->queryBuilder = clone $this->queryBuilder;
|
||||
|
@ -74,7 +74,7 @@ class Repository
|
|||
$this->select( "DISTINCT COUNT(*) OVER ()" );
|
||||
}
|
||||
else {
|
||||
$this->select(Common\Sql::function("COUNT", "*"));
|
||||
$this->select(Common\Sql::function("COUNT", '*'));
|
||||
}
|
||||
|
||||
$this->selectSqlQuery();
|
||||
|
@ -144,13 +144,13 @@ class Repository
|
|||
|
||||
$primaryKeyDefinition = Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField();
|
||||
|
||||
if ( ! $entity->isLoaded() ) {
|
||||
if ( $replace || ! $entity->isLoaded() ) {
|
||||
# $dataset = array_filter($dataset, fn($item, $field) => ! ($this->entityResolver->searchFieldAnnotation($field, new Field, false)->readonly ?? false), \ARRAY_FILTER_USE_BOTH);
|
||||
|
||||
$statement = $this->insertSqlQuery($fieldsAndValue ?? $dataset, $replace)->runInsertQuery();
|
||||
|
||||
if ( ( 0 !== $statement->lastInsertId ) &&
|
||||
( null !== $primaryKeyDefinition )) {
|
||||
|
||||
if ( ( 0 !== $statement->lastInsertId ) &&
|
||||
( null !== $primaryKeyDefinition )) {
|
||||
|
||||
$pkField = key($primaryKeyDefinition);
|
||||
$dataset[$pkField] = $statement->lastInsertId;
|
||||
|
@ -192,65 +192,64 @@ class Repository
|
|||
}
|
||||
|
||||
return $changed;
|
||||
|
||||
}
|
||||
|
||||
public function loadCollectionRelation(EntityCollection $collection, /*array|string*/ $fields) : void
|
||||
public function insertAll(/* EntityCollection | array */ $collection, int $size = 1000) : int
|
||||
{
|
||||
foreach ((array)$fields as $name) {
|
||||
if (null !== ($relation = $this->entityResolver->searchFieldAnnotation($name, new Annotation\Property\Relation()))) {
|
||||
$relationType = strtolower(str_replace(['-', '_', ' '], '', $relation->type));
|
||||
if ( empty($collection) ) {
|
||||
return 0;
|
||||
}
|
||||
elseif ( is_array($collection) ) {
|
||||
$collection = $this->entityClass::entityCollection($collection);
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
foreach($collection as $entity) {
|
||||
if ( ! $this->matchEntity($entity) ) {
|
||||
throw new \Exception("Your entity class `" . get_class($entity) . "` cannot match entity type of repository `{$this->entityClass}`");
|
||||
}
|
||||
}
|
||||
|
||||
$dataset = $entity->toArray();
|
||||
|
||||
$primaryKeyDefinition = Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField();
|
||||
|
||||
if ( $replace || ! $entity->isLoaded() ) {
|
||||
# $dataset = array_filter($dataset, fn($item, $field) => ! ($this->entityResolver->searchFieldAnnotation($field, new Field, false)->readonly ?? false), \ARRAY_FILTER_USE_BOTH);
|
||||
|
||||
$statement = $this->insertSqlQuery($fieldsAndValue ?? $dataset, $replace)->runInsertQuery();
|
||||
|
||||
if ( ( 0 !== $statement->lastInsertId ) &&
|
||||
( null !== $primaryKeyDefinition )) {
|
||||
|
||||
$pkField = key($primaryKeyDefinition);
|
||||
$dataset[$pkField] = $statement->lastInsertId;
|
||||
}
|
||||
|
||||
$entity->entityFillFromDataset($dataset, true);
|
||||
|
||||
return (bool) $statement->lastInsertId;
|
||||
}
|
||||
else {
|
||||
if ( $primaryKeyDefinition === null ) {
|
||||
throw new \Exception(sprintf("No primary key found for entity %s", $this->entityClass));
|
||||
}
|
||||
|
||||
$diff = $fieldsAndValue ?? $this->generateWritableDataset($entity);
|
||||
|
||||
if ( [] !== $diff ) {
|
||||
$pkField = key($primaryKeyDefinition);
|
||||
$pkFieldName = $primaryKeyDefinition[$pkField]->name ?? $pkField;
|
||||
$this->where($pkFieldName, $dataset[$pkFieldName]);
|
||||
|
||||
$update = $this->updateSqlQuery($diff)->runUpdateQuery();
|
||||
|
||||
$entity->entityFillFromDataset($dataset, true);
|
||||
|
||||
return $update ? (bool) $update->rowCount() : false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function replace(/*object|array*/ $entity, ? array $fieldsAndValue = null) : bool
|
||||
|
@ -258,11 +257,15 @@ class Repository
|
|||
return $this->save($entity, $fieldsAndValue, true);
|
||||
}
|
||||
|
||||
public function replaceAll(/*EntityCollection|array*/ $collection) : void
|
||||
public function replaceAll(/*EntityCollection|array*/ $collection) : int
|
||||
{
|
||||
$changed = 0;
|
||||
|
||||
foreach($collection as $entity) {
|
||||
$this->replace($entity);
|
||||
$this->replace($entity) && $changed++;
|
||||
}
|
||||
|
||||
return $changed;
|
||||
}
|
||||
|
||||
public function truncate(? string $table = null, ? string $alias = null, ? string $schema = null) : self
|
||||
|
@ -323,7 +326,9 @@ class Repository
|
|||
|
||||
public function removeQueryFragment(/*? Query\Fragment|string*/ $fragment) : self
|
||||
{
|
||||
$fragment && $this->queryBuilder->removeFragment($fragment);
|
||||
foreach((array) $fragment as $item) {
|
||||
$this->queryBuilder->removeFragment($item);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -407,7 +412,7 @@ class Repository
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function delete() : self
|
||||
public function delete(...$args) : self
|
||||
{
|
||||
$this->queryBuilder->delete($this->alias);
|
||||
|
||||
|
@ -489,10 +494,10 @@ class Repository
|
|||
public function randomizeOrder() : self
|
||||
{
|
||||
$this->queryBuilder->orderBy(Common\Sql::function('RAND', Sql::identifier('CURDATE()+0')));
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function orders(array $orderList) : self
|
||||
{
|
||||
foreach($orderList as $field => $direction) {
|
||||
|
@ -678,6 +683,64 @@ class Repository
|
|||
return $this;
|
||||
}
|
||||
|
||||
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 filterServerRequest(SearchRequest\SearchRequestInterface $searchRequest, bool $count = true) : self
|
||||
{
|
||||
if ($count) {
|
||||
|
@ -859,7 +922,7 @@ class Repository
|
|||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
public function instanciateEntityCollection(...$arguments) : EntityCollection
|
||||
{
|
||||
return $this->entityClass::entityCollection(...$arguments);
|
||||
|
@ -870,48 +933,7 @@ class Repository
|
|||
return new $this->entityClass();
|
||||
}
|
||||
|
||||
public function escapeField(string $identifier) : string
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
public function escapeDatabase(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_DATABASE);
|
||||
}
|
||||
|
||||
public function escapedDatabase() : ? string
|
||||
{
|
||||
$name = $this->entityResolver->tableAnnotation()->database ?? $this->adapter->adapter()->database ?? null;
|
||||
|
||||
return $name ? static::escapeDatabase($name) : null;
|
||||
}
|
||||
|
||||
public function escapeSchema(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_SCHEMA);
|
||||
}
|
||||
|
||||
|
||||
public function hasFilters() : bool
|
||||
{
|
||||
return isset($this->queryBuilder->where);
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Repository;
|
||||
|
||||
use Ulmus\{ Query, Adapter };
|
||||
use Ulmus\Annotation\Property\Field;
|
||||
|
||||
trait EscapeTrait
|
||||
{
|
||||
public function escapeField(string $identifier) : string
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
public function escapeDatabase(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_DATABASE);
|
||||
}
|
||||
|
||||
public function escapedDatabase() : ? string
|
||||
{
|
||||
$name = $this->entityResolver->tableAnnotation()->database ?? $this->adapter->adapter()->database ?? null;
|
||||
|
||||
return $name ? static::escapeDatabase($name) : null;
|
||||
}
|
||||
|
||||
public function escapeSchema(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_SCHEMA);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Ulmus\Repository;
|
||||
|
||||
use Ulmus\{ Repository, Query };
|
||||
use Ulmus\{Repository, Query, Ulmus, Common};
|
||||
|
||||
class MssqlRepository extends Repository {
|
||||
|
||||
|
@ -16,7 +16,7 @@ class MssqlRepository extends Repository {
|
|||
# an order by is mandatory for mssql offset/limit
|
||||
if ( null === $order = $this->queryBuilder->getFragment(Query\OrderBy::class) ) {
|
||||
$this->orderBy("(SELECT 0)");
|
||||
}
|
||||
}
|
||||
|
||||
$mssqlOffset = new \Ulmus\Query\MsSQL\Offset();
|
||||
|
||||
|
@ -40,4 +40,8 @@ class MssqlRepository extends Repository {
|
|||
}
|
||||
}
|
||||
|
||||
protected function serverRequestCountRepository() : Repository
|
||||
{
|
||||
return new static($this->entityClass, $this->alias, $this->adapter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ trait SearchRequestPaginationTrait {
|
|||
|
||||
public function offset(): int
|
||||
{
|
||||
return abs( ( $this->page - 1 ) * $this->limit() );
|
||||
return (int) ( abs( ( $this->page - 1 ) * $this->limit() ) );
|
||||
}
|
||||
|
||||
public function pagination(int $page, int $itemCount) : void
|
||||
|
|
|
@ -21,7 +21,6 @@ abstract class Ulmus
|
|||
public static function iterateQueryBuilder(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) : Generator
|
||||
{
|
||||
$rendered = $queryBuilder->render();
|
||||
|
||||
$statement = ( $adapter ?: static::$defaultAdapter )->connector()->select($rendered, $queryBuilder->parameters ?? []);
|
||||
|
||||
$i = 0;
|
||||
|
@ -81,12 +80,18 @@ abstract class Ulmus
|
|||
|
||||
public static function runInsertQuery(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null)
|
||||
{
|
||||
return static::runQuery($queryBuilder, $adapter);
|
||||
$return = static::connector($adapter)->runInsertQuery($queryBuilder->render(), array_merge($queryBuilder->values ?? [], $queryBuilder->parameters ?? []));
|
||||
$queryBuilder->reset();
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public static function runUpdateQuery(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null)
|
||||
{
|
||||
return static::runQuery($queryBuilder, $adapter);
|
||||
$return = static::connector($adapter)->runUpdateQuery($queryBuilder->render(), array_merge($queryBuilder->values ?? [], $queryBuilder->parameters ?? []));
|
||||
$queryBuilder->reset();
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public static function runDeleteQuery(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null)
|
||||
|
|
Loading…
Reference in New Issue