entityClass = $entity; $this->alias = $alias; $this->entityResolver = Ulmus::resolveEntity($entity); $this->adapter = $adapter ?? $this->entityResolver->databaseAdapter() ?? Ulmus::$defaultAdapter; $this->queryBuilder = new QueryBuilder( $this->adapter->adapter() ); } public function loadOne() : ? object { return $this->limit(1)->collectionFromQuery()[0] ?? null; } public function loadOneFromField($field, $value) : ? object { return $this->where($field, $value)->loadOne(); } public function loadFromPk($value, $primaryKey = "id") : ? object { return $this->loadOneFromField($primaryKey, $value); } public function loadAll() : EntityCollection { return $this->collectionFromQuery(); } public function loadFromField($field, $value) : EntityCollection { return $this->where($field, $value)->collectionFromQuery(); } public function count() : int { $this->select("COUNT(*)")->selectSqlQuery(); $this->finalizeQuery(); return Ulmus::runSelectQuery($this->queryBuilder, $this->adapter)->fetchColumn(0); } public function deleteOne() { return $this->limit(1)->deleteSqlQuery()->runQuery(); } public function deleteAll() { return $this->deleteSqlQuery()->runQuery(); } public function deleteFromPk($value) : bool { if ( $value !== 0 && empty($value) ) { throw new Exception\EntityPrimaryKeyUnknown("A primary key value has to be defined to delete an item."); } return (bool) $this->wherePrimaryKey($value)->deleteOne()->rowCount(); } public function save(object $entity) : bool { $dataset = $entity->toArray(); $primaryKeyDefinition = Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField(); if ( ! $entity->isLoaded() ) { $statement = $this->insertSqlQuery($dataset)->runQuery(); if ( ( 0 !== $statement->lastInsertId ) && ( null !== $primaryKeyDefinition )) { $pkField = key($primaryKeyDefinition); $entity->$pkField = $statement->lastInsertId; } return true; } else { if ( $primaryKeyDefinition === null ) { throw new \Exception(sprintf("No primary key found for entity %s", $this->entityClass)); } if ( [] !== $diff = $this->generateDatasetDiff($entity) ) { $pkField = key($primaryKeyDefinition); $pkFieldName = $primaryKeyDefinition[$pkField]->name ?? $pkField; $this->where($pkFieldName, $dataset[$pkFieldName]); $update = $this->updateSqlQuery($diff)->runQuery(); return $update ? (bool) $update->rowCount() : false; } } return false; } public function saveAll(EntityCollection $collection) : void { foreach($collection as $entity) { $this->save($entity); } } public function generateDatasetDiff(object $entity) : array { return array_diff_assoc( array_change_key_case($entity->toArray()), array_change_key_case($entity->entityGetDataset(true)) ); } public function yieldAll() : \Generator { } public function select($fields) : self { $this->queryBuilder->select($fields); return $this; } public function insert(array $fieldlist, string $table, string $alias, ? string $schema) : self { $this->queryBuilder->insert($fieldlist, $this->escapeTable($table), $alias, $schema); return $this; } public function values(array $dataset) : self { $this->queryBuilder->values($dataset); return $this; } public function update(string $table, string $alias, ? string $schema) : self { $this->queryBuilder->update($this->escapeTable($table), $alias, $schema); return $this; } public function set(array $dataset) : self { $this->queryBuilder->set($dataset); return $this; } public function delete() : self { $this->queryBuilder->delete($this->alias); return $this; } public function from(string $table, ? string $alias, ? string $schema) : self { $this->queryBuilder->from($this->escapeTable($table), $alias, $this->escapeDatabase($this->adapter->adapter()->database), $schema ? $this->escapeSchema($schema) : null); return $this; } public function join(string $type, $table, $field, $value) : self { $this->queryBuilder->join($type, $this->escapeTable($table), $field, $value); 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); } } 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\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, string $operator = Query\Where::OPERATOR_NOT_EQUAL) : self { $this->queryBuilder->having($field, $value, $operator, 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, string $operator = Query\Where::OPERATOR_EQUAL) : self { return $this->orNot($field, $value, Query\Where::OPERATOR_NOT_EQUAL, Query\Where::CONDITION_OR); } 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); } } return $this; } public function match() : self { } public function notMatch() : self { } public function between() : self { } public function notBetween() : self { } public function groupBy() : self { return $this; } public function groups(array $groups) : self { # foreach($this->groups) return $this; } public function orderBy($field, ? string $direction = null) : self { $this->queryBuilder->orderBy($field, $direction); return $this; } public function orders(array $orderList) : self { foreach($orderList as $field => $direction) { $this->queryBuilder->orderBy($field, $direction); } return $this; } public function limit(int $value) : self { $this->queryBuilder->limit($value); return $this; } public function offset(int $value) : self { $this->queryBuilder->offset($value); return $this; } public function commit() : self { return $this; } public function rollback() : self { return $this; } public function wherePrimaryKey($value) : self { if ( null === $primaryKeyField = Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField() ) { throw new Exception\EntityPrimaryKeyUnknown("Entity has no field containing attributes 'primary_key'"); } $pkField = key($primaryKeyField); return $this->where($primaryKeyField[$pkField]->name ?? $pkField, $value); } 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) ->orders($searchRequest->orders()) ->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()); } public function collectionFromQuery(? string $entityClass = null) : EntityCollection { $class = $entityClass ?: $this->entityClass; $entityCollection = new EntityCollection(); $this->selectSqlQuery(); $this->finalizeQuery(); foreach(Ulmus::iterateQueryBuilder($this->queryBuilder, $this->adapter) as $entityData) { $entityCollection->append( ( new $class() )->entityFillFromDataset($entityData) ); } return $entityCollection; } public function runQuery() : ? \PDOStatement { $this->finalizeQuery(); return Ulmus::runQuery($this->queryBuilder, $this->adapter); } 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->values($dataset); return $this; } protected function updateSqlQuery(array $dataset) : self { if ( null === $this->queryBuilder->getFragment(Query\Update::class) ) { $this->update($this->entityResolver->tableName(), $this->alias, $this->entityResolver->schemaName()); } $this->set($dataset); return $this; } protected function selectSqlQuery() : self { if ( null === $this->queryBuilder->getFragment(Query\Select::class) ) { $this->select("{$this->alias}.*"); } if ( null === $this->queryBuilder->getFragment(Query\From::class) ) { $this->from($this->entityResolver->tableName(), $this->alias, $this->entityResolver->schemaName()); } return $this; } protected function deleteSqlQuery() : self { if ( null === $this->queryBuilder->getFragment(Query\Delete::class) ) { $this->delete(); } if ( null === $this->queryBuilder->getFragment(Query\From::class) ) { $this->from($this->entityResolver->tableName(), null, $this->entityResolver->schemaName()); } return $this; } protected function fromRow($row) : self { } protected function fromCollection($rows) : self { } public function table() { return "REFLECT TABLE"; } 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 escapeSchema(string $identifier) : string { return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_SCHEMA); } protected function finalizeQuery() : void {} }