Merge branch 'master' of https://git.mcnd.ca/mcndave/ulmus
This commit is contained in:
commit
f14347f01d
|
@ -6,9 +6,10 @@ use Ulmus\Annotation\Annotation;
|
||||||
use Ulmus\Migration\FieldDefinition;
|
use Ulmus\Migration\FieldDefinition;
|
||||||
use Ulmus\Ulmus,
|
use Ulmus\Ulmus,
|
||||||
Ulmus\Adapter\AdapterInterface,
|
Ulmus\Adapter\AdapterInterface,
|
||||||
Ulmus\Annotation\Property\Field;
|
Ulmus\Annotation\Property\Field,
|
||||||
|
Ulmus\Query\WhereRawParameter;
|
||||||
|
|
||||||
class EntityField
|
class EntityField implements WhereRawParameter
|
||||||
{
|
{
|
||||||
public string $name;
|
public string $name;
|
||||||
|
|
||||||
|
|
|
@ -83,16 +83,6 @@ class EntityResolver {
|
||||||
throw new \InvalidArgumentException("Given `fieldKey` is unknown to the EntityResolver");
|
throw new \InvalidArgumentException("Given `fieldKey` is unknown to the EntityResolver");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($escape) {
|
|
||||||
if ( isset($tag['object']->name) ) {
|
|
||||||
$tag['object']->name = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( isset($item['name']) ) {
|
|
||||||
$item['name'] = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$fieldList[$key] = $item;
|
$fieldList[$key] = $item;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -135,7 +125,7 @@ class EntityResolver {
|
||||||
{
|
{
|
||||||
$list = [];
|
$list = [];
|
||||||
|
|
||||||
$search = $caseSensitive ? $this->properties : array_change_key_case($this->properties, \CASE_LOWER);
|
$search = $caseSensitive ? $this->properties : array_change_key_case($this->properties, \CASE_LOWER);
|
||||||
|
|
||||||
if ( null !== ( $search[$field] ?? null ) ) {
|
if ( null !== ( $search[$field] ?? null ) ) {
|
||||||
foreach($search[$field]['tags'] ?? [] as $tag) {
|
foreach($search[$field]['tags'] ?? [] as $tag) {
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
namespace Ulmus\Common;
|
namespace Ulmus\Common;
|
||||||
|
|
||||||
|
use Ulmus\Query\WhereRawParameter;
|
||||||
|
|
||||||
abstract class Sql {
|
abstract class Sql {
|
||||||
|
|
||||||
public static function function($name, ...$arguments)
|
public static function function($name, ...$arguments)
|
||||||
{
|
{
|
||||||
return new class($name, ...$arguments) {
|
return new class($name, ...$arguments) implements WhereRawParameter {
|
||||||
|
|
||||||
protected string $as = "";
|
protected string $as = "";
|
||||||
|
|
||||||
|
@ -20,19 +22,23 @@ abstract class Sql {
|
||||||
$this->parseArguments();
|
$this->parseArguments();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __toString() {
|
public function __toString() : string
|
||||||
|
{
|
||||||
return implode(' ', array_filter([
|
return implode(' ', array_filter([
|
||||||
"{$this->name}(" . implode(", ", $this->arguments) . ")",
|
"{$this->name}(" . implode(", ", $this->arguments) . ")",
|
||||||
$this->as ? "AS {$this->as}" : false,
|
$this->as ? "AS {$this->as}" : false,
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function as($fieldName) {
|
public function as($fieldName) : self
|
||||||
|
{
|
||||||
$this->as = $fieldName;
|
$this->as = $fieldName;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function parseArguments() {
|
protected function parseArguments() : void
|
||||||
|
{
|
||||||
foreach($this->arguments as &$item) {
|
foreach($this->arguments as &$item) {
|
||||||
$item = Sql::escape($item);
|
$item = Sql::escape($item);
|
||||||
}
|
}
|
||||||
|
@ -42,7 +48,7 @@ abstract class Sql {
|
||||||
|
|
||||||
public static function identifier(string $identifier) : object
|
public static function identifier(string $identifier) : object
|
||||||
{
|
{
|
||||||
return new class($identifier) {
|
return new class($identifier) implements WhereRawParameter {
|
||||||
|
|
||||||
protected string $identifier;
|
protected string $identifier;
|
||||||
|
|
||||||
|
|
|
@ -87,6 +87,13 @@ class EntityCollection extends \ArrayObject {
|
||||||
return $removed;
|
return $removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function clear() : self
|
||||||
|
{
|
||||||
|
$this->exchangeArray([]);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function search($value, string $field, bool $strict = true) : Generator
|
public function search($value, string $field, bool $strict = true) : Generator
|
||||||
{
|
{
|
||||||
foreach($this->filters(fn($v) => isset($v->$field) ? ( $strict ? $v->$field === $value : $v->$field == $value ) : false) as $key => $item) {
|
foreach($this->filters(fn($v) => isset($v->$field) ? ( $strict ? $v->$field === $value : $v->$field == $value ) : false) as $key => $item) {
|
||||||
|
@ -134,6 +141,11 @@ class EntityCollection extends \ArrayObject {
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function searchInstances(string $className) : self
|
||||||
|
{
|
||||||
|
return $this->filtersCollection(fn($obj) => is_a($obj, $className));
|
||||||
|
}
|
||||||
|
|
||||||
public function column($field, bool $unique = false) : array
|
public function column($field, bool $unique = false) : array
|
||||||
{
|
{
|
||||||
$list = [];
|
$list = [];
|
||||||
|
@ -146,8 +158,8 @@ class EntityCollection extends \ArrayObject {
|
||||||
$value = $item->$field;
|
$value = $item->$field;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($unique && in_array($value, $list)) {
|
if ($unique && in_array($value, $list, true)) {
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$list[] = $value;
|
$list[] = $value;
|
||||||
|
@ -279,6 +291,13 @@ class EntityCollection extends \ArrayObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function push($value) : self
|
||||||
|
{
|
||||||
|
$this->append($value);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function mergeWith( /*array|EntityCollection*/ $datasets ) : self
|
public function mergeWith( /*array|EntityCollection*/ $datasets ) : self
|
||||||
{
|
{
|
||||||
if ( is_object($datasets) ) {
|
if ( is_object($datasets) ) {
|
||||||
|
@ -331,6 +350,11 @@ class EntityCollection extends \ArrayObject {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function rsort(callable $callback, $function = "uasort") : self
|
||||||
|
{
|
||||||
|
return $this->sort(...func_get_args())->reverse();
|
||||||
|
}
|
||||||
|
|
||||||
public function pop() /* : mixed */
|
public function pop() /* : mixed */
|
||||||
{
|
{
|
||||||
$arr = $this->getArrayCopy();
|
$arr = $this->getArrayCopy();
|
||||||
|
|
|
@ -14,52 +14,23 @@ use Ulmus\Annotation\Property\Relation\{ Ignore as RelationIgnore };
|
||||||
|
|
||||||
trait EntityTrait {
|
trait EntityTrait {
|
||||||
use EventTrait;
|
use EventTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Ignore
|
* @Ignore
|
||||||
*/
|
*/
|
||||||
protected bool $entityStrictFieldsDeclaration = false;
|
protected bool $entityStrictFieldsDeclaration = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Ignore
|
* @Ignore
|
||||||
*/
|
*/
|
||||||
protected array $entityDatasetUnmatchedFields = [];
|
protected array $entityDatasetUnmatchedFields = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Ignore
|
* @Ignore
|
||||||
*/
|
*/
|
||||||
public array $entityLoadedDataset = [];
|
public array $entityLoadedDataset = [];
|
||||||
|
|
||||||
/**
|
/**entityLoadedDataset
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public function __get(string $name)
|
|
||||||
{
|
|
||||||
$relation = new Repository\RelationBuilder($this);
|
|
||||||
|
|
||||||
if ( false !== $data = $relation->searchRelation($name) ) {
|
|
||||||
return $this->$name = $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \Exception(sprintf("[%s] - Undefined variable: %s", static::class, $name));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public function __isset(string $name) : bool
|
|
||||||
{
|
|
||||||
#if ( null !== $relation = static::resolveEntity()->searchFieldAnnotation($name, new Relation() ) ) {
|
|
||||||
# return isset($this->{$relation->key});
|
|
||||||
#}
|
|
||||||
if ( $this->isLoaded() && static::resolveEntity()->searchFieldAnnotation($name, new Relation() ) ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isset($this->$name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
* @Ignore
|
||||||
*/
|
*/
|
||||||
public function entityFillFromDataset(iterable $dataset, bool $overwriteDataset = false) : self
|
public function entityFillFromDataset(iterable $dataset, bool $overwriteDataset = false) : self
|
||||||
|
@ -71,9 +42,7 @@ trait EntityTrait {
|
||||||
foreach($dataset as $key => $value) {
|
foreach($dataset as $key => $value) {
|
||||||
$field = $entityResolver->field(strtolower($key), EntityResolver::KEY_COLUMN_NAME, false) ?? null;
|
$field = $entityResolver->field(strtolower($key), EntityResolver::KEY_COLUMN_NAME, false) ?? null;
|
||||||
|
|
||||||
if ( $field === null ) {
|
$field ??= $entityResolver->field(strtolower($key), EntityResolver::KEY_LC_ENTITY_NAME, false);
|
||||||
$field = $entityResolver->field(strtolower($key), EntityResolver::KEY_LC_ENTITY_NAME, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $field === null ) {
|
if ( $field === null ) {
|
||||||
if ($this->entityStrictFieldsDeclaration ) {
|
if ($this->entityStrictFieldsDeclaration ) {
|
||||||
|
@ -104,17 +73,22 @@ trait EntityTrait {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif ( $field['type'] === 'bool' ) {
|
elseif ( $field['type'] === 'bool' ) {
|
||||||
$this->{$field['name']} = (bool) $value;
|
$value = (bool) $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->{$field['name']} = $value;
|
$this->{$field['name']} = $value;
|
||||||
}
|
}
|
||||||
elseif ( ! $field['builtin'] ) {
|
elseif ( ! $field['builtin'] ) {
|
||||||
$this->{$field['name']} = Ulmus::instanciateObject($field['type'], [ $value ]);
|
try {
|
||||||
|
$this->{$field['name']} = Ulmus::instanciateObject($field['type'], [ $value ]);
|
||||||
|
}
|
||||||
|
catch(\Error $e) {
|
||||||
|
throw new \Error(sprintf("%s for class '%s' on field '%s'", $e->getMessage(), get_class($this), $field['name']));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Keeping original data to diff on UPDATE query
|
# Keeping original data to diff on UPDATE query
|
||||||
if ( ! $loaded ) {
|
if ( ! $loaded /* || $isLoadedDataset */ ) {
|
||||||
#if ( $field !== null ) {
|
#if ( $field !== null ) {
|
||||||
# $annotation = $entityResolver->searchFieldAnnotation($field['name'], new Field() );
|
# $annotation = $entityResolver->searchFieldAnnotation($field['name'], new Field() );
|
||||||
# $this->entityLoadedDataset[$annotation ? $annotation->name : $field['name']] = $dataset; # <--------- THIS TO FIX !!!!!!
|
# $this->entityLoadedDataset[$annotation ? $annotation->name : $field['name']] = $dataset; # <--------- THIS TO FIX !!!!!!
|
||||||
|
@ -143,7 +117,7 @@ trait EntityTrait {
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Ignore
|
* @Ignore
|
||||||
*/
|
*/
|
||||||
|
@ -151,7 +125,7 @@ trait EntityTrait {
|
||||||
{
|
{
|
||||||
return $this->entityFillFromDataset($dataset);
|
return $this->entityFillFromDataset($dataset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Ignore
|
* @Ignore
|
||||||
*/
|
*/
|
||||||
|
@ -160,53 +134,53 @@ trait EntityTrait {
|
||||||
if ( $returnSource ) {
|
if ( $returnSource ) {
|
||||||
return $this->entityLoadedDataset;
|
return $this->entityLoadedDataset;
|
||||||
}
|
}
|
||||||
|
|
||||||
$dataset = [];
|
$dataset = [];
|
||||||
|
|
||||||
$entityResolver = $this->resolveEntity();
|
$entityResolver = $this->resolveEntity();
|
||||||
|
|
||||||
foreach($entityResolver->fieldList(Common\EntityResolver::KEY_ENTITY_NAME, true) as $key => $field) {
|
foreach($entityResolver->fieldList(Common\EntityResolver::KEY_ENTITY_NAME, true) as $key => $field) {
|
||||||
$annotation = $entityResolver->searchFieldAnnotation($key, new Field() );
|
$annotation = $entityResolver->searchFieldAnnotation($key, new Field() );
|
||||||
|
|
||||||
if ( isset($this->$key) ) {
|
if ( isset($this->$key) ) {
|
||||||
$realKey = $annotation->name ?? $key;
|
$realKey = $annotation->name ?? $key;
|
||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case is_object($this->$key):
|
case is_object($this->$key):
|
||||||
$dataset[$realKey] = Ulmus::convertObject($this->$key);
|
$dataset[$realKey] = Ulmus::convertObject($this->$key);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case is_array($this->$key):
|
case is_array($this->$key):
|
||||||
$dataset[$realKey] = Ulmus::encodeArray($this->$key);
|
$dataset[$realKey] = Ulmus::encodeArray($this->$key);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case is_bool($this->$key):
|
case is_bool($this->$key):
|
||||||
$dataset[$realKey] = (int) $this->$key;
|
$dataset[$realKey] = (int) $this->$key;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$dataset[$realKey] = $this->$key;
|
$dataset[$realKey] = $this->$key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif ( $field['nullable'] ) {
|
elseif ( $field['nullable'] ) {
|
||||||
$dataset[ $annotation->name ?? $key ] = null;
|
$dataset[ $annotation->name ?? $key ] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# @TODO Must fix recursive bug !
|
# @TODO Must fix recursive bug !
|
||||||
if ($includeRelations) {
|
if ($includeRelations) {
|
||||||
foreach($entityResolver->properties as $name => $field){
|
foreach($entityResolver->properties as $name => $field){
|
||||||
$relation = $entityResolver->searchFieldAnnotation($name, new Relation() );
|
$relation = $entityResolver->searchFieldAnnotation($name, new Relation() );
|
||||||
|
|
||||||
if ( $relation && isset($this->$name) && ($relation->entity ?? $relation->bridge) !== static::class ) {
|
if ( $relation && isset($this->$name) && ($relation->entity ?? $relation->bridge) !== static::class ) {
|
||||||
if ( null !== $value = $this->$name ?? null ) {
|
if ( null !== $value = $this->$name ?? null ) {
|
||||||
if ( is_iterable($value) ) {
|
if ( is_iterable($value) ) {
|
||||||
$list = [];
|
$list = [];
|
||||||
|
|
||||||
foreach($value as $entity) {
|
foreach($value as $entity) {
|
||||||
$list[] = $entity->entityGetDataset(false);
|
$list[] = $entity->entityGetDataset(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
$dataset[$name] = $list;
|
$dataset[$name] = $list;
|
||||||
}
|
}
|
||||||
elseif ( is_object($value) ) {
|
elseif ( is_object($value) ) {
|
||||||
|
@ -215,11 +189,11 @@ trait EntityTrait {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $dataset;
|
return $dataset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Ignore
|
* @Ignore
|
||||||
*/
|
*/
|
||||||
|
@ -227,7 +201,7 @@ trait EntityTrait {
|
||||||
{
|
{
|
||||||
return $this->entityGetDataset($includeRelations);
|
return $this->entityGetDataset($includeRelations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Ignore
|
* @Ignore
|
||||||
*/
|
*/
|
||||||
|
@ -235,7 +209,7 @@ trait EntityTrait {
|
||||||
{
|
{
|
||||||
return static::entityCollection([ $this ]);
|
return static::entityCollection([ $this ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Ignore
|
* @Ignore
|
||||||
*/
|
*/
|
||||||
|
@ -244,16 +218,45 @@ trait EntityTrait {
|
||||||
if ( null === $pkField = $this->resolveEntity()->getPrimaryKeyField($this) ) {
|
if ( null === $pkField = $this->resolveEntity()->getPrimaryKeyField($this) ) {
|
||||||
throw new Exception\EntityPrimaryKeyUnknown(sprintf("Entity %s has no field containing attributes 'primary_key'", static::class));
|
throw new Exception\EntityPrimaryKeyUnknown(sprintf("Entity %s has no field containing attributes 'primary_key'", static::class));
|
||||||
}
|
}
|
||||||
|
|
||||||
$key = key($pkField);
|
$key = key($pkField);
|
||||||
|
|
||||||
return isset($this->$key);
|
return isset($this->$key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Ignore
|
* @Ignore
|
||||||
*/
|
*/
|
||||||
public function __sleep()
|
public function __get(string $name)
|
||||||
|
{
|
||||||
|
$relation = new Repository\RelationBuilder($this);
|
||||||
|
|
||||||
|
if ( false !== $data = $relation->searchRelation($name) ) {
|
||||||
|
return $this->$name = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \Exception(sprintf("[%s] - Undefined variable: %s", static::class, $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Ignore
|
||||||
|
*/
|
||||||
|
public function __isset(string $name) : bool
|
||||||
|
{
|
||||||
|
#if ( null !== $relation = static::resolveEntity()->searchFieldAnnotation($name, new Relation() ) ) {
|
||||||
|
# return isset($this->{$relation->key});
|
||||||
|
#}
|
||||||
|
if ( $this->isLoaded() && static::resolveEntity()->searchFieldAnnotation($name, new Relation() ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($this->$name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Ignore
|
||||||
|
*/
|
||||||
|
public function __sleep()
|
||||||
{
|
{
|
||||||
return array_keys($this->resolveEntity()->fieldList());
|
return array_keys($this->resolveEntity()->fieldList());
|
||||||
}
|
}
|
||||||
|
@ -279,9 +282,9 @@ trait EntityTrait {
|
||||||
*/
|
*/
|
||||||
public function jsonSerialize()
|
public function jsonSerialize()
|
||||||
{
|
{
|
||||||
return $this->entityGetDataset();
|
return $this->entityGetDataset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Ignore
|
* @Ignore
|
||||||
*/
|
*/
|
||||||
|
@ -309,6 +312,15 @@ trait EntityTrait {
|
||||||
return $collection;
|
return $collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Ignore
|
||||||
|
*/
|
||||||
|
public static function queryBuilder() : QueryBuilder
|
||||||
|
{
|
||||||
|
return Ulmus::queryBuilder(static::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Ignore
|
* @Ignore
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
namespace Ulmus\Query;
|
namespace Ulmus\Query;
|
||||||
|
|
||||||
use Ulmus\Common\EntityField,
|
use Ulmus\Common\Sql;
|
||||||
Ulmus\Common\Sql;
|
|
||||||
|
|
||||||
class Where extends Fragment {
|
class Where extends Fragment {
|
||||||
const OPERATOR_LIKE = "LIKE";
|
const OPERATOR_LIKE = "LIKE";
|
||||||
|
@ -136,10 +135,11 @@ class Where extends Fragment {
|
||||||
{
|
{
|
||||||
if ( $value === null ) {
|
if ( $value === null ) {
|
||||||
$this->operator = in_array($this->operator, [ '!=', '<>' ]) ? Where::COMPARISON_IS . " " . Where::CONDITION_NOT : Where::COMPARISON_IS;
|
$this->operator = in_array($this->operator, [ '!=', '<>' ]) ? Where::COMPARISON_IS . " " . Where::CONDITION_NOT : Where::COMPARISON_IS;
|
||||||
|
|
||||||
return Where::COMPARISON_NULL;
|
return Where::COMPARISON_NULL;
|
||||||
}
|
}
|
||||||
elseif ( is_object($value) && ( $value instanceof EntityField ) ) {
|
elseif (is_object($value) && ( $value instanceof WhereRawParameter ) ) {
|
||||||
return $value->name();
|
return (string) $value;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return $this->queryBuilder->addParameter($value);
|
return $this->queryBuilder->addParameter($value);
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Ulmus\Query;
|
||||||
|
|
||||||
|
|
||||||
|
interface WhereRawParameter {}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Ulmus;
|
namespace Ulmus;
|
||||||
|
|
||||||
use Ulmus\Annotation\Property\{ Field, Filter, FilterJoin, Where, Having, Relation, Join, WithJoin, Relation\Ignore as RelationIgnore };
|
use Ulmus\Annotation\Property\{Field, Where, Having, Relation, Filter, Join, FilterJoin, WithJoin, Relation\Ignore as RelationIgnore};
|
||||||
use Ulmus\Common\EntityResolver;
|
use Ulmus\Common\EntityResolver;
|
||||||
|
|
||||||
class Repository
|
class Repository
|
||||||
|
@ -148,7 +148,7 @@ class Repository
|
||||||
# $dataset = array_filter($dataset, fn($item, $field) => ! ($this->entityResolver->searchFieldAnnotation($field, new Field, false)->readonly ?? false), \ARRAY_FILTER_USE_BOTH);
|
# $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();
|
$statement = $this->insertSqlQuery($fieldsAndValue ?? $dataset, $replace)->runInsertQuery();
|
||||||
|
|
||||||
if ( ( 0 !== $statement->lastInsertId ) &&
|
if ( ( 0 !== $statement->lastInsertId ) &&
|
||||||
( null !== $primaryKeyDefinition )) {
|
( null !== $primaryKeyDefinition )) {
|
||||||
|
|
||||||
|
@ -183,23 +183,82 @@ class Repository
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function saveAll(EntityCollection $collection) : int
|
||||||
|
{
|
||||||
|
$changed = 0;
|
||||||
|
|
||||||
|
foreach ($collection as $entity) {
|
||||||
|
$this->save($entity) && $changed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $changed;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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 replace(/*object|array*/ $entity, ? array $fieldsAndValue = null) : bool
|
public function replace(/*object|array*/ $entity, ? array $fieldsAndValue = null) : bool
|
||||||
{
|
{
|
||||||
return $this->save($entity, $fieldsAndValue, true);
|
return $this->save($entity, $fieldsAndValue, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveAll(EntityCollection $collection) : int
|
public function replaceAll(/*EntityCollection|array*/ $collection) : void
|
||||||
{
|
|
||||||
$changed = 0;
|
|
||||||
|
|
||||||
foreach($collection as $entity) {
|
|
||||||
$this->save($entity) && $changed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function replaceAll(EntityCollection $collection) : void
|
|
||||||
{
|
{
|
||||||
foreach($collection as $entity) {
|
foreach($collection as $entity) {
|
||||||
$this->replace($entity);
|
$this->replace($entity);
|
||||||
|
@ -262,6 +321,13 @@ class Repository
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function removeQueryFragment(/*? Query\Fragment|string*/ $fragment) : self
|
||||||
|
{
|
||||||
|
$fragment && $this->queryBuilder->removeFragment($fragment);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function selectEntity(string $entity, string $alias, string $prependField = "") : self
|
public function selectEntity(string $entity, string $alias, string $prependField = "") : self
|
||||||
{
|
{
|
||||||
$prependField and ($prependField .= "$");
|
$prependField and ($prependField .= "$");
|
||||||
|
@ -479,6 +545,7 @@ class Repository
|
||||||
$this->select("{$this->alias}.*");
|
$this->select("{$this->alias}.*");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# @TODO Apply FILTER annotation to this too !
|
||||||
foreach(array_filter((array) $fields) as $item) {
|
foreach(array_filter((array) $fields) as $item) {
|
||||||
$annotation = $this->entityResolver->searchFieldAnnotation($item, new Join) ?:
|
$annotation = $this->entityResolver->searchFieldAnnotation($item, new Join) ?:
|
||||||
$this->entityResolver->searchFieldAnnotation($item, new Relation);
|
$this->entityResolver->searchFieldAnnotation($item, new Relation);
|
||||||
|
@ -611,6 +678,30 @@ class Repository
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function filterServerRequest(SearchRequest\SearchRequestInterface $searchRequest, bool $count = true) : self
|
||||||
|
{
|
||||||
|
if ($count) {
|
||||||
|
$searchRequest->count = $searchRequest->filter($this->serverRequestCountRepository())
|
||||||
|
->wheres($searchRequest->wheres(), Query\Where::OPERATOR_EQUAL, Query\Where::CONDITION_AND)
|
||||||
|
->likes($searchRequest->likes(), Query\Where::CONDITION_OR)
|
||||||
|
->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());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function serverRequestCountRepository() : Repository
|
||||||
|
{
|
||||||
|
return new Repository\ServerRequestCountRepository($this->entityClass, $this->alias, $this->adapter);
|
||||||
|
}
|
||||||
|
|
||||||
public function collectionFromQuery(? string $entityClass = null) : EntityCollection
|
public function collectionFromQuery(? string $entityClass = null) : EntityCollection
|
||||||
{
|
{
|
||||||
$class = $entityClass ?: $this->entityClass;
|
$class = $entityClass ?: $this->entityClass;
|
||||||
|
@ -831,29 +922,4 @@ class Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function finalizeQuery() : void {}
|
protected function finalizeQuery() : void {}
|
||||||
|
|
||||||
public function filterServerRequest(SearchRequest\SearchRequestInterface $searchRequest, bool $count = true) : self
|
|
||||||
{
|
|
||||||
if ($count) {
|
|
||||||
# @TODO Must be placed inside an event instead of directly there !
|
|
||||||
$searchRequest->count = $searchRequest->filter($this->serverRequestCountRepository())
|
|
||||||
->wheres($searchRequest->wheres(), Query\Where::OPERATOR_EQUAL, Query\Where::CONDITION_AND)
|
|
||||||
->likes($searchRequest->likes(), Query\Where::CONDITION_OR)
|
|
||||||
->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());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function serverRequestCountRepository() : Repository
|
|
||||||
{
|
|
||||||
return new Repository\ServerRequestCountRepository($this->entityClass, $this->alias, $this->adapter);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -118,7 +118,7 @@ class RelationBuilder
|
||||||
$this->repository->open();
|
$this->repository->open();
|
||||||
|
|
||||||
foreach($this->wheres as $condition) {
|
foreach($this->wheres as $condition) {
|
||||||
$this->repository->where($condition->field, $condition->value, $condition->operator);
|
$this->repository->where($condition->field, is_callable($condition->value) ? call_user_func_array($condition->value, [ $this->entity ]) : $condition->value, $condition->operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->repository->close();
|
$this->repository->close();
|
||||||
|
@ -155,7 +155,10 @@ class RelationBuilder
|
||||||
$entity = $annotation->entity ?? $this->resolver->properties[$name]['type'];
|
$entity = $annotation->entity ?? $this->resolver->properties[$name]['type'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$name = strtolower($name);
|
||||||
|
|
||||||
foreach($data ?: $this->entity->entityLoadedDataset as $key => $value) {
|
foreach($data ?: $this->entity->entityLoadedDataset as $key => $value) {
|
||||||
|
|
||||||
if ( $key === sprintf(static::SUBQUERY_FIELD_SUFFIX, strtolower($name)) ) {
|
if ( $key === sprintf(static::SUBQUERY_FIELD_SUFFIX, strtolower($name)) ) {
|
||||||
if ($value) {
|
if ($value) {
|
||||||
if ( null === ( $dataset = \json_decode($value, true) ) ) {
|
if ( null === ( $dataset = \json_decode($value, true) ) ) {
|
||||||
|
@ -227,6 +230,11 @@ class RelationBuilder
|
||||||
|
|
||||||
})->where( $this->entity::field($bridgeRelation->foreignKey, $relationAlias), is_string($this->entity) ? $this->entity::field($bridgeRelation->foreignKey) : $this->entity->{$bridgeRelation->foreignKey} );
|
})->where( $this->entity::field($bridgeRelation->foreignKey, $relationAlias), is_string($this->entity) ? $this->entity::field($bridgeRelation->foreignKey) : $this->entity->{$bridgeRelation->foreignKey} );
|
||||||
|
|
||||||
|
|
||||||
|
$this->applyWhere();
|
||||||
|
|
||||||
|
$this->applyOrderBy();
|
||||||
|
|
||||||
if ($selectBridgeField && $relation->bridgeField) {
|
if ($selectBridgeField && $relation->bridgeField) {
|
||||||
$this->repository->selectEntity($relation->bridge, $bridgeAlias, $bridgeAlias);
|
$this->repository->selectEntity($relation->bridge, $bridgeAlias, $bridgeAlias);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue