- Merging

This commit is contained in:
Dave M. 2024-12-16 18:57:01 +00:00
commit 5a5d326d70
33 changed files with 763 additions and 423 deletions

View File

@ -39,7 +39,7 @@ class MsSQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface {
public string $database; public string $database;
public string $username; public string $username;
public string $password; public string $password;
public string $traceFile; public string $traceFile;
@ -51,7 +51,9 @@ class MsSQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface {
public function __construct( public function __construct(
?string $server = null, ?string $server = null,
?string $database = null, ?string $database = null,
#[\SensitiveParameter]
?string $username = null, ?string $username = null,
#[\SensitiveParameter]
?string $password = null, ?string $password = null,
?int $port = null ?int $port = null
) { ) {

View File

@ -38,7 +38,9 @@ class MySQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface {
public function __construct( public function __construct(
?string $hostname = null, ?string $hostname = null,
?string $database = null, ?string $database = null,
#[\SensitiveParameter]
?string $username = null, ?string $username = null,
#[\SensitiveParameter]
?string $password = null, ?string $password = null,
?int $port = null, ?int $port = null,
?string $charset = null ?string $charset = null

View File

@ -3,6 +3,7 @@
namespace Ulmus\Adapter; namespace Ulmus\Adapter;
use Ulmus\Migration\FieldDefinition; use Ulmus\Migration\FieldDefinition;
use Ulmus\Entity;
class SqlFieldMapper class SqlFieldMapper
{ {
@ -42,7 +43,7 @@ class SqlFieldMapper
switch($type) { switch($type) {
case "bool": case "bool":
$this->type = "TINYINT"; $this->type = "TINYINT";
$length = 1; $this->length = 1;
break; break;
case "array": case "array":
@ -52,6 +53,7 @@ class SqlFieldMapper
case "string": case "string":
if ($length && $length <= 255) { if ($length && $length <= 255) {
$this->type = "VARCHAR"; $this->type = "VARCHAR";
$this->length = $length;
break; break;
} }
elseif (! $length || ( $length <= 65535 ) ) { elseif (! $length || ( $length <= 65535 ) ) {
@ -74,11 +76,13 @@ class SqlFieldMapper
case "float": case "float":
$this->type = "DOUBLE"; $this->type = "DOUBLE";
break;
default: default:
$this->type = strtoupper($type); if ($length) {
break; $this->length = $length;
}
$this->type ??= strtoupper($type);
} }
$this->postProcess(); $this->postProcess();

View File

@ -13,7 +13,7 @@ class OrWhere extends Where {
public string $operator = Query\Where::OPERATOR_EQUAL, public string $operator = Query\Where::OPERATOR_EQUAL,
public string $condition = Query\Where::CONDITION_OR, public string $condition = Query\Where::CONDITION_OR,
public string|\Stringable|array|null $fieldValue = null, public string|\Stringable|array|null $fieldValue = null,
public null|array $generateValue = null, public null|array|\Closure $generateValue = null,
) { ) {
parent::__construct($field, $value, $operator, $condition, $fieldValue, $generateValue); parent::__construct($field, $value, $operator, $condition, $fieldValue, $generateValue);
} }

View File

@ -0,0 +1,42 @@
<?php
namespace Ulmus\Attribute\Property\Relation;
use Ulmus\Attribute\Property\Relation;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class ManyToMany extends Relation
{
public function __construct(
public \Stringable|string|array $key = "",
public null|\Closure|array $generateKey = null,
public null|\Stringable|string|array $foreignKey = null,
public null|\Stringable|string|array $foreignField = null,
public array $foreignKeys = [],
public null|string $bridge = null,
public null|\Stringable|string|array $bridgeKey = null,
public null|\Stringable|string|array $bridgeField = null,
public null|\Stringable|string|array $bridgeForeignKey = null,
public null|\Stringable|string|array $field = null,
public null|string $entity = null,
public null|string $join = null,
public null|string $function = null,
) {
parent::__construct(
RelationTypeEnum::manyToMany,
$this->key,
$this->generateKey,
$this->foreignKey,
$this->foreignField,
$this->foreignKeys,
$this->bridge,
$this->bridgeKey,
$this->bridgeField,
$this->bridgeForeignKey,
$this->field,
$this->entity,
$this->join,
$this->function,
);
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace Ulmus\Attribute\Property\Relation;
use Ulmus\Attribute\Property\Relation;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class OneToMany extends Relation
{
public function __construct(
public \Stringable|string|array $key = "",
public null|\Closure|array $generateKey = null,
public null|\Stringable|string|array $foreignKey = null,
public null|\Stringable|string|array $foreignField = null,
public array $foreignKeys = [],
public null|string $bridge = null,
public null|\Stringable|string|array $bridgeKey = null,
public null|\Stringable|string|array $bridgeField = null,
public null|\Stringable|string|array $bridgeForeignKey = null,
public null|\Stringable|string|array $field = null,
public null|string $entity = null,
public null|string $join = null,
public null|string $function = null,
) {
parent::__construct(
RelationTypeEnum::oneToMany,
$this->key,
$this->generateKey,
$this->foreignKey,
$this->foreignField,
$this->foreignKeys,
$this->bridge,
$this->bridgeKey,
$this->bridgeField,
$this->bridgeForeignKey,
$this->field,
$this->entity,
$this->join,
$this->function,
);
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace Ulmus\Attribute\Property\Relation;
use Ulmus\Attribute\Property\Relation;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class OneToOne extends Relation
{
public function __construct(
public \Stringable|string|array $key = "",
public null|\Closure|array $generateKey = null,
public null|\Stringable|string|array $foreignKey = null,
public null|\Stringable|string|array $foreignField = null,
public array $foreignKeys = [],
public null|string $bridge = null,
public null|\Stringable|string|array $bridgeKey = null,
public null|\Stringable|string|array $bridgeField = null,
public null|\Stringable|string|array $bridgeForeignKey = null,
public null|\Stringable|string|array $field = null,
public null|string $entity = null,
public null|string $join = null,
public null|string $function = null,
) {
parent::__construct(
RelationTypeEnum::oneToOne,
$this->key,
$this->generateKey,
$this->foreignKey,
$this->foreignField,
$this->foreignKeys,
$this->bridge,
$this->bridgeKey,
$this->bridgeField,
$this->bridgeForeignKey,
$this->field,
$this->entity,
$this->join,
$this->function,
);
}
}

View File

@ -13,7 +13,7 @@ class Where {
public string $operator = Query\Where::OPERATOR_EQUAL, public string $operator = Query\Where::OPERATOR_EQUAL,
public string $condition = Query\Where::CONDITION_AND, public string $condition = Query\Where::CONDITION_AND,
public string|\Stringable|array|null $fieldValue = null, public string|\Stringable|array|null $fieldValue = null,
public null|array $generateValue = null, public null|array|\Closure $generateValue = null,
) { ) {
$this->field = Attribute::handleArrayField($field); $this->field = Attribute::handleArrayField($field);
$this->fieldValue = Attribute::handleArrayField($fieldValue); $this->fieldValue = Attribute::handleArrayField($fieldValue);
@ -29,6 +29,9 @@ class Where {
throw new \Exception(sprintf("Could not generate value from non-instanciated entity for field %s.", (string) $this->field)); throw new \Exception(sprintf("Could not generate value from non-instanciated entity for field %s.", (string) $this->field));
} }
} }
elseif ($this->fieldValue && $entity) {
throw new \Exception(sprintf("Field value, from %s, could not be included in query since the entity is already loaded; it is meant to be used with a OneToOne relation loaded within a join.", (string) $this->fieldValue));
}
return $this->fieldValue ?? $this->value; return $this->fieldValue ?? $this->value;
} }

View File

@ -0,0 +1,22 @@
<?php
namespace Ulmus\Cache;
use Psr\SimpleCache\CacheInterface;
trait CacheEventTrait
{
public function __construct(
protected CacheInterface $cache,
protected string $cacheKey,
) {}
public function purgeCache() : void
{
$keys = $this->cache->get($this->cacheKey);
if ( $keys && is_iterable($keys) ) {
$this->cache->deleteMultiple(array_map(fn($e) => sprintf("%s:%s:", $this->cacheKey, $e), $keys));
}
}
}

117
src/CacheTrait.php Normal file
View File

@ -0,0 +1,117 @@
<?php
namespace Ulmus;
use Psr\SimpleCache\CacheInterface;
use Ulmus\Entity\EntityInterface;
use Ulmus\QueryBuilder\QueryBuilderInterface;
use Ulmus\Repository\RepositoryInterface;
trait CacheTrait
{
protected CacheInterface $cache;
public function attachCachingObject(CacheInterface $cache) : self
{
$cacheKey = "";
$this->cache = $cache;
# Reading from cache
$this->eventRegister(new class($cacheKey) implements Event\Repository\CollectionFromQueryDatasetInterface {
public function __construct(
protected string & $cacheKey
) {}
public function execute(RepositoryInterface $repository, array &$data): void
{
$this->cacheKey = $repository->queryBuilder->hashSerializedQuery();
$data = $repository->getFromCache( $this->cacheKey) ?: [];
}
});
# Setting to cache
$this->eventRegister(new class($cacheKey) implements Event\Repository\CollectionFromQueryInterface {
public function __construct(
protected string & $cacheKey
) {}
public function execute(RepositoryInterface $repository, EntityCollection $collection): EntityCollection
{
$repository->setToCache( $this->cacheKey, $collection->map(fn(EntityInterface $e) => $e->entityGetDataset(false, true)));
$this->cacheKey = "";
return $collection;
}
});
$this->eventRegister(new class($this->cache, $this->entityCacheKey()) implements Event\Query\Insert {
use Cache\CacheEventTrait;
public function execute(RepositoryInterface $repository, object|array $entity, ?array $dataset = null, bool $replace = false): void
{
$this->purgeCache();
}
});
# Cache invalidation
$this->eventRegister(new class($this->cache, $this->entityCacheKey()) implements Event\Query\Update {
use Cache\CacheEventTrait;
public function execute(RepositoryInterface $repository, object|array $entity, ?array $dataset = null, bool $replace = false): void
{
$this->purgeCache();
}
});
$this->eventRegister(new class($this->cache, $this->entityCacheKey()) implements Event\Query\Delete {
use Cache\CacheEventTrait;
public function execute(RepositoryInterface $repository, EntityInterface $entity): void
{
$this->purgeCache();
}
});
$this->eventRegister(new class($this->cache, $this->entityCacheKey()) implements Event\Query\Truncate {
use Cache\CacheEventTrait;
public function execute(RepositoryInterface $repository, ?string $table = null, ?string $alias = null, ?string $schema = null): void
{
$this->purgeCache();
}
});
return $this;
}
protected function entityCacheKey() : string
{
return sprintf("%s.%s", $this->entityResolver->databaseName(), $this->entityResolver->tableName());
}
public function getFromCache(string $key) : mixed
{
$keys = $this->cache->get($this->entityCacheKey(), []);
if (in_array($key, $keys)) {
return $this->cache->get(sprintf("%s:%s:", $this->entityCacheKey(), $key));
}
return null;
}
public function setToCache(string $key, mixed $value) : void
{
$keys = $this->cache->get($this->entityCacheKey(), []);
if (! in_array($key, $keys)) {
$keys[] = $key;
$this->cache->set($this->entityCacheKey(), $keys);
}
$this->cache->set(sprintf("%s:%s:", $this->entityCacheKey(), $key), $value);
}
}

View File

@ -49,7 +49,12 @@ class PdoObject extends PDO {
if (false !== ( $statement = $this->prepare($sql) )) { if (false !== ( $statement = $this->prepare($sql) )) {
return $this->execute($statement, $parameters, true); return $this->execute($statement, $parameters, true);
} }
} }
catch(\PDOException $pdo) {
if ( substr($pdo->getMessage(), 0, 30) !== 'There is no active transaction' ) {
throw $pdo;
}
}
catch (\Throwable $e) { catch (\Throwable $e) {
throw new \PdoException($e->getMessage() . " `$sql` with data:" . json_encode($parameters)); throw new \PdoException($e->getMessage() . " `$sql` with data:" . json_encode($parameters));
} }

View File

@ -2,26 +2,23 @@
namespace Ulmus; namespace Ulmus;
use Psr\SimpleCache\CacheInterface;
use Ulmus\Adapter\AdapterInterface; use Ulmus\Adapter\AdapterInterface;
use Ulmus\Common\PdoObject; use Ulmus\Common\PdoObject;
class ConnectionAdapter class ConnectionAdapter
{ {
public string $name;
public array $configuration;
protected AdapterInterface $adapter; protected AdapterInterface $adapter;
protected PdoObject $pdo; protected PdoObject $pdo;
public function __construct(string $name = "default", array $configuration = [], bool $default = false) public function __construct(
{ public string $name = "default",
$this->name = $name; protected array $configuration = [],
public bool $default = false,
$this->configuration = $configuration; public ? CacheInterface $cacheObject = null
) {
Ulmus::registerAdapter($this, $default); Ulmus::registerAdapter($this, $default);
} }

View File

@ -5,7 +5,7 @@ namespace Ulmus\Entity\InformationSchema;
use Ulmus\EntityCollection, use Ulmus\EntityCollection,
Ulmus\Entity\Field\Datetime; Ulmus\Entity\Field\Datetime;
use Ulmus\{Attribute\Obj\Table as TableObj}; use Ulmus\{Attribute\Obj\Table as TableObj, Entity\EntityInterface};
use Ulmus\Attribute\Property\{Field, Filter, FilterJoin, Relation, Join, Virtual, Where}; use Ulmus\Attribute\Property\{Field, Filter, FilterJoin, Relation, Join, Virtual, Where};
#[TableObj(name: "tables", database: "information_schema")] #[TableObj(name: "tables", database: "information_schema")]
@ -83,6 +83,12 @@ class Table
public ? string $temporary; public ? string $temporary;
#[Relation(type: "oneToMany", key: "name", foreignKey: [ Column::class, 'tableName' ], entity: Column::class)] #[Relation(type: "oneToMany", key: "name", foreignKey: [ Column::class, 'tableName' ], entity: Column::class)]
#[Where('TABLE_SCHEMA', fieldValue: [ Column::class, 'tableSchema' ])] #[Where(field: 'TABLE_SCHEMA', generateValue: [ Table::class, 'getSchema' ])]
public EntityCollection $columns; public EntityCollection $columns;
# Awaiting PHP 8.5 https://wiki.php.net/rfc/closures_in_const_expr
public static function getSchema(Table $entity) : string
{
return $entity->schema;
}
} }

View File

@ -12,6 +12,6 @@ use Ulmus\Attribute\Property\{Field, Filter, FilterJoin, Relation, Join, Virtual
class Table extends \Ulmus\Entity\InformationSchema\Table class Table extends \Ulmus\Entity\InformationSchema\Table
{ {
#[Relation(type: "oneToMany", key: "name", foreignKey: [ Column::class, 'tableName' ], entity: Column::class)] #[Relation(type: "oneToMany", key: "name", foreignKey: [ Column::class, 'tableName' ], entity: Column::class)]
#[Where('TABLE_SCHEMA', fieldValue: [ Column::class, 'tableSchema' ])] #[Where('TABLE_SCHEMA', generateValue: [ Table::class, 'getSchema' ])]
public EntityCollection $columns; public EntityCollection $columns;
} }

View File

@ -235,7 +235,7 @@ trait EntityTrait {
#[Ignore] #[Ignore]
public static function field($name, null|string|false $alias = Repository::DEFAULT_ALIAS) : EntityField public static function field($name, null|string|false $alias = Repository::DEFAULT_ALIAS) : EntityField
{ {
$default = ( $alias === false ? '' : Repository::DEFAULT_ALIAS ); # bw compatibility, to be deprecated $default = ( $alias === false ? '' : static::repository()::DEFAULT_ALIAS ); # bw compatibility, to be deprecated
$alias = $alias ? Ulmus::repository(static::class)->adapter->adapter()->escapeIdentifier($alias, Adapter\AdapterInterface::IDENTIFIER_FIELD) : $default; $alias = $alias ? Ulmus::repository(static::class)->adapter->adapter()->escapeIdentifier($alias, Adapter\AdapterInterface::IDENTIFIER_FIELD) : $default;

View File

@ -0,0 +1,9 @@
<?php
namespace Ulmus\Event\Query;
use Ulmus\Repository\RepositoryInterface;
interface Alter {
public function execute(RepositoryInterface $repository, array $fields) : void;
}

View File

@ -0,0 +1,9 @@
<?php
namespace Ulmus\Event\Query;
use Ulmus\Repository\RepositoryInterface;
interface Create {
public function execute(RepositoryInterface $repository) : void;
}

View File

@ -0,0 +1,10 @@
<?php
namespace Ulmus\Event\Query;
use Ulmus\Entity\EntityInterface;
use Ulmus\Repository\RepositoryInterface;
interface Delete {
public function execute(RepositoryInterface $repository, EntityInterface $entity) : void;
}

View File

@ -0,0 +1,9 @@
<?php
namespace Ulmus\Event\Query;
use Ulmus\Repository\RepositoryInterface;
interface Insert {
public function execute(RepositoryInterface $repository, object|array $entity, ? array $dataset = null, bool $replace = false) : void;
}

View File

@ -0,0 +1,9 @@
<?php
namespace Ulmus\Event\Query;
use Ulmus\Repository\RepositoryInterface;
interface Truncate {
public function execute(RepositoryInterface $repository, ? string $table = null, ? string $alias = null, ? string $schema = null) : void;
}

View File

@ -0,0 +1,9 @@
<?php
namespace Ulmus\Event\Query;
use Ulmus\Repository\RepositoryInterface;
interface Update {
public function execute(RepositoryInterface $repository, object|array $entity, ? array $dataset = null, bool $replace = false) : void;
}

View File

@ -0,0 +1,10 @@
<?php
namespace Ulmus\Event\Repository;
use Ulmus\EntityCollection;
use Ulmus\Repository\RepositoryInterface;
interface CollectionFromQueryDatasetInterface {
public function execute(RepositoryInterface $repository, array &$data) : void;
}

View File

@ -2,8 +2,8 @@
namespace Ulmus\Event\Repository; namespace Ulmus\Event\Repository;
use Ulmus\EntityCollection; use Ulmus\{ EntityCollection, Repository\RepositoryInterface };
interface CollectionFromQueryInterface { interface CollectionFromQueryInterface {
public function execute(EntityCollection $collection) : EntityCollection; public function execute(RepositoryInterface $repository, EntityCollection $collection) : EntityCollection;
} }

View File

@ -15,6 +15,7 @@ class Where extends Fragment {
const CONDITION_OR = "OR"; const CONDITION_OR = "OR";
const CONDITION_NOT = "NOT"; const CONDITION_NOT = "NOT";
const COMPARISON_IN = "IN"; const COMPARISON_IN = "IN";
const COMPARISON_NOT_IN = "NOT IN";
const COMPARISON_IS = "IS"; const COMPARISON_IS = "IS";
const COMPARISON_NULL = "NULL"; const COMPARISON_NULL = "NULL";
@ -100,11 +101,13 @@ class Where extends Fragment {
{ {
$value = $this->value(); $value = $this->value();
$operator = $this->operator();
return $this->content ?: $this->content = implode(" ", array_filter([ return $this->content ?: $this->content = implode(" ", array_filter([
$this->condition, $this->condition,
$this->not ? Where::CONDITION_NOT : "", $this->not ? Where::CONDITION_NOT : "",
$this->field, $this->field,
$this->operator(), $operator,
$value, $value,
])); ]));
} }
@ -112,7 +115,11 @@ class Where extends Fragment {
protected function operator() : string protected function operator() : string
{ {
if ( is_array($this->value) ) { if ( is_array($this->value) ) {
return Where::COMPARISON_IN; if (true === $not = $this->not) {
$this->not = false;
}
return $not ? Where::COMPARISON_NOT_IN : Where::COMPARISON_IN;
} }
# whitelisting operators # whitelisting operators

View File

@ -4,5 +4,5 @@ namespace Ulmus;
class QueryBuilder extends QueryBuilder\Sql\MysqlQueryBuilder class QueryBuilder extends QueryBuilder\Sql\MysqlQueryBuilder
{ {
# Backward compatibility defaulting on MySQL/MariaDB query builder
} }

View File

@ -12,4 +12,5 @@ interface QueryBuilderInterface
public function reset() : void; public function reset() : void;
public function getFragment(string $class, int $index = 0) : ? QueryFragmentInterface; public function getFragment(string $class, int $index = 0) : ? QueryFragmentInterface;
public function removeFragment(QueryFragmentInterface|array|\Stringable|string $fragment) : void; public function removeFragment(QueryFragmentInterface|array|\Stringable|string $fragment) : void;
public function hashSerializedQuery() : string;
} }

View File

@ -432,8 +432,12 @@ class MysqlQueryBuilder extends SqlQueryBuilder
return array_shift($this->queryStack); return array_shift($this->queryStack);
} }
public function render(bool $skipToken = false) /* : mixed */ public function render(bool $skipToken = false) : mixed
{ {
if (isset($this->rendered)) {
return $this->rendered;
}
$sql = []; $sql = [];
usort($this->queryStack, function($q1, $q2) { usort($this->queryStack, function($q1, $q2) {
@ -444,7 +448,7 @@ class MysqlQueryBuilder extends SqlQueryBuilder
$sql[] = $fragment->render($skipToken); $sql[] = $fragment->render($skipToken);
} }
return implode(" ", $sql); return $this->rendered = implode(" ", $sql);
} }
public function reset() : void public function reset() : void
@ -453,8 +457,7 @@ class MysqlQueryBuilder extends SqlQueryBuilder
$this->whereConditionOperator = Query\Where::CONDITION_AND; $this->whereConditionOperator = Query\Where::CONDITION_AND;
$this->havingConditionOperator = Query\Where::CONDITION_AND; $this->havingConditionOperator = Query\Where::CONDITION_AND;
$this->parameterIndex = 0; $this->parameterIndex = 0;
unset($this->where, $this->having, $this->rendered, $this->hash);
unset($this->where, $this->having);
} }
public function getFragment(string $class, int $index = 0) : ? QueryFragmentInterface public function getFragment(string $class, int $index = 0) : ? QueryFragmentInterface

View File

@ -7,6 +7,9 @@ use Ulmus\Query\QueryFragmentInterface;
# TODO -> Extract from MysqlQueryBuilder to build an ISO/IEC 9075:2023 compatible layer for a basic SQL QueryBuilder # TODO -> Extract from MysqlQueryBuilder to build an ISO/IEC 9075:2023 compatible layer for a basic SQL QueryBuilder
class SqlQueryBuilder implements QueryBuilderInterface class SqlQueryBuilder implements QueryBuilderInterface
{ {
protected string $rendered;
protected string $hash;
public function push(QueryFragmentInterface $queryFragment): QueryBuilderInterface public function push(QueryFragmentInterface $queryFragment): QueryBuilderInterface
{ {
@ -18,7 +21,7 @@ class SqlQueryBuilder implements QueryBuilderInterface
// TODO: Implement pull() method. // TODO: Implement pull() method.
} }
public function render(bool $skipToken = false) public function render(bool $skipToken = false) : mixed
{ {
// TODO: Implement render() method. // TODO: Implement render() method.
} }
@ -37,4 +40,9 @@ class SqlQueryBuilder implements QueryBuilderInterface
{ {
// TODO: Implement removeFragment() method. // TODO: Implement removeFragment() method.
} }
public function hashSerializedQuery(): string
{
return $this->hash ??= md5(sprintf("%s:%s", $this->render(), serialize($this->parameters)));
}
} }

View File

@ -5,25 +5,27 @@ namespace Ulmus;
use Ulmus\Attribute\Property\{ use Ulmus\Attribute\Property\{
Field, OrderBy, Where, Having, Relation, Filter, Join, FilterJoin, WithJoin Field, OrderBy, Where, Having, Relation, Filter, Join, FilterJoin, WithJoin
}; };
use Psr\SimpleCache\CacheInterface;
use Ulmus\Common\EntityResolver; use Ulmus\Common\EntityResolver;
use Ulmus\Entity\EntityInterface;
use Ulmus\Repository\RepositoryInterface; use Ulmus\Repository\RepositoryInterface;
use Ulmus\Repository\WithOptionEnum; use Ulmus\Repository\WithOptionEnum;
class Repository implements RepositoryInterface class Repository implements RepositoryInterface
{ {
use EventTrait, Repository\ConditionTrait, Repository\EscapeTrait; use EventTrait, CacheTrait, Repository\ConditionTrait, Repository\EscapeTrait, Repository\QueryBuildingTrait;
const DEFAULT_ALIAS = "this"; const DEFAULT_ALIAS = "this";
public ? ConnectionAdapter $adapter;
public string $alias; public string $alias;
public string $entityClass; public string $entityClass;
public array $events = []; public array $events = [];
protected QueryBuilder\QueryBuilderInterface $queryBuilder; public ? ConnectionAdapter $adapters;
public readonly QueryBuilder\QueryBuilderInterface $queryBuilder;
protected EntityResolver $entityResolver; protected EntityResolver $entityResolver;
@ -33,10 +35,21 @@ class Repository implements RepositoryInterface
$this->entityClass = $entity; $this->entityClass = $entity;
$this->alias = $alias; $this->alias = $alias;
$this->entityResolver = Ulmus::resolveEntity($entity); $this->entityResolver = Ulmus::resolveEntity($entity);
$this->adapter = $adapter ?? $this->entityResolver->databaseAdapter();
$queryBuilder = $this->adapter->adapter()->queryBuilderClass(); if ($adapter) {
$this->queryBuilder = new $queryBuilder(); $this->adapter = $adapter;
$qbClass = $adapter->adapter()->queryBuilderClass();
$this->queryBuilder = new $qbClass();
}
else {
$this->adapter = $this->entityResolver->databaseAdapter();
$this->queryBuilder = $this->entityClass::queryBuilder();
}
if ($this->adapter->cacheObject) {
$this->attachCachingObject($this->adapter->cacheObject);
}
} }
public function __clone() public function __clone()
@ -113,6 +126,8 @@ class Repository implements RepositoryInterface
public function deleteAll() public function deleteAll()
{ {
$this->eventExecute(Event\Query\Delete::class, $this);
return $this->deleteSqlQuery()->runDeleteQuery(); return $this->deleteSqlQuery()->runDeleteQuery();
} }
@ -139,6 +154,8 @@ class Repository implements RepositoryInterface
else { else {
$pkField = key($primaryKeyDefinition); $pkField = key($primaryKeyDefinition);
$this->eventExecute(Event\Query\Delete::class, $this, $entity);
return $this->deleteFromPk($entity->$pkField); return $this->deleteFromPk($entity->$pkField);
} }
} }
@ -193,6 +210,8 @@ class Repository implements RepositoryInterface
$entity->entityFillFromDataset($dataset, true); $entity->entityFillFromDataset($dataset, true);
$this->eventExecute(Event\Query\Insert::class, $this, $entity, $dataset, $replace);
return (bool) ( $pkValue ?? $pdoObject->lastInsertId ); return (bool) ( $pkValue ?? $pdoObject->lastInsertId );
} }
else { else {
@ -211,6 +230,9 @@ class Repository implements RepositoryInterface
$entity->entityFillFromDataset($dataset, true); $entity->entityFillFromDataset($dataset, true);
# $fieldsAndValue ??= &$dataset;
$this->eventExecute(Event\Query\Update::class, $this, $entity, $dataset, $replace);
return $update ? (bool) $update->rowCount : false; return $update ? (bool) $update->rowCount : false;
} }
} }
@ -229,64 +251,6 @@ class Repository implements RepositoryInterface
return $changed; return $changed;
} }
public function insertAll(EntityCollection|array $collection, int $size = 1000) : int
{
if ( empty($collection) ) {
return 0;
}
elseif ( is_array($collection) ) {
$collection = $this->entityClass::entityCollection($collection);
}
foreach($collection as $entity) {
if ( ! $this->matchEntity($entity) ) {
throw new \Exception("Your entity class `" . get_class($entity) . "` cannot match entity type of repository `{$this->entityClass}`");
}
}
$dataset = $entity->toArray();
$primaryKeyDefinition = Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField();
if ( ! $entity->isLoaded() ) {
# $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();
if ( ( 0 !== $statement->lastInsertId ) &&
( null !== $primaryKeyDefinition )) {
$pkField = key($primaryKeyDefinition);
$dataset[$pkField] = $statement->lastInsertId;
}
$entity->entityFillFromDataset($dataset, true);
return (bool) $statement->lastInsertId;
}
else {
if ( $primaryKeyDefinition === null ) {
throw new \Exception(sprintf("No primary key found for entity %s", $this->entityClass));
}
$diff = $fieldsAndValue ?? $this->generateWritableDataset($entity);
if ( [] !== $diff ) {
$pkField = key($primaryKeyDefinition);
$pkFieldName = $primaryKeyDefinition[$pkField]->name ?? $pkField;
$this->where($pkFieldName, $dataset[$pkFieldName]);
$update = $this->updateSqlQuery($diff)->runUpdateQuery();
$entity->entityFillFromDataset($dataset, true);
return $update ? (bool) $update->rowCount : false;
}
}
return 0;
}
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);
@ -303,35 +267,34 @@ class Repository implements RepositoryInterface
return $changed; return $changed;
} }
public function truncate(? string $table = null, ? string $alias = null, ? string $schema = null) : self
public function createTable() : mixed
{ {
$schema = $schema ?: $this->entityResolver->schemaName(); $this->eventExecute(Event\Query\Create::class, $this);
$this->queryBuilder->truncate($this->escapeTable($table ?: $this->entityResolver->tableName()), $this->escapeIdentifier($alias ?: $this->alias), $this->escapedDatabase(), $schema ? $this->escapeSchema($schema) : null);
$this->finalizeQuery();
# ??? $result = Ulmus::runSelectQuery($this->queryBuilder, $this->adapter);
return $this;
}
public function createTable()
{
return $this->createSqlQuery()->runQuery(); return $this->createSqlQuery()->runQuery();
} }
public function alterTable(array $fields) public function alterTable(array $fields) : mixed
{ {
$this->eventExecute(Event\Query\Alter::class, $this, $fields);
return $this->alterSqlQuery($fields)->runQuery(); return $this->alterSqlQuery($fields)->runQuery();
} }
public function listTables(? string $database = null) public function truncateTable() : mixed
{
$this->eventExecute(Event\Query\Truncate::class, $this, $table, $alias, $schema);
return $this->truncate()->runQuery();
}
public function listTables(? string $database = null) : mixed
{ {
return $this->showTablesSqlQuery($database)->runQuery(); return $this->showTablesSqlQuery($database)->runQuery();
} }
public function listColumns(? string $table = null) public function listColumns(? string $table = null) : EntityCollection
{ {
$table ??= $this->entityResolver->tableName(); $table ??= $this->entityResolver->tableName();
@ -358,11 +321,8 @@ class Repository implements RepositoryInterface
return (string) $e1 !== (string) $e2; return (string) $e1 !== (string) $e2;
}); });
# return array_diff_assoc($oldValues ? $dataset : $array , $oldValues ? $array : $dataset );
} }
public function generateWritableDataset(object $entity, bool $oldValues = false) : array public function generateWritableDataset(object $entity, bool $oldValues = false) : array
{ {
$intersect = []; $intersect = [];
@ -370,7 +330,7 @@ class Repository implements RepositoryInterface
$dataset = $this->generateDatasetDiff($entity, $oldValues); $dataset = $this->generateDatasetDiff($entity, $oldValues);
foreach($dataset as $field => $value) { foreach($dataset as $field => $value) {
if ( false === ( $this->entityResolver->searchFieldAnnotation($field, [ Field::class, Field::class ], false)->readonly ?? false ) ) { if ( false === ( $this->entityResolver->searchFieldAnnotation($field, Field::class, false)->readonly ?? false ) ) {
$intersect[$field] = $field; $intersect[$field] = $field;
} }
} }
@ -389,15 +349,6 @@ class Repository implements RepositoryInterface
} }
} }
public function removeQueryFragment(null|Query\QueryFragmentInterface|string|array $fragment) : self
{
foreach((array) $fragment as $item) {
$this->queryBuilder->removeFragment($item);
}
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 .= "$");
@ -431,167 +382,6 @@ class Repository implements RepositoryInterface
); );
} }
public function select(array|string|\Stringable $fields, bool $distinct = false) : self
{
$this->queryBuilder->select($fields, $distinct);
return $this;
}
public function distinct(array|string|\Stringable $fields) : self
{
$this->queryBuilder->select($fields);
$this->queryBuilder->getFragment(Query\Select::class)->distinct = true;
return $this;
}
public function insert(array $fieldlist, string $table, string $alias, ? string $schema, bool $replace = false) : self
{
$this->queryBuilder->insert($fieldlist, $this->escapeTable($table), $this->escapeIdentifier($alias), $this->escapedDatabase(), $schema, $replace);
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 ? $this->escapeIdentifier($alias) : null, $this->escapedDatabase(), $schema);
return $this;
}
public function set(array $dataset) : self
{
$keys = array_keys($dataset);
$escapedFields = array_combine($keys, array_map([ $this, 'escapeField' ], $keys));
$this->queryBuilder->set($dataset, $escapedFields);
return $this;
}
public function delete(...$args) : self
{
$this->queryBuilder->delete();
return $this;
}
public function from(string $table, ? string $alias, ? string $schema) : self
{
$this->queryBuilder->from($this->escapeTable($table), $alias ? $this->escapeIdentifier($alias) : null, $this->escapedDatabase(), $schema ? $this->escapeSchema($schema) : null);
return $this;
}
public function join(string $type, $table, string|\Stringable $field, mixed $value, ? string $alias = null, ? callable $callback = null) : self
{
$join = $this->queryBuilder->withJoin($type, $this->escapeTable($table), $field, $value, false, $alias ? $this->escapeIdentifier($alias) : null);
if ( $callback ) {
$callback($join);
}
return $this;
}
public function outerJoin(string $type, $table, string|\Stringable $field, mixed $value, ? string $alias = null, ? callable $callback = null) : self
{
$join = $this->queryBuilder->withJoin($type, $this->escapeTable($table), $field, $value, true, $alias ? $this->escapeIdentifier($alias) : null);
if ( $callback ) {
$callback($join);
}
return $this;
}
public function match() : self
{
}
public function notMatch() : self
{
}
public function between() : self
{
}
public function notBetween() : self
{
}
public function groupBy(string|\Stringable $field) : self
{
$this->queryBuilder->groupBy($field);
return $this;
}
public function groups(array $groups) : self
{
foreach($groups as $field ) {
$this->groupBy($field);
}
return $this;
}
public function orderBy(string|\Stringable $field, null|string|\Stringable $direction = null) : self
{
$this->queryBuilder->orderBy($field, $direction);
return $this;
}
# @UNTESTED
public function randomizeOrder() : self
{
$this->queryBuilder->orderBy(Common\Sql::function('RAND', Common\Sql::identifier('CURDATE()+0')));
return $this;
}
public function orders(array $orderList) : self
{
foreach($orderList as $field => $direction) {
if (is_numeric($field)) {
$this->orderBy($direction);
}
else {
# Associative array with direction
$this->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;
}
/* @TODO */ /* @TODO */
public function commit() : self public function commit() : self
{ {
@ -686,23 +476,23 @@ class Repository implements RepositoryInterface
$foreignKey = is_string($attribute->foreignKey) ? $entity::field($attribute->foreignKey, $alias) : $attribute->foreignKey; $foreignKey = is_string($attribute->foreignKey) ? $entity::field($attribute->foreignKey, $alias) : $attribute->foreignKey;
$this->join("LEFT", $entity::resolveEntity()->tableName(), $key, $foreignKey, $alias, function($join) use ($item, $entity, $alias, $options) { $this->join("LEFT", $entity::resolveEntity()->tableName(), $key, $foreignKey, $alias, function($join) use ($item, $entity, $alias, $options) {
if ( ! in_array(WithOptionEnum::SkipJoinWhere, $options)) { if ( ! in_array(WithOptionEnum::SkipJoinWhere, $options)) {
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ]) as $condition) { foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ]) as $condition) {
if ( ! is_object($condition->field) ) { if ( ! is_object($condition->field) ) {
$field = $this->entityClass::field($condition->field); $field = $this->entityClass::field($condition->field);
} }
else { else {
$field = clone $condition->field; $field = clone $condition->field;
} }
# Adding directly # Adding directly
if ( $field->entityClass === $entity ) { if ( $field->entityClass === $entity ) {
$field->alias = $alias; $field->alias = $alias;
$join->where(is_object($field) ? $field : $entity::field($field, $alias), $condition->getValue(), $condition->operator); $join->where(is_object($field) ? $field : $entity::field($field, $alias), $condition->getValue(), $condition->operator);
}
} }
} }
}
if ( ! in_array(WithOptionEnum::SkipJoinFilter, $options) ) { if ( ! in_array(WithOptionEnum::SkipJoinFilter, $options) ) {
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ FilterJoin::class ]) as $filter) { foreach ($this->entityResolver->searchFieldAnnotationList($item, [ FilterJoin::class ]) as $filter) {
@ -804,7 +594,7 @@ class Repository implements RepositoryInterface
foreach ($where as $condition) { foreach ($where as $condition) {
# $repository->where($condition->field, is_callable($condition->value) ? call_user_func_array($condition->value, [$this]) : $condition->getValue(), $condition->operator, $condition->condition); # $repository->where($condition->field, is_callable($condition->value) ? call_user_func_array($condition->value, [$this]) : $condition->getValue(), $condition->operator, $condition->condition);
$repository->where($condition->field, $condition->getValue($this), $condition->operator, $condition->condition); $repository->where($condition->field, $condition->getValue(/* why repository sent here ??? $this */), $condition->operator, $condition->condition);
} }
foreach ($order as $item) { foreach ($order as $item) {
@ -873,17 +663,21 @@ class Repository implements RepositoryInterface
$entityCollection = $entityClass::entityCollection(); $entityCollection = $entityClass::entityCollection();
$this->finalizeQuery(); $this->finalizeQuery();
foreach(Ulmus::iterateQueryBuilder($this->queryBuilder, $this->adapter) as $entityData) { $dataset = [];
$this->eventExecute(\Ulmus\Event\Repository\CollectionFromQueryDatasetInterface::class, $this, $dataset);
foreach($dataset ?: Ulmus::iterateQueryBuilder($this->queryBuilder, $this->adapter) as $entityData) {
$this->eventExecute(\Ulmus\Event\Repository\CollectionFromQueryItemInterface::class, $entityData); $this->eventExecute(\Ulmus\Event\Repository\CollectionFromQueryItemInterface::class, $entityData);
$entity = $this->instanciateEntity($entityClass); $entity = $this->instanciateEntity($entityClass);
$entity->loadedFromAdapter = $this->adapter->name; $entity->loadedFromAdapter = $this->adapter->name;
$entityCollection->append( $entity->resetVirtualProperties()->entityFillFromDataset($entityData) ); $entityCollection->append( $entity->entityFillFromDataset($entityData) );
} }
$this->eventExecute(Event\Repository\CollectionFromQueryInterface::class, $entityCollection); $this->eventExecute(Event\Repository\CollectionFromQueryInterface::class, $this, $entityCollection);
return $entityCollection; return $entityCollection;
} }
@ -925,110 +719,6 @@ class Repository implements RepositoryInterface
return Ulmus::runDeleteQuery($this->queryBuilder, $this->adapter); return Ulmus::runDeleteQuery($this->queryBuilder, $this->adapter);
} }
public function resetQuery() : self
{
$this->queryBuilder->reset();
return $this;
}
protected function insertSqlQuery(array $dataset, bool $replace = false) : self
{
if ( null === $insert = $this->queryBuilder->getFragment(Query\Insert::class) ) {
$this->insert(array_map([ $this, 'escapeField' ] , array_keys($dataset)), $this->entityResolver->tableName(), $this->alias, $this->entityResolver->schemaName(), $replace);
}
else {
$insert->replace = $replace;
}
$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) ) {
$fields = $this->entityResolver->fieldList(EntityResolver::KEY_COLUMN_NAME, true);
$this->select($this->entityClass::fields(array_map(fn($f) => $f->object->name ?? $f->name, $fields)));
}
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;
}
public function createSqlQuery() : self
{
if ( null === $this->queryBuilder->getFragment(Query\Create::class) ) {
$this->queryBuilder->create($this->adapter->adapter(), $this->escapeFieldList($this->entityResolver->fieldList(EntityResolver::KEY_ENTITY_NAME, true)), $this->escapeTable($this->entityResolver->tableName()), $this->entityResolver->schemaName());
}
return $this;
}
public function alterSqlQuery(array $fields) : self
{
if ( null === $this->queryBuilder->getFragment(Query\Alter::class) ) {
$this->queryBuilder->alter($this->adapter->adapter(), $fields, $this->escapeTable($this->entityResolver->tableName()), $this->entityResolver->schemaName());
}
return $this;
}
public function showDatabasesSqlQuery() : self
{
if ( null === $this->queryBuilder->getFragment(Query\Show::class) ) {
$this->queryBuilder->showDatabases();
}
return $this;
}
public function showTablesSqlQuery() : self
{
if ( null === $this->queryBuilder->getFragment(Query\Show::class) ) {
$this->queryBuilder->showTables();
}
return $this;
}
public function showColumnsSqlQuery(string $table) : self
{
if ( null === $this->queryBuilder->getFragment(Query\Show::class) ) {
$this->queryBuilder->showColumns($table);
}
return $this;
}
protected function fromRow($row) : self protected function fromRow($row) : self
{ {
@ -1039,15 +729,6 @@ class Repository implements RepositoryInterface
} }
public function getSqlQuery(bool $flush = true) : string
{
$result = $this->queryBuilder->render();
$flush and $this->queryBuilder->reset();
return $result;
}
public function instanciateEntityCollection(...$arguments) : EntityCollection public function instanciateEntityCollection(...$arguments) : EntityCollection
{ {
return $this->entityClass::entityCollection(...$arguments); return $this->entityClass::entityCollection(...$arguments);

View File

@ -151,7 +151,7 @@ trait ConditionTrait
return $this; return $this;
} }
public function removeQueryFragment(Query\Fragment|\Stringable|string|array $fragment) : self public function removeQueryFragment(null|Query\QueryFragmentInterface|string|\Stringable|array $fragment) : self
{ {
foreach((array) $fragment as $item) { foreach((array) $fragment as $item) {
$this->queryBuilder->removeFragment($item); $this->queryBuilder->removeFragment($item);

View File

@ -0,0 +1,292 @@
<?php
namespace Ulmus\Repository;
use Ulmus\{Common\EntityResolver, Query, Common};
trait QueryBuildingTrait
{
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()), $this->escapeIdentifier($alias ?: $this->alias), $this->escapedDatabase(), $schema ? $this->escapeSchema($schema) : null);
return $this;
}
public function select(array|string|\Stringable $fields, bool $distinct = false) : self
{
$this->queryBuilder->select($fields, $distinct);
return $this;
}
public function distinct(array|string|\Stringable $fields) : self
{
$this->queryBuilder->select($fields);
$this->queryBuilder->getFragment(Query\Select::class)->distinct = true;
return $this;
}
public function insert(array $fieldlist, string $table, string $alias, ? string $schema, bool $replace = false) : self
{
$this->queryBuilder->insert($fieldlist, $this->escapeTable($table), $this->escapeIdentifier($alias), $this->escapedDatabase(), $schema, $replace);
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 ? $this->escapeIdentifier($alias) : null, $this->escapedDatabase(), $schema);
return $this;
}
public function set(array $dataset) : self
{
$keys = array_keys($dataset);
$escapedFields = array_combine($keys, array_map([ $this, 'escapeField' ], $keys));
$this->queryBuilder->set($dataset, $escapedFields);
return $this;
}
public function delete(...$args) : self
{
$this->queryBuilder->delete();
return $this;
}
public function from(string $table, ? string $alias, ? string $schema) : self
{
$this->queryBuilder->from($this->escapeTable($table), $alias ? $this->escapeIdentifier($alias) : null, $this->escapedDatabase(), $schema ? $this->escapeSchema($schema) : null);
return $this;
}
public function join(string $type, $table, string|\Stringable $field, mixed $value, ? string $alias = null, ? callable $callback = null) : self
{
$join = $this->queryBuilder->withJoin($type, $this->escapeTable($table), $field, $value, false, $alias ? $this->escapeIdentifier($alias) : null);
if ( $callback ) {
$callback($join);
}
return $this;
}
public function outerJoin(string $type, $table, string|\Stringable $field, mixed $value, ? string $alias = null, ? callable $callback = null) : self
{
$join = $this->queryBuilder->withJoin($type, $this->escapeTable($table), $field, $value, true, $alias ? $this->escapeIdentifier($alias) : null);
if ( $callback ) {
$callback($join);
}
return $this;
}
public function match() : self
{
}
public function notMatch() : self
{
}
public function between() : self
{
}
public function notBetween() : self
{
}
public function groupBy(string|\Stringable $field) : self
{
$this->queryBuilder->groupBy($field);
return $this;
}
public function groups(array $groups) : self
{
foreach($groups as $field ) {
$this->groupBy($field);
}
return $this;
}
public function orderBy(string|\Stringable $field, null|string|\Stringable $direction = null) : self
{
$this->queryBuilder->orderBy($field, $direction);
return $this;
}
# @UNTESTED
public function randomizeOrder() : self
{
$this->queryBuilder->orderBy(Common\Sql::function('RAND', Common\Sql::identifier('CURDATE()+0')));
return $this;
}
public function orders(array $orderList) : self
{
foreach($orderList as $field => $direction) {
if (is_numeric($field)) {
$this->orderBy($direction);
}
else {
# Associative array with direction
$this->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 getSqlQuery(bool $flush = true) : string
{
$result = $this->queryBuilder->render();
$flush and $this->queryBuilder->reset();
return $result;
}
public function resetQuery() : self
{
$this->queryBuilder->reset();
return $this;
}
protected function insertSqlQuery(array $dataset, bool $replace = false) : self
{
if ( null === $insert = $this->queryBuilder->getFragment(Query\Insert::class) ) {
$this->insert(array_map([ $this, 'escapeField' ] , array_keys($dataset)), $this->entityResolver->tableName(), $this->alias, $this->entityResolver->schemaName(), $replace);
}
else {
$insert->replace = $replace;
}
$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) ) {
$fields = $this->entityResolver->fieldList(EntityResolver::KEY_COLUMN_NAME, true);
$this->select($this->entityClass::fields(array_map(fn($f) => $f->object->name ?? $f->name, $fields)));
}
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;
}
public function createSqlQuery() : self
{
if ( null === $this->queryBuilder->getFragment(Query\Create::class) ) {
$this->queryBuilder->create($this->adapter->adapter(), $this->escapeFieldList($this->entityResolver->fieldList(EntityResolver::KEY_ENTITY_NAME, true)), $this->escapeTable($this->entityResolver->tableName()), $this->entityResolver->schemaName());
}
return $this;
}
public function alterSqlQuery(array $fields) : self
{
if ( null === $this->queryBuilder->getFragment(Query\Alter::class) ) {
$this->queryBuilder->alter($this->adapter->adapter(), $fields, $this->escapeTable($this->entityResolver->tableName()), $this->entityResolver->schemaName());
}
return $this;
}
public function showDatabasesSqlQuery() : self
{
if ( null === $this->queryBuilder->getFragment(Query\Show::class) ) {
$this->queryBuilder->showDatabases();
}
return $this;
}
public function showTablesSqlQuery() : self
{
if ( null === $this->queryBuilder->getFragment(Query\Show::class) ) {
$this->queryBuilder->showTables();
}
return $this;
}
public function showColumnsSqlQuery(string $table) : self
{
if ( null === $this->queryBuilder->getFragment(Query\Show::class) ) {
$this->queryBuilder->showColumns($table);
}
return $this;
}
}

View File

@ -2,7 +2,7 @@
namespace Ulmus\Repository; namespace Ulmus\Repository;
use Ulmus\{Repository, Entity}; use Ulmus\{EntityCollection, Repository, Entity};
class SqliteRepository extends Repository { class SqliteRepository extends Repository {
use JsonConditionTrait; use JsonConditionTrait;
@ -19,7 +19,7 @@ class SqliteRepository extends Repository {
} }
public function listColumns(? string $table = null) public function listColumns(? string $table = null) : EntityCollection
{ {
$table ??= $this->entityResolver->tableName(); $table ??= $this->entityResolver->tableName();
$this->showColumnsSqlQuery($table); $this->showColumnsSqlQuery($table);

View File

@ -64,7 +64,7 @@ abstract class Ulmus
} }
public static function runSelectQuery(QueryBuilder\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) public static function runSelectQuery(QueryBuilder\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null)
{ {
$dataset = static::connector($adapter)->select($queryBuilder->render(), array_merge($queryBuilder->values ?? [], $queryBuilder->parameters ?? [])); $dataset = static::connector($adapter)->select($queryBuilder->render(), array_merge($queryBuilder->values ?? [], $queryBuilder->parameters ?? []));
$queryBuilder->reset(); $queryBuilder->reset();
@ -74,7 +74,6 @@ abstract class Ulmus
public static function runQuery(QueryBuilder\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) public static function runQuery(QueryBuilder\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null)
{ {
$return = static::connector($adapter)->runQuery($queryBuilder->render(), array_merge($queryBuilder->values ?? [], $queryBuilder->parameters ?? [])); $return = static::connector($adapter)->runQuery($queryBuilder->render(), array_merge($queryBuilder->values ?? [], $queryBuilder->parameters ?? []));
$queryBuilder->reset(); $queryBuilder->reset();
return $return; return $return;