Merge branch 'master' of https://git.mcnd.ca/mcndave/ulmus
This commit is contained in:
commit
27d5fd0b08
|
@ -55,6 +55,11 @@ abstract class Sql {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static function raw(string $sql) : object
|
||||
{
|
||||
return static::identifier($sql);
|
||||
}
|
||||
|
||||
public static function escape($value)
|
||||
{
|
||||
|
|
|
@ -6,6 +6,8 @@ use Generator;
|
|||
|
||||
class EntityCollection extends \ArrayObject {
|
||||
|
||||
public ? string $entityClass = null;
|
||||
|
||||
public function filters(Callable $callback) : Generator
|
||||
{
|
||||
$idx = 0;
|
||||
|
@ -29,6 +31,17 @@ class EntityCollection extends \ArrayObject {
|
|||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
public function iterate(Callable $callback) : array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
foreach($this as $item) {
|
||||
$results[] = $callback($item);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function removeOne($value, string $field, bool $strict = true) : ? object
|
||||
{
|
||||
|
@ -72,6 +85,17 @@ class EntityCollection extends \ArrayObject {
|
|||
}
|
||||
}
|
||||
|
||||
public function column($field) : array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach($this as $item) {
|
||||
$list[] = $item->$field;
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function buildArray(string $keyColumn, /* string|callable|null */ $value = null) : array
|
||||
{
|
||||
$list = [];
|
||||
|
@ -98,6 +122,17 @@ class EntityCollection extends \ArrayObject {
|
|||
return $list;
|
||||
}
|
||||
|
||||
public function implode(string $glue, ? Callable $callback = null) : string
|
||||
{
|
||||
$values = [];
|
||||
|
||||
foreach($this as $item) {
|
||||
$values[] = $callback ? $callback($item) : (string) $item;
|
||||
}
|
||||
|
||||
return implode($glue, $values);
|
||||
}
|
||||
|
||||
public function toArray(bool $includeRelations = false) : array {
|
||||
$list = [];
|
||||
|
||||
|
@ -107,4 +142,30 @@ class EntityCollection extends \ArrayObject {
|
|||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function fromArray(array $datasets, ? string $entityClass = null) : self
|
||||
{
|
||||
if ( ! ($this->entityClass || $entityClass) ) {
|
||||
throw new \Exception("An entity class name must be provided to be instanciated and populated before insertion into this collection.");
|
||||
}
|
||||
|
||||
$className = $entityClass ?: $this->entityClass;
|
||||
|
||||
foreach($datasets as $dataset) {
|
||||
$this->append( (new $className() )->fromArray($dataset) );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function mergeWith( /*array|EntityCollection*/ $datasets ) : self
|
||||
{
|
||||
if ( is_object($datasets) ) {
|
||||
$datasets = $datasets->getArrayCopy();
|
||||
}
|
||||
|
||||
$this->exchangeArray( array_merge( $this->getArrayCopy(), $datasets ) );
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,6 +110,10 @@ trait EntityTrait {
|
|||
$bridgeRelation = $bridgeEntity->searchFieldAnnotation($relation->field, new Relation() );
|
||||
$relationRelation = $bridgeEntity->searchFieldAnnotation($relation->foreignField, new Relation() );
|
||||
|
||||
if ($relationRelation === null) {
|
||||
throw new \Exception("@Relation annotation not found for field `{$relation->foreignField}` in entity {$relation->bridge}");
|
||||
}
|
||||
|
||||
$repository = $relationRelation->entity()->repository();
|
||||
|
||||
$bridgeAlias = uniqid("bridge_");
|
||||
|
@ -321,9 +325,9 @@ trait EntityTrait {
|
|||
/**
|
||||
* @Ignore
|
||||
*/
|
||||
public function toCollection() : array
|
||||
public function toCollection() : EntityCollection
|
||||
{
|
||||
return new EntityCollection($this->toArray());
|
||||
return static::entityCollection([ $this->toArray() ]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -377,7 +381,10 @@ trait EntityTrait {
|
|||
*/
|
||||
public static function entityCollection(...$arguments) : EntityCollection
|
||||
{
|
||||
return new EntityCollection(...$arguments);
|
||||
$collection = new EntityCollection(...$arguments);
|
||||
$collection->entityClass = static::class;
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Event;
|
||||
namespace Ulmus\Event\Repository;
|
||||
|
||||
use Ulmus\EntityCollection;
|
||||
|
||||
interface RepositoryCollectionFromQueryInterface {
|
||||
interface CollectionFromQueryInterface {
|
||||
public function execute(EntityCollection $collection) : EntityCollection;
|
||||
}
|
|
@ -12,4 +12,20 @@ abstract class Fragment {
|
|||
{
|
||||
return implode($glue, array_filter($segments, function($i) { return ! is_null($i) && $i !== false && $i !== ""; }));
|
||||
}
|
||||
|
||||
protected function renderTables(/*array|string*/ $tableList, ? int $maxTableCount = null) : string
|
||||
{
|
||||
$i = 0;
|
||||
$list = [];
|
||||
|
||||
foreach((array) $tableList as $alias => $table) {
|
||||
$list[] = ! is_numeric($alias) ? "$table $alias" : $table;
|
||||
|
||||
if ( ++$i === $maxTableCount ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return implode(", ", $list);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ class From extends Fragment {
|
|||
|
||||
public array $tables = [];
|
||||
|
||||
public string $sql;
|
||||
|
||||
public function set(array $tables) : self
|
||||
{
|
||||
$this->tables = $tables;
|
||||
|
@ -29,18 +31,7 @@ class From extends Fragment {
|
|||
public function render() : string
|
||||
{
|
||||
return $this->renderSegments([
|
||||
static::SQL_TOKEN, $this->renderTables(),
|
||||
static::SQL_TOKEN, $this->sql ?? $this->renderTables($this->tables),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function renderTables() : string
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach((array) $this->tables as $alias => $table) {
|
||||
$list[] = ! is_numeric($alias) ? "$table $alias" : $table;
|
||||
}
|
||||
|
||||
return implode(", ", $list);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,142 @@
|
|||
|
||||
namespace Ulmus\Query;
|
||||
|
||||
class Having extends Where {
|
||||
const SQL_TOKEN = "HAVING";
|
||||
|
||||
use Ulmus\QueryBuilder;
|
||||
|
||||
use Ulmus\Common\EntityField,
|
||||
Ulmus\Common\Sql;
|
||||
|
||||
class Having extends Fragment {
|
||||
const SQL_TOKEN = "HAVING";
|
||||
|
||||
public int $order = 80;
|
||||
|
||||
public array $conditionList;
|
||||
|
||||
public QueryBuilder $queryBuilder;
|
||||
|
||||
public ? Having $parent = null;
|
||||
|
||||
public string $condition = Where::CONDITION_AND;
|
||||
|
||||
public function __construct(? QueryBuilder $queryBuilder, $condition = Where::CONDITION_AND)
|
||||
{
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
$this->condition = $condition;
|
||||
$this->parent = $queryBuilder->having ?? null;
|
||||
}
|
||||
|
||||
public function add($field, $value, string $operator, string $condition, bool $not = false) : self
|
||||
{
|
||||
$this->conditionList[] = [
|
||||
$field,
|
||||
$value,
|
||||
$operator ?: $this->queryBuilder->conditionOperator,
|
||||
$condition,
|
||||
$not
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render(bool $skipToken = false) : string
|
||||
{
|
||||
$stack = [];
|
||||
|
||||
foreach ($this->conditionList ?? [] as $key => $item) {
|
||||
if ( $item instanceof static ) {
|
||||
if ( $item->conditionList ?? false ) {
|
||||
$stack[] = ( $key !== 0 ? "{$item->condition} " : "" ) . "(" . $item->render($skipToken) . ")";
|
||||
}
|
||||
}
|
||||
else {
|
||||
list($field, $value, $operator, $condition, $not) = $item;
|
||||
$stack[] = $latest = $this->havingCondition($field, $value, $operator, $key !== 0 ? $condition : "", $not);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->renderSegments([
|
||||
! $this->parent && ! $skipToken ? static::SQL_TOKEN : "",
|
||||
implode(" ", $stack)
|
||||
]);
|
||||
}
|
||||
|
||||
protected function havingCondition($field, $value, string $operator = Where::OPERATOR_EQUAL, string $condition = Where::CONDITION_AND, bool $not = false) {
|
||||
return new class($this->queryBuilder, $field, $value, $operator, $condition, $not) {
|
||||
|
||||
public $value;
|
||||
public bool $not = false;
|
||||
public string $field;
|
||||
public string $operator;
|
||||
public string $condition;
|
||||
public QueryBuilder $queryBuilder;
|
||||
|
||||
protected string $content = "";
|
||||
|
||||
public function __construct(QueryBuilder $queryBuilder, string $field, $value, string $operator, string $condition, bool $not) {
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
$this->field = $field;
|
||||
$this->value = $value;
|
||||
$this->condition = $condition;
|
||||
$this->operator = $operator;
|
||||
$this->not = $not;
|
||||
}
|
||||
|
||||
public function render() : string
|
||||
{
|
||||
$value = $this->value();
|
||||
|
||||
return $this->content ?: $this->content = implode(" ", array_filter([
|
||||
$this->condition,
|
||||
$this->not ? Where::CONDITION_NOT : "",
|
||||
$this->field,
|
||||
$this->operator(),
|
||||
$value,
|
||||
]));
|
||||
}
|
||||
|
||||
protected function operator() : string
|
||||
{
|
||||
if ( is_array($this->value) ) {
|
||||
return (in_array($this->operator, [ '!=', '<>' ]) ? Where::CONDITION_NOT . " " : "") . Where::COMPARISON_IN;
|
||||
}
|
||||
|
||||
return $this->operator;
|
||||
}
|
||||
|
||||
protected function value()
|
||||
{
|
||||
if ( is_array($this->value) ) {
|
||||
$stack = [];
|
||||
|
||||
foreach($this->value as $item) {
|
||||
$stack[] = $this->filterValue($item);
|
||||
}
|
||||
|
||||
return "(" . implode(", ", $stack) . ")";
|
||||
}
|
||||
|
||||
return $this->filterValue($this->value);
|
||||
}
|
||||
|
||||
protected function filterValue($value)
|
||||
{
|
||||
if ( $value === null ) {
|
||||
$this->operator = in_array($this->operator, [ '!=', '<>' ]) ? Where::COMPARISON_IS . " " . Where::CONDITION_NOT : Where::COMPARISON_IS;
|
||||
return Where::COMPARISON_NULL;
|
||||
}
|
||||
elseif ( is_object($value) && ( $value instanceof EntityField ) ) {
|
||||
return $value->name();
|
||||
}
|
||||
else {
|
||||
return $this->queryBuilder->addParameter($value);
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
return $this->render();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,13 @@
|
|||
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";
|
||||
|
||||
const TYPE_LEFT = "LEFT";
|
||||
|
@ -33,8 +37,10 @@ class Join extends Fragment {
|
|||
|
||||
public /* QueryBuilder */ $queryBuilder;
|
||||
|
||||
public function __construct(QueryBuilder $queryBuilder) {
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
public function __construct(QueryBuilder $queryBuilder)
|
||||
{
|
||||
$this->queryBuilder = new QueryBuilder();
|
||||
$this->queryBuilder->parent = $queryBuilder;
|
||||
}
|
||||
|
||||
public function set(string $side, /* QueryBuilder|string */ $table, string $field, /* QueryBuilder|string */ $value)
|
||||
|
@ -44,9 +50,15 @@ class Join extends Fragment {
|
|||
$this->field = $field;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
|
||||
public function render() : string
|
||||
{
|
||||
return $this->renderSegments([ strtoupper($this->side), static::SQL_TOKEN, $this->table, $this->alias ?? "", $this->attachment, $this->field, "=", $this->value ]);
|
||||
if ($this->queryBuilder->where ?? false ) {
|
||||
$where = $this->renderSegments([Where::CONDITION_AND, $this->queryBuilder->render(true)]);
|
||||
}
|
||||
|
||||
return $this->renderSegments([
|
||||
strtoupper($this->side), $this->outer ? static::SQL_OUTER : "", static::SQL_TOKEN, $this->table, $this->alias ?? "", $this->attachment, $this->field, "=", $this->value, $where ?? ""
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@ namespace Ulmus\Query;
|
|||
|
||||
class Select extends Fragment {
|
||||
|
||||
const FIELD_AS = "%s as %s";
|
||||
|
||||
public int $order = -100;
|
||||
|
||||
public bool $distinct = false;
|
||||
|
@ -36,6 +38,13 @@ class Select extends Fragment {
|
|||
|
||||
public function render() : string
|
||||
{
|
||||
foreach($this->fields as $key => &$value) {
|
||||
if ( ! is_numeric($key) ) {
|
||||
$value = sprintf(static::FIELD_AS, $value, $key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $this->renderSegments([
|
||||
( $this->union ? 'UNION' : false ),
|
||||
static::SQL_TOKEN,
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Query;
|
||||
|
||||
class Truncate extends Fragment {
|
||||
|
||||
const SQL_TOKEN = "TRUNCATE TABLE";
|
||||
|
||||
public int $order = -100;
|
||||
|
||||
public string $table;
|
||||
|
||||
public function set(string $tableName) : self
|
||||
{
|
||||
$this->table = $tableName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render() : string
|
||||
{
|
||||
return $this->renderSegments([
|
||||
static::SQL_TOKEN, $this->table,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -50,14 +50,14 @@ class Where extends Fragment {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function render() : string
|
||||
public function render(bool $skipToken = false) : string
|
||||
{
|
||||
$stack = [];
|
||||
|
||||
foreach ($this->conditionList ?? [] as $key => $item) {
|
||||
if ( $item instanceof Where ) {
|
||||
if ( $item->conditionList ?? false ) {
|
||||
$stack[] = ( $key !== 0 ? "{$item->condition} " : "" ) . "(" . $item->render() . ")";
|
||||
$stack[] = ( $key !== 0 ? "{$item->condition} " : "" ) . "(" . $item->render($skipToken) . ")";
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -67,7 +67,7 @@ class Where extends Fragment {
|
|||
}
|
||||
|
||||
return $this->renderSegments([
|
||||
! $this->parent ? static::SQL_TOKEN : "",
|
||||
! $this->parent && ! $skipToken ? static::SQL_TOKEN : "",
|
||||
implode(" ", $stack)
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ class QueryBuilder
|
|||
|
||||
public Query\Having $having;
|
||||
|
||||
public QueryBuilder $parent;
|
||||
|
||||
/**
|
||||
* Those are the parameters we are going to bind to PDO.
|
||||
*/
|
||||
|
@ -18,10 +20,10 @@ class QueryBuilder
|
|||
* Those values are to be inserted or updated
|
||||
*/
|
||||
public array $values = [];
|
||||
|
||||
|
||||
public string $whereConditionOperator = Query\Where::CONDITION_AND;
|
||||
|
||||
public string $havingConditionOperator = Query\Having::CONDITION_AND;
|
||||
public string $havingConditionOperator = Query\Where::CONDITION_AND;
|
||||
|
||||
protected int $parameterIndex = 0;
|
||||
|
||||
|
@ -194,7 +196,12 @@ class QueryBuilder
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function having($field, $value, string $operator = Query\Having::OPERATOR_EQUAL, string $condition = Query\Having::CONDITION_AND, bool $not = false) : self
|
||||
public function notWhere($field, $value, string $operator = Query\Where::CONDITION_AND) : self
|
||||
{
|
||||
return $this->where($field, $value, $operator, true);
|
||||
}
|
||||
|
||||
public function having($field, $value, string $operator = Query\Where::OPERATOR_EQUAL, string $condition = Query\Where::CONDITION_AND, bool $not = false) : self
|
||||
{
|
||||
if ( $this->having ?? false ) {
|
||||
$having = $this->having;
|
||||
|
@ -209,13 +216,7 @@ class QueryBuilder
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function notWhere($field, $value, string $operator = Query\Where::CONDITION_AND) : self
|
||||
{
|
||||
return $this->where($field, $value, $operator, true);
|
||||
}
|
||||
|
||||
public function limit(int $value) : self
|
||||
{
|
||||
if ( null === $limit = $this->getFragment(Query\Limit::class) ) {
|
||||
|
@ -265,6 +266,13 @@ class QueryBuilder
|
|||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -276,17 +284,41 @@ class QueryBuilder
|
|||
|
||||
$join->alias = $alias;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
public function push(Query\Fragment $queryFragment) : self
|
||||
{
|
||||
$this->queryStack[] = $queryFragment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function pull(Query\Fragment $queryFragment) : self
|
||||
{
|
||||
return array_shift($this->queryStack);
|
||||
}
|
||||
|
||||
public function render() : string
|
||||
public function render(bool $skipToken = false) : string
|
||||
{
|
||||
$sql = [];
|
||||
|
||||
|
@ -295,7 +327,7 @@ class QueryBuilder
|
|||
});
|
||||
|
||||
foreach($this->queryStack as $fragment) {
|
||||
$sql[] = $fragment->render();
|
||||
$sql[] = $fragment->render($skipToken);
|
||||
}
|
||||
|
||||
return implode(" ", $sql);
|
||||
|
@ -305,10 +337,10 @@ class QueryBuilder
|
|||
{
|
||||
$this->parameters = $this->values = $this->queryStack = [];
|
||||
$this->whereConditionOperator = Query\Where::CONDITION_AND;
|
||||
$this->havingConditionOperator = Query\Having::CONDITION_AND;
|
||||
$this->havingConditionOperator = Query\Where::CONDITION_AND;
|
||||
$this->parameterIndex = 0;
|
||||
|
||||
unset($this->where);
|
||||
unset($this->where, $this->having);
|
||||
}
|
||||
|
||||
public function getFragment(string $class) : ? Query\Fragment
|
||||
|
@ -338,12 +370,16 @@ class QueryBuilder
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use Ulmus\Common\EntityResolver;
|
|||
|
||||
class Repository
|
||||
{
|
||||
use EventTrait;
|
||||
use EventTrait, Repository\ConditionTrait;
|
||||
|
||||
const DEFAULT_ALIAS = "this";
|
||||
|
||||
|
@ -27,7 +27,7 @@ class Repository
|
|||
$this->alias = $alias;
|
||||
$this->entityResolver = Ulmus::resolveEntity($entity);
|
||||
$this->adapter = $adapter ?? $this->entityResolver->databaseAdapter() ?? Ulmus::$defaultAdapter;
|
||||
$this->queryBuilder = new QueryBuilder( $this->adapter->adapter() );
|
||||
$this->queryBuilder = new QueryBuilder();
|
||||
}
|
||||
|
||||
public function loadOne() : ? object
|
||||
|
@ -61,7 +61,14 @@ class Repository
|
|||
$this->queryBuilder->removeFragment($select);
|
||||
}
|
||||
|
||||
$this->select("COUNT(*)")->selectSqlQuery();
|
||||
if ( $this->queryBuilder->getFragment(Query\GroupBy::class) ) {
|
||||
$this->select( "DISTINCT COUNT(*) OVER ()" );
|
||||
}
|
||||
else {
|
||||
$this->select(Common\Sql::function("COUNT", "*"));
|
||||
}
|
||||
|
||||
$this->selectSqlQuery();
|
||||
|
||||
$this->finalizeQuery();
|
||||
|
||||
|
@ -161,14 +168,35 @@ class Repository
|
|||
}
|
||||
}
|
||||
|
||||
public function truncate(? string $table = null, ? string $alias = null, ? string $schema = null) : self
|
||||
{
|
||||
$schema = $schema ?: $this->entityResolver->schemaName();
|
||||
|
||||
$this->queryBuilder->truncate($this->escapeTable($table ?: $this->entityResolver->tableName()), $alias ?: $this->alias, $this->escapeDatabase($this->adapter->adapter()->database), $schema ? $this->escapeSchema($schema) : null);
|
||||
|
||||
$this->finalizeQuery();
|
||||
|
||||
$result = Ulmus::runSelectQuery($this->queryBuilder, $this->adapter);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function generateDatasetDiff(object $entity) : array
|
||||
{
|
||||
return array_diff_assoc( array_change_key_case($entity->toArray()), array_change_key_case($entity->entityGetDataset(false, true)) );
|
||||
}
|
||||
|
||||
public function yieldAll() : \Generator
|
||||
|
||||
public function yield() : \Generator
|
||||
{
|
||||
$class = $this->entityClass;
|
||||
|
||||
$this->selectSqlQuery();
|
||||
|
||||
$this->finalizeQuery();
|
||||
|
||||
foreach(Ulmus::iterateQueryBuilder($this->queryBuilder, $this->adapter) as $entityData) {
|
||||
yield ( new $class() )->entityFillFromDataset($entityData);
|
||||
}
|
||||
}
|
||||
|
||||
public function select($fields) : self
|
||||
|
@ -220,194 +248,23 @@ class Repository
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function join(string $type, $table, $field, $value, ? string $alias = null) : self
|
||||
public function join(string $type, $table, $field, $value, ? string $alias = null, ? callable $callback = null) : self
|
||||
{
|
||||
$this->queryBuilder->join($type, $this->escapeTable($table), $field, $value, false, $alias);
|
||||
$join = $this->queryBuilder->withJoin($type, $this->escapeTable($table), $field, $value, false, $alias);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function outerJoin(string $type, $table, $field, $value, ? string $alias = null) : self
|
||||
{
|
||||
$this->queryBuilder->join($type, $this->escapeTable($table), $field, $value, true, $alias);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function open(string $condition = Query\Where::CONDITION_AND) : self
|
||||
{
|
||||
$this->queryBuilder->open($condition);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orOpen() : self
|
||||
{
|
||||
return $this->open(Query\Where::CONDITION_OR);
|
||||
}
|
||||
|
||||
public function close() : self
|
||||
{
|
||||
$this->queryBuilder->close();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function where($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_AND);
|
||||
|
||||
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);
|
||||
}
|
||||
if ( $callback ) {
|
||||
$callback($join);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function and($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
return $this->where($field, $value, $operator);
|
||||
}
|
||||
|
||||
public function or($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
public function outerJoin(string $type, $table, $field, $value, ? string $alias = null, ? callable $callback = null) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_OR);
|
||||
$join = $this->queryBuilder->withJoin($type, $this->escapeTable($table), $field, $value, true, $alias);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function notWhere($field, $value, string $operator = Query\Where::OPERATOR_NOT_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_AND, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orNot($field, $value, string $operator = Query\Where::OPERATOR_NOT_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->notWhere($field, $value, $operator, Query\Where::CONDITION_OR, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function having($field, $value, string $operator = Query\Having::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->having($field, $value, $operator, Query\Having::CONDITION_AND);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orHaving($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->having($field, $value, $operator, Query\Having::CONDITION_OR);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function notHaving($field, $value, string $operator = Query\Where::OPERATOR_NOT_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->having($field, $value, $operator, Query\Having::CONDITION_AND, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orNotHaving($field, $value) : self
|
||||
{
|
||||
$this->queryBuilder->having($field, $value, Query\Where::OPERATOR_NOT_EQUAL, Query\Having::CONDITION_OR, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function in($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orIn($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_OR);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function notIn($field, $value) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, Query\Where::OPERATOR_NOT_EQUAL);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orNotIn($field, $value) : self
|
||||
{
|
||||
return $this->orNot($field, $value, Query\Where::OPERATOR_NOT_EQUAL, Query\Where::CONDITION_OR, true);
|
||||
}
|
||||
|
||||
public function like($field, $value) : self
|
||||
{
|
||||
$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->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);
|
||||
}
|
||||
if ( $callback ) {
|
||||
$callback($join);
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
@ -432,15 +289,20 @@ class Repository
|
|||
{
|
||||
|
||||
}
|
||||
|
||||
public function groupBy() : self
|
||||
|
||||
public function groupBy($field) : self
|
||||
{
|
||||
$this->queryBuilder->groupBy($field);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function groups(array $groups) : self
|
||||
{
|
||||
# foreach($this->groups)
|
||||
foreach($groups as $field ) {
|
||||
$this->groupBy($field);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -454,7 +316,7 @@ class Repository
|
|||
public function orders(array $orderList) : self
|
||||
{
|
||||
foreach($orderList as $field => $direction) {
|
||||
$this->queryBuilder->orderBy($field, $direction);
|
||||
$this->orderBy($field, $direction);
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
@ -502,7 +364,10 @@ class Repository
|
|||
public function withJoin(/*string|array*/ $fields) : self
|
||||
{
|
||||
$resolvedEntity = Ulmus::resolveEntity($this->entityClass);
|
||||
$this->select("{$this->alias}.*");
|
||||
|
||||
if ( null === $this->queryBuilder->getFragment(Query\Select::class) ) {
|
||||
$this->select("{$this->alias}.*");
|
||||
}
|
||||
|
||||
foreach((array) $fields as $item) {
|
||||
if ( null !== $join = $resolvedEntity->searchFieldAnnotation($item, new Annotation\Property\Join) ) {
|
||||
|
@ -528,17 +393,21 @@ class Repository
|
|||
|
||||
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)
|
||||
->groups($searchRequest->groups())
|
||||
$likes = $searchRequest->likes();
|
||||
$wheres = $searchRequest->wheres();
|
||||
$groups = $searchRequest->groups();
|
||||
|
||||
$searchRequest->count = $searchRequest->skipCount ? 0 : $searchRequest->filter( clone $this )
|
||||
->wheres($wheres, Query\Where::OPERATOR_EQUAL, Query\Where::CONDITION_AND)
|
||||
->likes($likes, Query\Where::CONDITION_OR)
|
||||
->groups($groups)
|
||||
->count();
|
||||
|
||||
return $searchRequest->filter($this)
|
||||
->wheres($searchRequest->wheres(), Query\Where::OPERATOR_EQUAL, Query\Where::CONDITION_AND)
|
||||
->likes($searchRequest->likes(), Query\Where::CONDITION_OR)
|
||||
->wheres($wheres, Query\Where::OPERATOR_EQUAL, Query\Where::CONDITION_AND)
|
||||
->likes($likes, Query\Where::CONDITION_OR)
|
||||
->orders($searchRequest->orders())
|
||||
->groups($searchRequest->groups())
|
||||
->groups($groups)
|
||||
->offset($searchRequest->offset())
|
||||
->limit($searchRequest->limit());
|
||||
}
|
||||
|
@ -546,8 +415,8 @@ class Repository
|
|||
public function collectionFromQuery(? string $entityClass = null) : EntityCollection
|
||||
{
|
||||
$class = $entityClass ?: $this->entityClass;
|
||||
|
||||
$entityCollection = $this->instanciateEntityCollection();
|
||||
|
||||
$entityCollection = $class::entityCollection();
|
||||
|
||||
$this->selectSqlQuery();
|
||||
|
||||
|
@ -557,7 +426,7 @@ class Repository
|
|||
$entityCollection->append( ( new $class() )->entityFillFromDataset($entityData) );
|
||||
}
|
||||
|
||||
$this->eventExecute(Event\RepositoryCollectionFromQueryInterface::class, $entityCollection);
|
||||
$this->eventExecute(Event\Repository\CollectionFromQueryInterface::class, $entityCollection);
|
||||
|
||||
return $entityCollection;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Repository;
|
||||
|
||||
use Ulmus\Query;
|
||||
|
||||
trait ConditionTrait
|
||||
{
|
||||
public function open(string $condition = Query\Where::CONDITION_AND) : self
|
||||
{
|
||||
$this->queryBuilder->open($condition);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orOpen() : self
|
||||
{
|
||||
return $this->open(Query\Where::CONDITION_OR);
|
||||
}
|
||||
|
||||
public function close() : self
|
||||
{
|
||||
$this->queryBuilder->close();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function where($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_AND);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function wheres(array $fieldValues, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
foreach($fieldValues as $field => $value) {
|
||||
$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);
|
||||
}
|
||||
|
||||
public function or($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_OR);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function notWhere($field, $value, string $operator = Query\Where::OPERATOR_NOT_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_AND, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orNot($field, $value, string $operator = Query\Where::OPERATOR_NOT_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->notWhere($field, $value, $operator, Query\Where::CONDITION_OR, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function having($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->having($field, $value, $operator, Query\Where::CONDITION_AND);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orHaving($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->having($field, $value, $operator, Query\Where::CONDITION_OR);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function notHaving($field, $value, string $operator = Query\Where::OPERATOR_NOT_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->having($field, $value, $operator, Query\Where::CONDITION_AND, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orNotHaving($field, $value) : self
|
||||
{
|
||||
$this->queryBuilder->having($field, $value, Query\Where::OPERATOR_NOT_EQUAL, Query\Where::CONDITION_OR, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function in($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orIn($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_OR);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function notIn($field, $value) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, Query\Where::OPERATOR_NOT_EQUAL);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orNotIn($field, $value) : self
|
||||
{
|
||||
return $this->orNot($field, $value, Query\Where::OPERATOR_NOT_EQUAL, Query\Where::CONDITION_OR, true);
|
||||
}
|
||||
|
||||
public function like($field, $value) : self
|
||||
{
|
||||
$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->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) {
|
||||
$this->like($field, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -12,6 +12,8 @@ trait SearchRequestPaginationTrait {
|
|||
|
||||
public int $limit = 25;
|
||||
|
||||
public bool $skipCount = false;
|
||||
|
||||
public function limit(): int
|
||||
{
|
||||
return $this->limit;
|
||||
|
|
Loading…
Reference in New Issue