- Added a MssqlRepository
- Adapter some code to allow more flexibility for the new LDAP adapter from ulmus-ldap. - Insert's fields are now escaped by default. - A new RelationBuilder was added, a lot of code from the EntityTrait was moved into this. Some code from the Repository class will need to be moved there too.
This commit is contained in:
1 parent
8f9332fbad
commit
16c6eb57fb
32 files changed
+641
-243
No files matched your search
+126
-17
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Ulmus;
|
||||
|
||||
use Ulmus\Annotation\Property\{ Where, Having, Relation, Join };
|
||||
use Ulmus\Annotation\Property\{ Where, Having, Relation, Join, WithJoin, Relation\Ignore as RelationIgnore };
|
||||
use Ulmus\Common\EntityResolver;
|
||||
|
||||
class Repository
|
||||
@@ -13,7 +13,7 @@ class Repository
|
||||
|
||||
public ? ConnectionAdapter $adapter;
|
||||
|
||||
protected QueryBuilder $queryBuilder;
|
||||
protected Query\QueryBuilderInterface $queryBuilder;
|
||||
|
||||
protected EntityResolver $entityResolver;
|
||||
|
||||
@@ -271,6 +271,39 @@ class Repository
|
||||
}
|
||||
}
|
||||
|
||||
public function selectEntity(string $entity, string $alias, string $prependField = "") : self
|
||||
{
|
||||
$prependField and ( $prependField .= "$" );
|
||||
|
||||
foreach($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
||||
if ( null === $entity::resolveEntity()->searchFieldAnnotation($field['name'], new RelationIgnore) ) {
|
||||
$this->select("$alias.$key as {$prependField}{$field['name']}");
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function selectJsonEntity(string $entity, string $alias, bool $skipFrom = false) : self
|
||||
{
|
||||
$fieldlist = [];
|
||||
|
||||
foreach ($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
||||
if (null === $entity::resolveEntity()->searchFieldAnnotation($field['name'], new RelationIgnore)) {
|
||||
$fieldlist[] = $key;
|
||||
$fieldlist[] = $entity::field($field['name'], $alias);
|
||||
}
|
||||
}
|
||||
|
||||
$this->select(
|
||||
Common\Sql::function('JSON_ARRAYAGG', Common\Sql::function('JSON_OBJECT', ... $fieldlist))
|
||||
);
|
||||
|
||||
return $skipFrom ? $this : $this->from(
|
||||
$entity::resolveEntity()->tableName(), $alias, $entity::resolveEntity()->schemaName()
|
||||
);
|
||||
}
|
||||
|
||||
public function select(/*array|Stringable*/ $fields) : self
|
||||
{
|
||||
$this->queryBuilder->select($fields);
|
||||
@@ -444,8 +477,8 @@ class Repository
|
||||
$this->select("{$this->alias}.*");
|
||||
}
|
||||
|
||||
# Apply FILTER annotation to this too !
|
||||
foreach((array) $fields as $item) {
|
||||
# @TODO Apply FILTER annotation to this too !
|
||||
foreach(array_filter((array) $fields) as $item) {
|
||||
$annotation = $this->entityResolver->searchFieldAnnotation($item, new Join) ?:
|
||||
$this->entityResolver->searchFieldAnnotation($item, new Relation);
|
||||
|
||||
@@ -459,7 +492,9 @@ class Repository
|
||||
$entity = $annotation->entity ?? $this->entityResolver->properties[$item]['type'];
|
||||
|
||||
foreach($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
||||
$this->select("$alias.$key as {$alias}\${$field['name']}");
|
||||
if ( null === $entity::resolveEntity()->searchFieldAnnotation($field['name'], new RelationIgnore) ) {
|
||||
$this->select("$alias.$key as {$alias}\${$field['name']}");
|
||||
}
|
||||
}
|
||||
|
||||
$this->open();
|
||||
@@ -482,9 +517,14 @@ class Repository
|
||||
|
||||
$this->join("LEFT", $entity::resolveEntity()->tableName(), $key, $foreignKey, $alias, function($join) use ($item, $entity, $alias) {
|
||||
foreach($this->entityResolver->searchFieldAnnotationList($item, new Where() ) as $condition) {
|
||||
$field = clone $condition->field;
|
||||
if ( ! is_object($condition->field) ) {
|
||||
$field = $this->entityClass::field($condition->field);
|
||||
}
|
||||
else {
|
||||
$field = clone $condition->field;
|
||||
}
|
||||
|
||||
if ( is_object($condition->field) && ( $condition->field->entityClass === $entity ) ) {
|
||||
if ( $condition->field->entityClass === $entity ) {
|
||||
$field->alias = $alias;
|
||||
|
||||
$join->where(is_object($field) ? $field : $entity::field($field, $alias), $condition->value, $condition->operator);
|
||||
@@ -500,13 +540,77 @@ class Repository
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterServerRequest(SearchRequest\SearchRequestInterface $searchRequest) : self
|
||||
public function withSubquery(/*string|array*/ $fields) : 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())
|
||||
->count();
|
||||
# We skip subqueries when counting results since it should not affect the row count.
|
||||
if ( $this instanceof Repository\ServerRequestCountRepository ) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ( null === $this->queryBuilder->getFragment(Query\Select::class) ) {
|
||||
$this->select("{$this->alias}.*");
|
||||
}
|
||||
|
||||
# Apply FILTER annotation to this too !
|
||||
foreach(array_filter((array) $fields) as $item) {
|
||||
if ( $relation = $this->entityResolver->searchFieldAnnotation($item, new Relation) ) {
|
||||
$alias = $relation->alias ?? $item;
|
||||
|
||||
if ( $relation->isManyToMany() ) {
|
||||
$entity = $relation->bridge;
|
||||
$repository = $relation->bridge::repository();
|
||||
$repository->queryBuilder->parent = $this->queryBuilder;
|
||||
|
||||
extract(Repository\RelationBuilder::relationAnnotations($item, $relation));
|
||||
|
||||
$repository->join(Query\Join::TYPE_INNER, $bridgeEntity->tableName(), $relation->bridge::field($relationRelation->key, $relation->bridgeField), $relationRelation->entity::field($relationRelation->foreignKey, $alias), $relation->bridgeField)
|
||||
->where( $entity::field($bridgeRelation->key, $relation->bridgeField), $entity::field($bridgeRelation->foreignKey, $this->alias))
|
||||
->selectJsonEntity($relationRelation->entity, $alias)->open();
|
||||
}
|
||||
else {
|
||||
$entity = $relation->entity ?? $this->entityResolver->properties[$item]['type'];
|
||||
$repository = $entity::repository()->selectJsonEntity($entity, $alias)->open();
|
||||
}
|
||||
|
||||
# $relation->isManyToMany() and $repository->selectJsonEntity($relation->bridge, $relation->bridgeField, true);
|
||||
|
||||
foreach($this->entityResolver->searchFieldAnnotationList($item, new Where() ) as $condition) {
|
||||
$repository->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->value, $condition->operator);
|
||||
}
|
||||
|
||||
foreach($this->entityResolver->searchFieldAnnotationList($item, new Having() ) as $condition) {
|
||||
$repository->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->value, $condition->operator);
|
||||
}
|
||||
|
||||
$repository->close();
|
||||
|
||||
$key = is_string($relation->key) ? $this->entityClass::field($relation->key) : $relation->key;
|
||||
|
||||
if (! $relation->isManyToMany() ) {
|
||||
$foreignKey = is_string($relation->foreignKey) ? $entity::field($relation->foreignKey, $alias) : $relation->foreignKey;
|
||||
|
||||
$repository->where( $foreignKey, $key);
|
||||
}
|
||||
|
||||
$this->select("(" . $r = Common\Sql::raw($repository->queryBuilder->render() . ") as $item\$collection"));
|
||||
}
|
||||
else {
|
||||
throw new \Exception("You referenced field `$item` which do not exist or do not contain a valid @Join annotation.");
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterServerRequest(SearchRequest\SearchRequestInterface $searchRequest, bool $count = true) : self
|
||||
{
|
||||
if ($count) {
|
||||
$searchRequest->count = $searchRequest->filter(new Repository\ServerRequestCountRepository($this->entityClass, $this->alias, $this->adapter))
|
||||
->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)
|
||||
@@ -551,18 +655,18 @@ class Repository
|
||||
|
||||
return Ulmus::runQuery($this->queryBuilder, $this->adapter);
|
||||
}
|
||||
|
||||
|
||||
public function resetQuery() : self
|
||||
{
|
||||
$this->queryBuilder->reset();
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
protected function insertSqlQuery(array $dataset) : self
|
||||
{
|
||||
if ( null === $this->queryBuilder->getFragment(Query\Insert::class) ) {
|
||||
$this->insert(array_keys($dataset), $this->entityResolver->tableName(), $this->alias, $this->entityResolver->schemaName());
|
||||
$this->insert(array_map([ $this, 'escapeField' ] , array_keys($dataset)), $this->entityResolver->tableName(), $this->alias, $this->entityResolver->schemaName());
|
||||
}
|
||||
|
||||
$this->values($dataset);
|
||||
@@ -651,6 +755,11 @@ class Repository
|
||||
return new $this->entityClass();
|
||||
}
|
||||
|
||||
public function escapeField(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_FIELD);
|
||||
}
|
||||
|
||||
public function escapeTable(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_TABLE);
|
||||
|
||||
Reference in new issue
Block a user