Compare commits
5
Commits
v1.0.0
...
761e0401b0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
761e0401b0 | ||
|
|
1ec582d1ac | ||
|
|
9556dea849 | ||
|
|
bfddf84564 | ||
|
|
e0ec140661 |
No files matched your search
@@ -27,6 +27,7 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface
|
|||||||
public function connect() : PdoObject
|
public function connect() : PdoObject
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
#$pdo = new PdoObject($this->buildDataSourceName(), null, null);
|
||||||
$pdo = new PdoObject\SqlitePdoObject($this->buildDataSourceName(), null, null);
|
$pdo = new PdoObject\SqlitePdoObject($this->buildDataSourceName(), null, null);
|
||||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||||
$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
|
$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ class Index {
|
|||||||
public IndexTypeEnum $type = IndexTypeEnum::Unique,
|
public IndexTypeEnum $type = IndexTypeEnum::Unique,
|
||||||
public null|string $name = null,
|
public null|string $name = null,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ namespace Ulmus\Common;
|
|||||||
use Notes\Common\ReflectedClass;
|
use Notes\Common\ReflectedClass;
|
||||||
use Notes\Common\ReflectedProperty;
|
use Notes\Common\ReflectedProperty;
|
||||||
use Psr\SimpleCache\CacheInterface;
|
use Psr\SimpleCache\CacheInterface;
|
||||||
|
use Ulmus\Attribute\Obj\Index;
|
||||||
use Ulmus\Ulmus,
|
use Ulmus\Ulmus,
|
||||||
Ulmus\Attribute\Obj\Table,
|
Ulmus\Attribute\Obj\Table,
|
||||||
Ulmus\Attribute\Obj\AdapterAttributeInterface,
|
Ulmus\Attribute\Obj\AdapterAttributeInterface,
|
||||||
@@ -214,9 +215,9 @@ class EntityResolver {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCompoundKeyFields() : ? array
|
public function getCompoundKeyFields() : ? Index
|
||||||
{
|
{
|
||||||
return null;
|
return $this->getAttributeImplementing(Index::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUniqueFields() : ? array
|
public function getUniqueFields() : ? array
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class PdoObject extends 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), (int) $e->getCode(), $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -83,7 +83,7 @@ class PdoObject extends PDO {
|
|||||||
$this->lastInsertId = null;
|
$this->lastInsertId = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ( ! $this->inTransaction() ) {
|
if (! $this->inTransaction()) {
|
||||||
$this->beginTransaction();
|
$this->beginTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,8 +113,6 @@ class PdoObject extends PDO {
|
|||||||
catch (\Throwable $e) {
|
catch (\Throwable $e) {
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function bindVariables(PDOStatement $statement, array &$parameters) : void
|
protected function bindVariables(PDOStatement $statement, array &$parameters) : void
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class SqlPdoObject extends \Ulmus\Common\PdoObject
|
|||||||
return $this->openTransaction();
|
return $this->openTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->exec('SAVEPOINT transaction_'.$this->openedTransaction) !== false;
|
return $this->exec("SAVEPOINT transaction_{$this->openedTransaction}") !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function commit() : bool
|
public function commit() : bool
|
||||||
@@ -21,16 +21,19 @@ class SqlPdoObject extends \Ulmus\Common\PdoObject
|
|||||||
return parent::commit();
|
return parent::commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return $this->exec("RELEASE SAVEPOINT transaction_{$this->openedTransaction}") !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rollback() : bool
|
public function rollback() : bool
|
||||||
{
|
{
|
||||||
if (0 !== $this->openedTransaction) {
|
if ($this->openedTransaction > 1) {
|
||||||
return $this->exec('ROLLBACK TO transaction_' . $this->openedTransaction--) !== false;
|
return $this->exec('ROLLBACK TO transaction_' . $this->openedTransaction--) !== false;
|
||||||
}
|
}
|
||||||
|
elseif ($this->openedTransaction-- === 1) {
|
||||||
|
return parent::rollback();
|
||||||
|
}
|
||||||
|
|
||||||
return parent::rollback();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function openTransaction() : bool
|
protected function openTransaction() : bool
|
||||||
|
|||||||
@@ -17,6 +17,19 @@ class SqlitePdoObject extends SqlPdoObject
|
|||||||
return $this->exec("COMMIT") !== false;
|
return $this->exec("COMMIT") !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this->exec("RELEASE SAVEPOINT transaction_{$this->openedTransaction}") !== false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rollback() : bool
|
||||||
|
{
|
||||||
|
if ($this->openedTransaction > 1) {
|
||||||
|
return $this->exec('ROLLBACK TO transaction_' . $this->openedTransaction--) !== false;
|
||||||
|
}
|
||||||
|
elseif ($this->openedTransaction-- === 1) {
|
||||||
|
# We must do it manually since opening a transaction manually stucks PDO into thinking we've got no transaction opened
|
||||||
|
return $this->exec('ROLLBACK') !== false;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,27 @@
|
|||||||
|
|
||||||
namespace Ulmus\Container;
|
namespace Ulmus\Container;
|
||||||
|
|
||||||
|
use Ulmus\ConnectionAdapter;
|
||||||
|
|
||||||
class AdapterProxy
|
class AdapterProxy
|
||||||
{
|
{
|
||||||
public array $connections = [];
|
public array $connections = [];
|
||||||
|
|
||||||
public function __construct(...$arguments)
|
public function __construct(...$arguments)
|
||||||
{
|
{
|
||||||
$this->connections = $arguments;
|
$this->push(array_filter($arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function push(array|ConnectionAdapter $connection) : void
|
||||||
|
{
|
||||||
|
foreach((array) $connection as $adapter) {
|
||||||
|
foreach($this->connections as $existing) {
|
||||||
|
if ($adapter === $existing) {
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->connections[] = $adapter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,7 +343,20 @@ class EntityCollection extends \ArrayObject implements \JsonSerializable {
|
|||||||
$list[] = $entity instanceof \JsonSerializable ? $entity->jsonSerialize() : $entity->toArray($includeRelations);
|
$list[] = $entity instanceof \JsonSerializable ? $entity->jsonSerialize() : $entity->toArray($includeRelations);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $list;
|
return $this->utf8ize($list, "UTF-8", "UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function utf8ize(mixed $content) {
|
||||||
|
if (is_array($content)) {
|
||||||
|
foreach ($content as $key => $value) {
|
||||||
|
$content[$key] = $this->utf8ize($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif (is_string($content)) {
|
||||||
|
return mb_convert_encoding($content, "UTF-8", "UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fromArray(array $datasets, ? string /*stringable*/ $entityClass = null) : self
|
public function fromArray(array $datasets, ? string /*stringable*/ $entityClass = null) : self
|
||||||
|
|||||||
+21
-7
@@ -43,12 +43,12 @@ trait EntityTrait {
|
|||||||
#[Ignore]
|
#[Ignore]
|
||||||
public function initializeEntity(iterable|null $dataset = null) : void
|
public function initializeEntity(iterable|null $dataset = null) : void
|
||||||
{
|
{
|
||||||
|
$this->datasetHandler = new DatasetHandler(static::resolveEntity(), $this->entityStrictFieldsDeclaration);
|
||||||
|
|
||||||
if ($dataset) {
|
if ($dataset) {
|
||||||
$this->fromArray($dataset);
|
$this->fromArray($dataset);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->datasetHandler = new DatasetHandler(static::resolveEntity(), $this->entityStrictFieldsDeclaration);
|
|
||||||
|
|
||||||
$this->resetVirtualProperties();
|
$this->resetVirtualProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,13 +144,27 @@ trait EntityTrait {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( null === $pkField = $this->resolveEntity()->getPrimaryKeyField($this) ) {
|
if ( null === $pkField = $this->resolveEntity()->getPrimaryKeyField() ) {
|
||||||
throw new Exception\EntityPrimaryKeyUnknown(sprintf("Entity %s has no field containing attributes 'primary_key'", static::class));
|
if ( null !== $compoundKeyFields = $this->resolveEntity()->getCompoundKeyFields() ) {
|
||||||
|
$loaded = false;
|
||||||
|
|
||||||
|
foreach ($compoundKeyFields->column as $column) {
|
||||||
|
$field = $this->resolveEntity()->field($column);
|
||||||
|
|
||||||
|
if (! $field->allowsNull() ) {
|
||||||
|
$loaded |= isset($this->{$field->name});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $loaded;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$key = key($pkField);
|
||||||
|
return isset($this->$key);
|
||||||
}
|
}
|
||||||
|
|
||||||
$key = key($pkField);
|
throw new Exception\EntityPrimaryKeyUnknown(sprintf("Entity %s has no field containing attributes 'primary_key'", static::class));
|
||||||
|
|
||||||
return isset($this->$key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Ignore]
|
#[Ignore]
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Query\Sqlite;
|
||||||
|
|
||||||
|
use Ulmus\Query\Fragment;
|
||||||
|
|
||||||
|
class Truncate extends Fragment {
|
||||||
|
|
||||||
|
const SQL_TOKEN = "DELETE FROM";
|
||||||
|
|
||||||
|
public int $order = -100;
|
||||||
|
|
||||||
|
public string $table;
|
||||||
|
|
||||||
|
public function set(string $tableName) : self
|
||||||
|
{
|
||||||
|
$this->table = $tableName;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() : string
|
||||||
|
{
|
||||||
|
return $this->renderSegments([
|
||||||
|
static::SQL_TOKEN, $this->table,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,7 +59,7 @@ class MysqlQueryBuilder extends SqlQueryBuilder
|
|||||||
$this->push($select);
|
$this->push($select);
|
||||||
}
|
}
|
||||||
|
|
||||||
$select->distinct = $distinct;
|
$select->distinct = $select->distinct || $distinct;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace Ulmus\QueryBuilder\Sql;
|
namespace Ulmus\QueryBuilder\Sql;
|
||||||
|
|
||||||
use Ulmus\Query;
|
use Ulmus\Query;
|
||||||
|
use Ulmus\Adapter\AdapterInterface;
|
||||||
|
|
||||||
# Soon to extends SqlQueryBuilder
|
# Soon to extends SqlQueryBuilder
|
||||||
class SqliteQueryBuilder extends MysqlQueryBuilder
|
class SqliteQueryBuilder extends MysqlQueryBuilder
|
||||||
@@ -38,4 +39,41 @@ class SqliteQueryBuilder extends MysqlQueryBuilder
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function insert(array $fieldlist, string $table, ? string $alias = null, ? string $database = null, ? string $schema = null, bool $replace = false) : self
|
||||||
|
{
|
||||||
|
return parent::insert($fieldlist, $table, $alias, null, null, $replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(string $table, ? string $alias = null, ? string $database = null, ? string $schema = null) : self
|
||||||
|
{
|
||||||
|
return parent::update($table, $alias, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(AdapterInterface $adapter, array $fieldlist, string $table, ? string $database = null, ? string $schema = null) : self
|
||||||
|
{
|
||||||
|
return parent::create($adapter, $fieldlist, $table, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alter(AdapterInterface $adapter, array $fieldlist, string $table, ? string $database = null, ? string $schema = null) : self
|
||||||
|
{
|
||||||
|
return parent::alter($adapter, $fieldlist, $table, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function from(string $table, ? string $alias = null, ? string $database = null, ? string $schema = null) : self
|
||||||
|
{
|
||||||
|
return parent::from($table, $alias, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function truncate(string $table, ? string $alias = null, ? string $database = null, ? string $schema = null) : self
|
||||||
|
{
|
||||||
|
$truncate = new Query\Sqlite\Truncate($this);
|
||||||
|
|
||||||
|
$this->push($truncate);
|
||||||
|
|
||||||
|
$truncate->set($table);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ namespace Ulmus\QueryBuilder;
|
|||||||
use Ulmus\Query\QueryFragmentInterface;
|
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
|
abstract class SqlQueryBuilder implements QueryBuilderInterface
|
||||||
{
|
{
|
||||||
protected string $rendered;
|
protected string $rendered;
|
||||||
|
|
||||||
|
|||||||
+17
-5
@@ -97,7 +97,7 @@ class Repository implements RepositoryInterface
|
|||||||
else {
|
else {
|
||||||
$pk = Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField();
|
$pk = Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField();
|
||||||
|
|
||||||
if (count($pk) === 1) {
|
if ($pk && count($pk) === 1) {
|
||||||
$field = key($pk);
|
$field = key($pk);
|
||||||
|
|
||||||
$this->select(Common\Sql::function('COUNT', Common\Sql::raw("DISTINCT " . $this->entityClass::field($field))));
|
$this->select(Common\Sql::function('COUNT', Common\Sql::raw("DISTINCT " . $this->entityClass::field($field))));
|
||||||
@@ -216,7 +216,12 @@ class Repository implements RepositoryInterface
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ( $primaryKeyDefinition === null ) {
|
if ( $primaryKeyDefinition === null ) {
|
||||||
throw new \Exception(sprintf("No primary key found for entity %s", $this->entityClass));
|
if ( null !== $compoundKeyFields = Ulmus::resolveEntity($this->entityClass)->getCompoundKeyFields() ) {
|
||||||
|
throw new \Exception("TO DO!");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new \Exception(sprintf("No primary key found for entity %s", $this->entityClass));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$diff = $fieldsAndValue ?? $this->generateWritableDataset($entity);
|
$diff = $fieldsAndValue ?? $this->generateWritableDataset($entity);
|
||||||
@@ -319,6 +324,10 @@ class Repository implements RepositoryInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_null($e1) || is_null($e2)) {
|
||||||
|
return $e1 !== $e2;
|
||||||
|
}
|
||||||
|
|
||||||
return (string) $e1 !== (string) $e2;
|
return (string) $e1 !== (string) $e2;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -407,7 +416,9 @@ class Repository implements RepositoryInterface
|
|||||||
|
|
||||||
public function withJoin(string|array $fields, array $options = []) : self
|
public function withJoin(string|array $fields, array $options = []) : self
|
||||||
{
|
{
|
||||||
if ( null === $this->queryBuilder->getFragment(Query\Select::class) ) {
|
$canSelect = null === $this->queryBuilder->getFragment(Query\Select::class);
|
||||||
|
|
||||||
|
if ( $canSelect ) {
|
||||||
$select = $this->entityResolver->fieldList(EntityResolver::KEY_COLUMN_NAME, true);
|
$select = $this->entityResolver->fieldList(EntityResolver::KEY_COLUMN_NAME, true);
|
||||||
$this->select($this->entityClass::fields(array_map(fn($f) => $f['object']->name ?? $f['name'], $select)));
|
$this->select($this->entityClass::fields(array_map(fn($f) => $f['object']->name ?? $f['name'], $select)));
|
||||||
}
|
}
|
||||||
@@ -442,8 +453,9 @@ class Repository implements RepositoryInterface
|
|||||||
|
|
||||||
$name = $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Field::class ])->name ?? $field->name;
|
$name = $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Field::class ])->name ?? $field->name;
|
||||||
|
|
||||||
|
if ($canSelect) {
|
||||||
$this->select("$escAlias.$fieldName as $alias\${$name}");
|
$this->select("$escAlias.$fieldName as $alias\${$name}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,6 @@ abstract class SearchParameter {
|
|||||||
|
|
||||||
public function getParameters() : array
|
public function getParameters() : array
|
||||||
{
|
{
|
||||||
return array_filter((array) $this->parameter);
|
return array_filter((array) ( $this->parameter ?? [] ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,8 @@ trait SearchRequestFromRequestTrait
|
|||||||
case $attribute instanceof SearchManual:
|
case $attribute instanceof SearchManual:
|
||||||
if ($attribute->toggle) {
|
if ($attribute->toggle) {
|
||||||
$this->$propertyName = !empty($value);
|
$this->$propertyName = !empty($value);
|
||||||
} elseif ($value !== null) {
|
}
|
||||||
|
elseif ($value !== null) {
|
||||||
foreach ($property->getTypes() as $type) {
|
foreach ($property->getTypes() as $type) {
|
||||||
$enum = $type->type;
|
$enum = $type->type;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user