diff --git a/src/Annotation/Property/Field/Id.php b/src/Annotation/Property/Field/Id.php index 931b0ae..277373c 100644 --- a/src/Annotation/Property/Field/Id.php +++ b/src/Annotation/Property/Field/Id.php @@ -11,7 +11,7 @@ class Id extends \Ulmus\Annotation\Property\Field { $this->attributes['primary_key'] = true; $this->attributes['auto_increment'] = true; - parent::__construct('int'); + parent::__construct('bigint'); } } diff --git a/src/Annotation/Property/Filter.php b/src/Annotation/Property/Filter.php index d1f4eb9..e25f831 100644 --- a/src/Annotation/Property/Filter.php +++ b/src/Annotation/Property/Filter.php @@ -12,4 +12,4 @@ class Filter implements \Ulmus\Annotation\Annotation { $this->method = $method; } } -} +} \ No newline at end of file diff --git a/src/Annotation/Property/Where.php b/src/Annotation/Property/Where.php index e0e922d..622179c 100644 --- a/src/Annotation/Property/Where.php +++ b/src/Annotation/Property/Where.php @@ -11,8 +11,10 @@ class Where implements \Ulmus\Annotation\Annotation { public $value; public string $operator; + + public string $condition; - public function __construct(/* stringable */ $field = null, $value = null, ? string $operator = null) + public function __construct(? string $field = null, $value = null, ? string $operator = null, ? string $condition = null) { if ( $field !== null ) { $this->field = $field; @@ -22,11 +24,7 @@ class Where implements \Ulmus\Annotation\Annotation { $this->value = $value; } - if ( $operator !== null ) { - $this->operator = $operator; - } - else { - $this->operator = Query\Where::OPERATOR_EQUAL; - } + $this->operator = $operator !== null ? $operator : Query\Where::OPERATOR_EQUAL; + $this->condition = $condition !== null ? $condition : Query\Where::CONDITION_AND; } } diff --git a/src/EntityCollection.php b/src/EntityCollection.php index e66440f..e490113 100644 --- a/src/EntityCollection.php +++ b/src/EntityCollection.php @@ -25,12 +25,12 @@ class EntityCollection extends \ArrayObject { } } } - - public function filtersCollection(Callable $callback, bool $yieldValueOnly = false, bool $replaceCollection = false) : self + + public function filtersCollection(Callable $callback, bool $replaceCollection = false) : self { $collection = new static(); - - foreach($this->filters($callback, $yieldValueOnly) as $item) { + + foreach($this->filters($callback, true) as $item) { $collection->append($item); } @@ -77,8 +77,15 @@ class EntityCollection extends \ArrayObject { return $removed; } - - public function search($value, string $field, bool $strict = true) : Generator + + public function clear() : self + { + $this->exchangeArray([]); + + return $this; + } + + public function search($value, string $field, bool $strict = true) : Generator { foreach($this->filters(fn($v) => $strict ? $v->$field === $value : $v->$field == $value) as $key => $item) { yield $key => $item; @@ -105,7 +112,12 @@ class EntityCollection extends \ArrayObject { 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 { $list = []; @@ -117,9 +129,9 @@ class EntityCollection extends \ArrayObject { else { $value = $item->$field; } - - if ($unique && in_array($value, $list)) { - break; + + if ($unique && in_array($value, $list, true)) { + continue; } $list[] = $value; @@ -249,15 +261,22 @@ class EntityCollection extends \ArrayObject { parent::append($value); } } - - public function mergeWith( /*array|EntityCollection*/ $datasets ) : self + + public function mergeWith(... $datasets) : self { - if ( is_object($datasets) ) { - $datasets = $datasets->getArrayCopy(); + $list = []; + + foreach($datasets as $dataset) { + if ( is_object($dataset) ) { + $list = array_merge($dataset->getArrayCopy(), $list); + } + else { + $list = array_merge($dataset, $list); + } } - - $this->exchangeArray( array_merge( $this->getArrayCopy(), $datasets ) ); - + + $this->exchangeArray( array_merge( $this->getArrayCopy(), $list ) ); + return $this; } @@ -287,4 +306,16 @@ class EntityCollection extends \ArrayObject { return $this; } + + public function rsort(callable $callback, $function = "uasort") : self + { + $this->sort(...func_get_args()); + + return $this->reverse(); + } + + public function reverse() : self + { + return $this->replaceWith(array_reverse($this->getArrayCopy()));; + } } diff --git a/src/EntityTrait.php b/src/EntityTrait.php index bb8ac09..e7e2e74 100644 --- a/src/EntityTrait.php +++ b/src/EntityTrait.php @@ -8,7 +8,7 @@ use Ulmus\Repository, Ulmus\Common\EntityField; use Ulmus\Annotation\Classes\{ Method, Table, Collation, }; -use Ulmus\Annotation\Property\{ Field, Relation, OrderBy, Where, Join, Filter, On }; +use Ulmus\Annotation\Property\{ Field, Filter, Relation, OrderBy, Where, Join, Virtual, On, }; use Ulmus\Annotation\Property\Field\{ Id, ForeignKey, CreatedAt, UpdatedAt, Datetime as DateTime, Date, Time, }; trait EntityTrait { @@ -67,11 +67,10 @@ trait EntityTrait { $filters = $entityResolver->searchFieldAnnotationList($name, new Filter() ); $baseEntity = $relation->entity ?? $relation->bridge ?? $entityResolver->properties[$name]['type']; - $repository = $baseEntity::repository()->open(); - 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($where as $condition) { + $repository->where($condition->field, is_callable($condition->value) ? call_user_func_array($condition->value, [ $this ]) : $condition->value, $condition->operator, $condition->condition); } $repository->close(); @@ -79,6 +78,14 @@ trait EntityTrait { foreach ($order as $item) { $repository->orderBy($item->field, $item->order); } + + $applyFilter = function($repository) use ($filters, $name) { + foreach($filters as $filter) { + $repository = call_user_func_array([ $this, $filter->method ], [ $repository, $name ]); + } + + return $repository; + }; $field = $relation->key; @@ -112,6 +119,7 @@ trait EntityTrait { if ($relation->foreignKey) { $repository->where( is_object($relation->foreignKey) ? $relation->foreignKey : $baseEntity::field($relation->foreignKey), is_callable($field) ? $field($this) : $this->$field ); } + $this->eventExecute(Event\EntityRelationLoadInterface::class, $name, $repository); return $this->$name = call_user_func([$applyFilter($repository), $relation->function]); diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 40fcf00..23d50c6 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -66,7 +66,7 @@ class QueryBuilder if ( $schema ) { $table = "$schema.$table"; } - + if ( $database ) { $table = "$database.$table"; } @@ -329,7 +329,7 @@ class QueryBuilder if ( $schema ) { $table = "$schema.$table"; } - + if ( $database ) { $table = "$database.$table"; } diff --git a/src/Repository.php b/src/Repository.php index 73931d4..db8e757 100644 --- a/src/Repository.php +++ b/src/Repository.php @@ -177,6 +177,64 @@ class Repository } } + 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 truncate(? string $table = null, ? string $alias = null, ? string $schema = null) : self { $schema = $schema ?: $this->entityResolver->schemaName(); diff --git a/src/Repository/ConditionTrait.php b/src/Repository/ConditionTrait.php index 3c681e0..d8eee98 100644 --- a/src/Repository/ConditionTrait.php +++ b/src/Repository/ConditionTrait.php @@ -25,9 +25,9 @@ trait ConditionTrait return $this; } - public function where($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self + public function where($field, $value, string $operator = Query\Where::OPERATOR_EQUAL, $condition = Query\Where::CONDITION_AND) : self { - $this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_AND); + $this->queryBuilder->where($field, $value, $operator, $condition); return $this; } @@ -67,9 +67,9 @@ trait ConditionTrait return $this; } - public function having($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self + public function having($field, $value, string $operator = Query\Where::OPERATOR_EQUAL, $condition = Query\Where::CONDITION_AND) : self { - $this->queryBuilder->having($field, $value, $operator, Query\Where::CONDITION_AND); + $this->queryBuilder->having($field, $value, $operator, $condition); return $this; }