Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9507a1e596 | ||
|
|
f197443871 | ||
|
|
f6bbdda137 | ||
|
|
eb505f4772 | ||
|
|
25ab845886 | ||
|
|
c417b00ed4 |
No files matched your search
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace Ulmus\Adapter;
|
namespace Ulmus\Adapter;
|
||||||
|
|
||||||
use Ulmus\Common\PdoObject;
|
|
||||||
use Ulmus\Migration\FieldDefinition;
|
use Ulmus\Migration\FieldDefinition;
|
||||||
|
|
||||||
interface AdapterInterface {
|
interface AdapterInterface {
|
||||||
@@ -12,7 +11,7 @@ interface AdapterInterface {
|
|||||||
public const IDENTIFIER_SCHEMA = 4;
|
public const IDENTIFIER_SCHEMA = 4;
|
||||||
public const IDENTIFIER_VALUE = 5;
|
public const IDENTIFIER_VALUE = 5;
|
||||||
|
|
||||||
public function connect() : object /* | PdoObject|mixed */;
|
public function connect() : object;
|
||||||
public function buildDataSourceName() : string;
|
public function buildDataSourceName() : string;
|
||||||
public function setup(array $configuration) : void;
|
public function setup(array $configuration) : void;
|
||||||
public function writableValue(/* mixed */ $value); /*: mixed*/
|
public function writableValue(/* mixed */ $value); /*: mixed*/
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Ulmus\Adapter;
|
namespace Ulmus\Adapter;
|
||||||
|
|
||||||
use Ulmus\Common\PdoObject;
|
use Ulmus\Pdo;
|
||||||
|
|
||||||
use Ulmus\Exception\AdapterConfigurationException;
|
use Ulmus\Exception\AdapterConfigurationException;
|
||||||
|
|
||||||
@@ -83,10 +83,10 @@ class MsSQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function connect() : PdoObject
|
public function connect() : \PDO
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$pdo = new PdoObject($this->buildDataSourceName(), $this->username, $this->password);
|
$pdo = new Pdo\Mssql($this->buildDataSourceName(), $this->username, $this->password);
|
||||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||||
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
|
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
|
||||||
$pdo->setAttribute(\PDO::SQLSRV_ATTR_ENCODING, \PDO::SQLSRV_ENCODING_UTF8);
|
$pdo->setAttribute(\PDO::SQLSRV_ATTR_ENCODING, \PDO::SQLSRV_ENCODING_UTF8);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use Ulmus\Migration\FieldDefinition;
|
|||||||
use Ulmus\Migration\MigrateInterface;
|
use Ulmus\Migration\MigrateInterface;
|
||||||
use Ulmus\Migration\SqlMigrationTrait;
|
use Ulmus\Migration\SqlMigrationTrait;
|
||||||
use Ulmus\QueryBuilder\Sql;
|
use Ulmus\QueryBuilder\Sql;
|
||||||
use Ulmus\Common\PdoObject;
|
use Ulmus\Pdo;
|
||||||
|
|
||||||
use Ulmus\Exception\AdapterConfigurationException;
|
use Ulmus\Exception\AdapterConfigurationException;
|
||||||
use Ulmus\Repository;
|
use Ulmus\Repository;
|
||||||
@@ -71,10 +71,10 @@ class MySQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function connect() : PdoObject
|
public function connect() : \PDO
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$pdo = new PdoObject($this->buildDataSourceName(), $this->username, $this->password);
|
$pdo = new Pdo\Mysql($this->buildDataSourceName(), $this->username, $this->password);
|
||||||
$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);
|
||||||
$pdo->setAttribute(\PDO::ATTR_AUTOCOMMIT, false);
|
$pdo->setAttribute(\PDO::ATTR_AUTOCOMMIT, false);
|
||||||
|
|||||||
+39
-40
@@ -3,7 +3,7 @@
|
|||||||
namespace Ulmus\Adapter;
|
namespace Ulmus\Adapter;
|
||||||
|
|
||||||
use Collator;
|
use Collator;
|
||||||
use Ulmus\Common\PdoObject;
|
use Ulmus\Pdo;
|
||||||
use Ulmus\ConnectionAdapter;
|
use Ulmus\ConnectionAdapter;
|
||||||
use Ulmus\Entity;
|
use Ulmus\Entity;
|
||||||
use Ulmus\Migration\FieldDefinition;
|
use Ulmus\Migration\FieldDefinition;
|
||||||
@@ -24,16 +24,15 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface
|
|||||||
public null|array $pragmaClose = null,
|
public null|array $pragmaClose = null,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public function connect() : PdoObject
|
public function connect() : \Pdo
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
#$pdo = new PdoObject($this->buildDataSourceName(), null, null);
|
$pdo = new Pdo\Sqlite($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);
|
||||||
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
|
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
$pdo->onClose = function(PdoObject $obj) {
|
$pdo->onClose = function(\PDO $obj) {
|
||||||
static::registerPragma($obj, $this->pragmaClose);
|
static::registerPragma($obj, $this->pragmaClose);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -158,28 +157,28 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface
|
|||||||
return QueryBuilder\Sql\SqliteQueryBuilder::class;
|
return QueryBuilder\Sql\SqliteQueryBuilder::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function exportFunctions(PdoObject $pdo) : void
|
public function exportFunctions(\PDO $pdo) : void
|
||||||
{
|
{
|
||||||
$pdo->sqliteCreateFunction('if', fn($comparison, $yes, $no) => $comparison ? $yes : $no, 3);
|
$pdo->createFunction('if', fn($comparison, $yes, $no) => $comparison ? $yes : $no, 3);
|
||||||
$pdo->sqliteCreateFunction('length', fn($string) => strlen($string), 1);
|
$pdo->createFunction('length', fn($string) => strlen($string), 1);
|
||||||
$pdo->sqliteCreateFunction('lcase', fn($string) => strtolower($string), 1);
|
$pdo->createFunction('lcase', fn($string) => strtolower($string), 1);
|
||||||
$pdo->sqliteCreateFunction('ucase', fn($string) => strtoupper($string), 1);
|
$pdo->createFunction('ucase', fn($string) => strtoupper($string), 1);
|
||||||
$pdo->sqliteCreateFunction('md5', fn($string) => md5($string), 1);
|
$pdo->createFunction('md5', fn($string) => md5($string), 1);
|
||||||
$pdo->sqliteCreateFunction('sha1', fn($string) => sha1($string), 1);
|
$pdo->createFunction('sha1', fn($string) => sha1($string), 1);
|
||||||
$pdo->sqliteCreateFunction('sha256', fn($string) => hash('sha256', $string), 1);
|
$pdo->createFunction('sha256', fn($string) => hash('sha256', $string), 1);
|
||||||
$pdo->sqliteCreateFunction('sha512', fn($string) => hash('sha512', $string), 1);
|
$pdo->createFunction('sha512', fn($string) => hash('sha512', $string), 1);
|
||||||
$pdo->sqliteCreateFunction('whirlpool', fn($string) => hash('whirlpool', $string), 1);
|
$pdo->createFunction('whirlpool', fn($string) => hash('whirlpool', $string), 1);
|
||||||
$pdo->sqliteCreateFunction('murmur3a', fn($string) => hash('murmur3a', $string), 1);
|
$pdo->createFunction('murmur3a', fn($string) => hash('murmur3a', $string), 1);
|
||||||
$pdo->sqliteCreateFunction('murmur3c', fn($string) => hash('murmur3c', $string), 1);
|
$pdo->createFunction('murmur3c', fn($string) => hash('murmur3c', $string), 1);
|
||||||
$pdo->sqliteCreateFunction('murmur3f', fn($string) => hash('murmur3f', $string), 1);
|
$pdo->createFunction('murmur3f', fn($string) => hash('murmur3f', $string), 1);
|
||||||
$pdo->sqliteCreateFunction('left', fn($string, $length) => substr($string, 0, $length), 2);
|
$pdo->createFunction('left', fn($string, $length) => substr($string, 0, $length), 2);
|
||||||
$pdo->sqliteCreateFunction('right', fn($string, $length) => substr($string, -$length), 2);
|
$pdo->createFunction('right', fn($string, $length) => substr($string, -$length), 2);
|
||||||
$pdo->sqliteCreateFunction('strcmp', fn($s1, $s2) => strcmp($s1, $s2), 2);
|
$pdo->createFunction('strcmp', fn($s1, $s2) => strcmp($s1, $s2), 2);
|
||||||
$pdo->sqliteCreateFunction('lpad', fn($string, $length, $pad) => str_pad($string, $length, $pad, STR_PAD_LEFT), 3);
|
$pdo->createFunction('lpad', fn($string, $length, $pad) => str_pad($string, $length, $pad, STR_PAD_LEFT), 3);
|
||||||
$pdo->sqliteCreateFunction('rpad', fn($string, $length, $pad) => str_pad($string, $length, $pad, STR_PAD_RIGHT), 3);
|
$pdo->createFunction('rpad', fn($string, $length, $pad) => str_pad($string, $length, $pad, STR_PAD_RIGHT), 3);
|
||||||
$pdo->sqliteCreateFunction('concat', fn(...$args) => implode('', $args), -1);
|
$pdo->createFunction('concat', fn(...$args) => implode('', $args), -1);
|
||||||
$pdo->sqliteCreateFunction('concat_ws', fn($separator, ...$args) => implode($separator, $args), -1);
|
$pdo->createFunction('concat_ws', fn($separator, ...$args) => implode($separator, $args), -1);
|
||||||
$pdo->sqliteCreateFunction('find_in_set', function($string, $string_list) {
|
$pdo->createFunction('find_in_set', function($string, $string_list) {
|
||||||
if ( $string === null || $string_list === null ) {
|
if ( $string === null || $string_list === null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -190,13 +189,13 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface
|
|||||||
|
|
||||||
return (int) in_array($string, explode(',', $string_list));
|
return (int) in_array($string, explode(',', $string_list));
|
||||||
}, 2);
|
}, 2);
|
||||||
$pdo->sqliteCreateFunction('day', fn($date) => ( new \DateTime($date) )->format('j'), 1);
|
$pdo->createFunction('day', fn($date) => ( new \DateTime($date) )->format('j'), 1);
|
||||||
$pdo->sqliteCreateFunction('month', fn($date) => ( new \DateTime($date) )->format('n'), 1);
|
$pdo->createFunction('month', fn($date) => ( new \DateTime($date) )->format('n'), 1);
|
||||||
$pdo->sqliteCreateFunction('year', fn($date) => ( new \DateTime($date) )->format('Y'), 1);
|
$pdo->createFunction('year', fn($date) => ( new \DateTime($date) )->format('Y'), 1);
|
||||||
$pdo->sqliteCreateFunction('str_to_date', fn($value, $format) => ($f = \DateTime::createFromFormat($format, $value)) ? $f->format('Y-m-d H:i:s') : false);
|
$pdo->createFunction('str_to_date', fn($value, $format) => ($f = \DateTime::createFromFormat($format, $value)) ? $f->format('Y-m-d H:i:s') : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function exportCollations(PdoObject $pdo) : void
|
public function exportCollations(\PDO $pdo) : void
|
||||||
{
|
{
|
||||||
|
|
||||||
if ( class_exists('Locale') ) {
|
if ( class_exists('Locale') ) {
|
||||||
@@ -205,28 +204,28 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface
|
|||||||
$collator->setStrength(Collator::TERTIARY);
|
$collator->setStrength(Collator::TERTIARY);
|
||||||
$collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON);
|
$collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON);
|
||||||
|
|
||||||
$pdo->sqliteCreateCollation('locale_cmp', fn($e1, $e2) => $collator->compare($e1, $e2));
|
$pdo->createCollation('locale_cmp', fn($e1, $e2) => $collator->compare($e1, $e2));
|
||||||
$pdo->sqliteCreateCollation('locale_cmp_desc', fn($e1, $e2) => $collator->compare($e2, $e1));
|
$pdo->createCollation('locale_cmp_desc', fn($e1, $e2) => $collator->compare($e2, $e1));
|
||||||
|
|
||||||
$collatorCi = new Collator(\Locale::getDefault());
|
$collatorCi = new Collator(\Locale::getDefault());
|
||||||
$collatorCi->setStrength(Collator::SECONDARY);
|
$collatorCi->setStrength(Collator::SECONDARY);
|
||||||
$collatorCi->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON);
|
$collatorCi->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON);
|
||||||
|
|
||||||
$pdo->sqliteCreateCollation('locale_cmp_ci', fn($e1, $e2) => $collatorCi->compare($e1, $e2));
|
$pdo->createCollation('locale_cmp_ci', fn($e1, $e2) => $collatorCi->compare($e1, $e2));
|
||||||
$pdo->sqliteCreateCollation('locale_cmp_ci_desc', fn($e1, $e2) => $collatorCi->compare($e2, $e1));
|
$pdo->createCollation('locale_cmp_ci_desc', fn($e1, $e2) => $collatorCi->compare($e2, $e1));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$comp = fn(string $e1, string $e2) => \iconv('UTF-8', 'ASCII//TRANSLIT', $e1) <=> \iconv('UTF-8', 'ASCII//TRANSLIT', $e2);
|
$comp = fn(string $e1, string $e2) => \iconv('UTF-8', 'ASCII//TRANSLIT', $e1) <=> \iconv('UTF-8', 'ASCII//TRANSLIT', $e2);
|
||||||
$pdo->sqliteCreateCollation('locale_cmp', fn($e1, $e2) => $comp);
|
$pdo->createCollation('locale_cmp', fn($e1, $e2) => $comp);
|
||||||
$pdo->sqliteCreateCollation('locale_cmp_desc', fn($e2, $e1) => $comp);
|
$pdo->createCollation('locale_cmp_desc', fn($e2, $e1) => $comp);
|
||||||
|
|
||||||
$compCi = fn(string $e1, string $e2) => strtoupper(\iconv('UTF-8', 'ASCII//TRANSLIT', $e1)) <=> strtoupper(\iconv('UTF-8', 'ASCII//TRANSLIT', $e2));
|
$compCi = fn(string $e1, string $e2) => strtoupper(\iconv('UTF-8', 'ASCII//TRANSLIT', $e1)) <=> strtoupper(\iconv('UTF-8', 'ASCII//TRANSLIT', $e2));
|
||||||
$pdo->sqliteCreateCollation('locale_cmp_ci', fn($e1, $e2) => $compCi);
|
$pdo->createCollation('locale_cmp_ci', fn($e1, $e2) => $compCi);
|
||||||
$pdo->sqliteCreateCollation('locale_cmp_ci_desc', fn($e2, $e1) => $compCi);
|
$pdo->createCollation('locale_cmp_ci_desc', fn($e2, $e1) => $compCi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function registerPragma(PdoObject $pdo, array $pragmaList) : void
|
public static function registerPragma(\PDO $pdo, array $pragmaList) : void
|
||||||
{
|
{
|
||||||
$builder = new QueryBuilder\Sql\SqliteQueryBuilder();
|
$builder = new QueryBuilder\Sql\SqliteQueryBuilder();
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use Ulmus\Repository;
|
|||||||
|
|
||||||
class Attribute
|
class Attribute
|
||||||
{
|
{
|
||||||
public static function handleArrayField(null|\Stringable|string|array $field, null|string|bool $alias = Repository::DEFAULT_ALIAS, string $separator = ', ') : mixed
|
public static function handleArrayField(null|\Stringable|string|array $field, null|string|false $alias = Repository::DEFAULT_ALIAS, string $separator = ', ') : mixed
|
||||||
{
|
{
|
||||||
if ( is_array($field) ) {
|
if ( is_array($field) ) {
|
||||||
if (count($field) < 2) {
|
if (count($field) < 2) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class Where {
|
|||||||
public null|array|\Closure $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, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getValue(/* null|EntityInterface */ $entity = null) : mixed
|
public function getValue(/* null|EntityInterface */ $entity = null) : mixed
|
||||||
|
|||||||
+1
-1
@@ -59,7 +59,7 @@ trait CacheTrait
|
|||||||
$this->eventRegister(new class($this->cache, $this->entityCacheKey()) implements Event\Query\Update {
|
$this->eventRegister(new class($this->cache, $this->entityCacheKey()) implements Event\Query\Update {
|
||||||
use Cache\CacheEventTrait;
|
use Cache\CacheEventTrait;
|
||||||
|
|
||||||
public function execute(RepositoryInterface $repository, object|array $entity, ?array $dataset = null, bool $replace = false): void
|
public function execute(RepositoryInterface $repository, object|array $entity, array $dataset, array $diff, bool $replace = false): void
|
||||||
{
|
{
|
||||||
$this->purgeCache();
|
$this->purgeCache();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,11 @@ namespace Ulmus;
|
|||||||
use Psr\SimpleCache\CacheInterface;
|
use Psr\SimpleCache\CacheInterface;
|
||||||
use Ulmus\Adapter\AdapterInterface;
|
use Ulmus\Adapter\AdapterInterface;
|
||||||
|
|
||||||
use Ulmus\Common\PdoObject;
|
|
||||||
|
|
||||||
class ConnectionAdapter
|
class ConnectionAdapter
|
||||||
{
|
{
|
||||||
protected AdapterInterface $adapter;
|
protected AdapterInterface $adapter;
|
||||||
|
|
||||||
protected PdoObject $pdo;
|
protected \PDO $pdo;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $name = "default",
|
public string $name = "default",
|
||||||
@@ -63,7 +61,7 @@ class ConnectionAdapter
|
|||||||
return $this->adapter;
|
return $this->adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function pdo() : PdoObject
|
public function pdo() : \PDO
|
||||||
{
|
{
|
||||||
return $this->pdo ?? $this->connect()->pdo;
|
return $this->pdo ?? $this->connect()->pdo;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,4 @@ use Ulmus\Entity\EntityObjectInterface;
|
|||||||
|
|
||||||
class Datetime extends \DateTime implements EntityObjectInterface, \JsonSerializable {
|
class Datetime extends \DateTime implements EntityObjectInterface, \JsonSerializable {
|
||||||
use DatetimeTrait;
|
use DatetimeTrait;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ trait DatetimeTrait
|
|||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTime($hour, $minute, $second = 0, $microsecond = 0) : \DateTimeInterface
|
public function setTime($hour, $minute, $second = 0, $microsecond = 0) : \DateTime
|
||||||
{
|
{
|
||||||
$fromParent = parent::setTime($hour, $minute, $second, $microsecond);
|
$fromParent = parent::setTime($hour, $minute, $second, $microsecond);
|
||||||
|
|
||||||
@@ -65,4 +65,4 @@ trait DatetimeTrait
|
|||||||
{
|
{
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ use Ulmus\Repository;
|
|||||||
#[\Ulmus\Attribute\Obj\Table(name: "sqlite_master")]
|
#[\Ulmus\Attribute\Obj\Table(name: "sqlite_master")]
|
||||||
class Table extends Schema
|
class Table extends Schema
|
||||||
{
|
{
|
||||||
public static function repository(string $alias = Repository::DEFAULT_ALIAS, ConnectionAdapter $adapter = null): Repository
|
public static function repository(string $alias = Repository::DEFAULT_ALIAS, ?ConnectionAdapter $adapter = null): Repository
|
||||||
{
|
{
|
||||||
$repository = new class(static::class, $alias, $adapter) extends Repository\SqliteRepository
|
$repository = new class(static::class, $alias, $adapter) extends Repository\SqliteRepository
|
||||||
{
|
{
|
||||||
|
|||||||
+19
-1
@@ -93,7 +93,7 @@ trait EntityTrait {
|
|||||||
$this->$field = $value;
|
$this->$field = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->eventExecute(Event\Entity\DatasetFill::class, $this);
|
$this->eventExecute(Event\Entity\DatasetFill::class, $generator, $this);
|
||||||
|
|
||||||
$this->entityDatasetUnmatchedFields = $generator->getReturn();
|
$this->entityDatasetUnmatchedFields = $generator->getReturn();
|
||||||
|
|
||||||
@@ -237,6 +237,11 @@ trait EntityTrait {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Unexisting variable
|
||||||
|
if (! new \ReflectionClass($this)->hasProperty($name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
# Handling undefined 'nullable'
|
# Handling undefined 'nullable'
|
||||||
return new \ReflectionProperty($this, $name)->isInitialized($this);
|
return new \ReflectionProperty($this, $name)->isInitialized($this);
|
||||||
}
|
}
|
||||||
@@ -263,6 +268,19 @@ trait EntityTrait {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Ignore]
|
||||||
|
public function __debugInfo() : array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return $this->toArray(true) ;
|
||||||
|
}
|
||||||
|
catch (\Throwable $ex) {
|
||||||
|
return $this->entityGetDataset(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ "An error occured trying to export this dataset..." ];
|
||||||
|
}
|
||||||
|
|
||||||
#[Ignore]
|
#[Ignore]
|
||||||
public function jsonSerialize() : mixed
|
public function jsonSerialize() : mixed
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ namespace Ulmus\Event\Query;
|
|||||||
use Ulmus\Repository\RepositoryInterface;
|
use Ulmus\Repository\RepositoryInterface;
|
||||||
|
|
||||||
interface Update {
|
interface Update {
|
||||||
public function execute(RepositoryInterface $repository, object|array $entity, ? array $dataset = null, bool $replace = false) : void;
|
public function execute(RepositoryInterface $repository, object|array $entity, array $dataset, array $diff, bool $replace = false) : void;
|
||||||
}
|
}
|
||||||
@@ -70,6 +70,10 @@ class FieldDefinition {
|
|||||||
$default = $this->getDefault();
|
$default = $this->getDefault();
|
||||||
$syntax = $this->adapter->tableSyntax();
|
$syntax = $this->adapter->tableSyntax();
|
||||||
|
|
||||||
|
if (is_callable($default)) {
|
||||||
|
$default = "(" . call_user_func($default) . ")";
|
||||||
|
}
|
||||||
|
|
||||||
return implode(' ', array_filter([
|
return implode(' ', array_filter([
|
||||||
$this->isUnsigned() ? $syntax['unsigned'] : null,
|
$this->isUnsigned() ? $syntax['unsigned'] : null,
|
||||||
$this->nullable ? "NULL" : "NOT NULL",
|
$this->nullable ? "NULL" : "NOT NULL",
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Pdo;
|
||||||
|
|
||||||
|
class Mssql extends \PDO
|
||||||
|
{
|
||||||
|
use PdoTrait;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Pdo;
|
||||||
|
|
||||||
|
class Mysql extends \PDO\Mysql
|
||||||
|
{
|
||||||
|
use PdoTrait;
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Pdo;
|
||||||
|
|
||||||
|
use PDO,
|
||||||
|
PDOStatement;
|
||||||
|
|
||||||
|
trait PdoTrait
|
||||||
|
{
|
||||||
|
public bool $executionStatus;
|
||||||
|
|
||||||
|
public int $rowCount = 0;
|
||||||
|
|
||||||
|
public mixed $lastInsertId = null;
|
||||||
|
|
||||||
|
public \Closure $onClose;
|
||||||
|
|
||||||
|
public function select(string $sql, array $parameters = []): PDOStatement
|
||||||
|
{
|
||||||
|
# static::$dump && call_user_func_array(static::$dump, [ $this->fillDebugSqlParameters($sql, $parameters) ]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (false !== ( $statement = $this->prepare($sql) )) {
|
||||||
|
$statement->setFetchMode(\PDO::FETCH_ASSOC);
|
||||||
|
$this->execute($statement, $parameters, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (\Throwable $e) {
|
||||||
|
throw new \PdoException($e->getMessage() . " `$sql` with data:" . json_encode($parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $statement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
if ($this->onClose ?? null) {
|
||||||
|
call_user_func($this->onClose, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function runQuery(string $sql, array $parameters = []): ? static
|
||||||
|
{
|
||||||
|
# static::$dump && call_user_func_array(static::$dump, [ $this->fillDebugSqlParameters($sql, $parameters) ]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (false !== ( $statement = $this->prepare($sql) )) {
|
||||||
|
return $this->execute($statement, $parameters, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(\PDOException $pdo) {
|
||||||
|
if ( !str_starts_with($pdo->getMessage(), 'There is no active transaction') ) {
|
||||||
|
throw new \PdoException($pdo->getMessage() . " `$sql` with data:" . json_encode($parameters), (int) $pdo->getCode(), $pdo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (\Throwable $e) {
|
||||||
|
throw new \PdoException($e->getMessage() . " `$sql` with data:" . json_encode($parameters), (int) $e->getCode(), $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function runInsertQuery(string $sql, array $parameters = []) : ? static
|
||||||
|
{
|
||||||
|
return $this->runQuery($sql, $parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function runUpdateQuery(string $sql, array $parameters = []) : ? static
|
||||||
|
{
|
||||||
|
return $this->runQuery($sql, $parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function runDeleteQuery(string $sql, array $parameters = []) : ? static
|
||||||
|
{
|
||||||
|
return $this->runQuery($sql, $parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(PDOStatement $statement, array $parameters = [], bool $commit = true) : ? static
|
||||||
|
{
|
||||||
|
$this->executionStatus = false;
|
||||||
|
$this->lastInsertId = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (! $this->inTransaction()) {
|
||||||
|
$this->beginTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->bindVariables($statement, $parameters);
|
||||||
|
|
||||||
|
$this->executionStatus = $statement->execute();
|
||||||
|
|
||||||
|
if ( $this->executionStatus ) {
|
||||||
|
$this->lastInsertId = $this->lastInsertId();
|
||||||
|
$this->rowCount = $statement->rowCount();
|
||||||
|
|
||||||
|
if ( $commit ) {
|
||||||
|
$this->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new \PDOException($statement->errorCode() . " - " . json_encode($statement->errorInfo()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (\PDOException $e) {
|
||||||
|
$this->rollback();
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function bindVariables(PDOStatement $statement, array &$parameters) : void
|
||||||
|
{
|
||||||
|
if ($parameters) {
|
||||||
|
if (array_is_list($parameters)) {
|
||||||
|
$parameters = array_combine(range(1, count($parameters)), array_values($parameters));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foreach ($parameters as $key => $value) {
|
||||||
|
$type = match (strtolower(gettype($value))) {
|
||||||
|
"boolean", "integer" => Pdo::PARAM_INT,
|
||||||
|
"null" => Pdo::PARAM_NULL,
|
||||||
|
default => Pdo::PARAM_STR,
|
||||||
|
};
|
||||||
|
|
||||||
|
$statement->bindValue($key, $value, $type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function fillDebugSqlParameters(string $sql, array $parameters) : string
|
||||||
|
{
|
||||||
|
$parameters = array_reverse($parameters);
|
||||||
|
|
||||||
|
return str_replace(array_keys($parameters), array_map(fn($e) => is_numeric($e) ? $e : (
|
||||||
|
is_bool($e) ? (int) $e : $this->quote($e)
|
||||||
|
), array_values($parameters)), $sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Pdo;
|
||||||
|
|
||||||
|
trait SqlPdoTrait
|
||||||
|
{
|
||||||
|
protected int $openedTransaction = 0;
|
||||||
|
|
||||||
|
public function beginTransaction(): bool
|
||||||
|
{
|
||||||
|
if ( 0 === $this->openedTransaction++ ) {
|
||||||
|
return $this->openTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->exec("SAVEPOINT transaction_{$this->openedTransaction}") !== false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function commit() : bool
|
||||||
|
{
|
||||||
|
if ( 0 === --$this->openedTransaction) {
|
||||||
|
return parent::commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return parent::rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function openTransaction() : bool
|
||||||
|
{
|
||||||
|
return parent::beginTransaction();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Pdo;
|
||||||
|
|
||||||
|
class Sqlite extends \PDO\Sqlite
|
||||||
|
{
|
||||||
|
use PdoTrait, SqlitePdoTrait;
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Pdo;
|
||||||
|
|
||||||
|
trait SqlitePdoTrait
|
||||||
|
{
|
||||||
|
use SqlPdoTrait;
|
||||||
|
|
||||||
|
public bool $inTransaction = false;
|
||||||
|
|
||||||
|
public function inTransaction(): bool
|
||||||
|
{
|
||||||
|
return $this->openedTransaction > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function commit(): bool
|
||||||
|
{
|
||||||
|
if ( 0 === --$this->openedTransaction) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function openTransaction(): bool
|
||||||
|
{
|
||||||
|
return $this->exec("BEGIN IMMEDIATE TRANSACTION") !== false;
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-108
@@ -8,12 +8,13 @@ use Ulmus\Attribute\Property\{
|
|||||||
use Psr\SimpleCache\CacheInterface;
|
use Psr\SimpleCache\CacheInterface;
|
||||||
use Ulmus\Common\EntityResolver;
|
use Ulmus\Common\EntityResolver;
|
||||||
use Ulmus\Entity\EntityInterface;
|
use Ulmus\Entity\EntityInterface;
|
||||||
|
use Ulmus\Repository\JoinBuilder;
|
||||||
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, CacheTrait, Repository\ConditionTrait, Repository\EscapeTrait, Repository\QueryBuildingTrait;
|
use EventTrait, CacheTrait, Repository\ConditionTrait, Repository\EscapeTrait, Repository\QueryBuildingTrait, Repository\JoinBuilderTrait;
|
||||||
|
|
||||||
const DEFAULT_ALIAS = "this";
|
const DEFAULT_ALIAS = "this";
|
||||||
|
|
||||||
@@ -263,7 +264,7 @@ class Repository implements RepositoryInterface
|
|||||||
|
|
||||||
$entity->entityFillFromDataset($dataset, true);
|
$entity->entityFillFromDataset($dataset, true);
|
||||||
|
|
||||||
$this->eventExecute(Event\Query\Update::class, $this, $entity, $dataset, $replace);
|
$this->eventExecute(Event\Query\Update::class, $this, $entity, $dataset, $diff, $replace);
|
||||||
|
|
||||||
return $update ? (bool) $update->rowCount : false;
|
return $update ? (bool) $update->rowCount : false;
|
||||||
}
|
}
|
||||||
@@ -450,112 +451,6 @@ class Repository implements RepositoryInterface
|
|||||||
return $this->where($this->entityClass::field($primaryKeyField[$pkField]->name ?? $pkField, $alias), $value);
|
return $this->where($this->entityClass::field($primaryKeyField[$pkField]->name ?? $pkField, $alias), $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function withJoin(string|array $fields, array $options = []) : self
|
|
||||||
{
|
|
||||||
$canSelect = null === $this->queryBuilder->getFragment(Query\Select::class);
|
|
||||||
|
|
||||||
if ( $canSelect ) {
|
|
||||||
$select = $this->entityResolver->fieldList(EntityResolver::KEY_COLUMN_NAME, true);
|
|
||||||
$this->select($this->entityClass::fields(array_map(fn($f) => $f['object']->name ?? $f['name'], $select), select: true));
|
|
||||||
}
|
|
||||||
|
|
||||||
$fields = (array) $fields;
|
|
||||||
usort($fields, fn($e1, $e2) => str_contains($e1, '.') <=> str_contains($e2, '.'));
|
|
||||||
|
|
||||||
# @TODO Apply fff annotation to this too !
|
|
||||||
foreach(array_filter($fields, fn($e) => $e && ! isset($this->joined[$e]) ) as $item) {
|
|
||||||
$this->joined[$item] = true;
|
|
||||||
|
|
||||||
$attribute = clone ($this->entityResolver->searchFieldAnnotation($item, [ Join::class ]) ?:
|
|
||||||
$this->entityResolver->searchFieldAnnotation($item, [ Relation::class ]));
|
|
||||||
|
|
||||||
$isRelation = $attribute instanceof Relation;
|
|
||||||
|
|
||||||
if ($isRelation && ( $attribute->isManyToMany() )) {
|
|
||||||
throw new \Exception("Many-to-many relation can not be preloaded within joins.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $attribute ) {
|
|
||||||
$alias = $attribute->alias ?? $item;
|
|
||||||
$entity = $attribute->entity ?? $this->entityResolver->getPropertyEntityType($item);
|
|
||||||
|
|
||||||
foreach($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
|
||||||
if ( null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Relation\Ignore::class ]) ) {
|
|
||||||
$escAlias = $this->escapeIdentifier($alias);
|
|
||||||
$fieldName = $this->escapeIdentifier($key);
|
|
||||||
|
|
||||||
$name = $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Field::class ])->name ?? $field->name;
|
|
||||||
|
|
||||||
if ($canSelect) {
|
|
||||||
# @TODO add handling of the new #[Select] attribute here !
|
|
||||||
|
|
||||||
$this->select("$escAlias.$fieldName as $alias\${$name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->open();
|
|
||||||
|
|
||||||
if ( ! in_array(WithOptionEnum::SkipWhere, $options)) {
|
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ] ) as $condition) {
|
|
||||||
if ( is_object($condition->field) && ( $condition->field->entityClass !== $entity ) ) {
|
|
||||||
$this->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! in_array(WithOptionEnum::SkipHaving, $options)) {
|
|
||||||
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Having::class ]) as $condition) {
|
|
||||||
$this->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! in_array(WithOptionEnum::SkipFilter, $options)) {
|
|
||||||
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Filter::class ]) as $filter) {
|
|
||||||
call_user_func_array([$this->entityClass, $filter->method], [$this, $item, true]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->close();
|
|
||||||
|
|
||||||
$key = is_string($attribute->key) ? $this->entityClass::field($attribute->key) : $attribute->key;
|
|
||||||
|
|
||||||
$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) {
|
|
||||||
if ( ! in_array(WithOptionEnum::SkipJoinWhere, $options)) {
|
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ]) as $condition) {
|
|
||||||
if ( ! is_object($condition->field) ) {
|
|
||||||
$field = $this->entityClass::field($condition->field);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$field = clone $condition->field;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Adding directly
|
|
||||||
if ( $field->entityClass === $entity ) {
|
|
||||||
$field->alias = $alias;
|
|
||||||
|
|
||||||
$join->where(is_object($field) ? $field : $entity::field($field, $alias), $condition->getValue(), $condition->operator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! in_array(WithOptionEnum::SkipJoinFilter, $options) ) {
|
|
||||||
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ FilterJoin::class ]) as $filter) {
|
|
||||||
call_user_func_array([$this->entityClass, $filter->method], [$join, $item, true]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new \Exception("Referenced field `$item` which do not exist or do not contain a valid @Join or @Relation attribute.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function withSubquery(string|array $fields, array $options = []) : self
|
public function withSubquery(string|array $fields, array $options = []) : self
|
||||||
{
|
{
|
||||||
# We skip subqueries when counting results since it should not affect the row count.
|
# We skip subqueries when counting results since it should not affect the row count.
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Repository;
|
||||||
|
|
||||||
|
use Ulmus\Attribute\Property\Field;
|
||||||
|
use Ulmus\Attribute\Property\Filter;
|
||||||
|
use Ulmus\Attribute\Property\FilterJoin;
|
||||||
|
use Ulmus\Attribute\Property\Having;
|
||||||
|
use Ulmus\Attribute\Property\Join;
|
||||||
|
use Ulmus\Attribute\Property\Relation;
|
||||||
|
use Ulmus\Attribute\Property\Where;
|
||||||
|
use Ulmus\Common\EntityField;
|
||||||
|
use Ulmus\Common\EntityResolver;
|
||||||
|
use Ulmus\QueryBuilder\QueryBuilderInterface;
|
||||||
|
use Ulmus\Query;
|
||||||
|
|
||||||
|
trait JoinBuilderTrait
|
||||||
|
{
|
||||||
|
public function withJoin(string|array $fields, array $options = []) : self
|
||||||
|
{
|
||||||
|
$defaultAlias = $this->escapeIdentifier(static::DEFAULT_ALIAS);
|
||||||
|
|
||||||
|
$canSelect = null === $this->queryBuilder->getFragment(Query\Select::class);
|
||||||
|
|
||||||
|
if ( $canSelect ) {
|
||||||
|
$select = $this->entityResolver->fieldList(EntityResolver::KEY_COLUMN_NAME, true);
|
||||||
|
|
||||||
|
$this->select($this->entityClass::fields(array_map(fn($f) => $f['object']->name ?? $f['name'], $select), select: true));
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = (array) $fields;
|
||||||
|
|
||||||
|
usort($fields, fn($e1, $e2) => str_contains($e1, '.') <=> str_contains($e2, '.'));
|
||||||
|
|
||||||
|
# @TODO Apply fff annotation to this too !
|
||||||
|
foreach(array_filter($fields, fn($e) => $e && ! isset($this->joined[$e]) ) as $item) {
|
||||||
|
$this->joined[$item] = true;
|
||||||
|
|
||||||
|
$attribute = clone ($this->entityResolver->searchFieldAnnotation($item, [ Join::class ]) ?:
|
||||||
|
$this->entityResolver->searchFieldAnnotation($item, [ Relation::class ]));
|
||||||
|
|
||||||
|
$isRelation = $attribute instanceof Relation;
|
||||||
|
|
||||||
|
if ($isRelation && ( $attribute->isManyToMany() )) {
|
||||||
|
throw new \Exception("Many-to-many relation can not be preloaded within joins.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $attribute ) {
|
||||||
|
$alias = $attribute->alias ?? $item;
|
||||||
|
$entity = $attribute->entity ?? $this->entityResolver->getPropertyEntityType($item);
|
||||||
|
|
||||||
|
foreach($entity::resolveEntity()->fieldList(EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
||||||
|
if ( null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Relation\Ignore::class ]) ) {
|
||||||
|
$escAlias = $this->escapeIdentifier($alias);
|
||||||
|
$fieldName = $this->escapeIdentifier($key);
|
||||||
|
|
||||||
|
$name = $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Field::class ])->name ?? $field->name;
|
||||||
|
|
||||||
|
if ($canSelect) {
|
||||||
|
# @TODO add handling of the new #[Select] attribute here !
|
||||||
|
|
||||||
|
$this->select("$escAlias.$fieldName as $alias\${$name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->open();
|
||||||
|
|
||||||
|
if ( ! in_array(WithOptionEnum::SkipWhere, $options)) {
|
||||||
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ] ) as $condition) {
|
||||||
|
$field = $this->joinFieldObject($condition->field, $this->entityClass);
|
||||||
|
|
||||||
|
if ( $field->entityClass === $this->entityClass ) {
|
||||||
|
$this->where($field, $this->joinFieldValue($condition->getValue(), $alias), $condition->operator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! in_array(WithOptionEnum::SkipHaving, $options)) {
|
||||||
|
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Having::class ]) as $condition) {
|
||||||
|
$field = $this->joinFieldObject($condition->field, $this->entityClass);
|
||||||
|
|
||||||
|
$this->having($field, $this->joinFieldValue($condition->getValue(), $alias), $condition->operator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! in_array(WithOptionEnum::SkipFilter, $options)) {
|
||||||
|
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Filter::class ]) as $filter) {
|
||||||
|
call_user_func_array([$this->entityClass, $filter->method], [ $this, $item, true ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->close();
|
||||||
|
|
||||||
|
$key = is_string($attribute->key) ? $this->entityClass::field($attribute->key) : $attribute->key;
|
||||||
|
|
||||||
|
$foreignKey = is_string($attribute->foreignKey) ? $entity::field($attribute->foreignKey, $alias) : $attribute->foreignKey;
|
||||||
|
|
||||||
|
$this->join(Query\Join::TYPE_LEFT, $entity::resolveEntity()->tableName(), $key, $foreignKey, $alias, function($join) use ($item, $entity, $alias, $defaultAlias, $options) {
|
||||||
|
if ( ! in_array(WithOptionEnum::SkipJoinWhere, $options)) {
|
||||||
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ]) as $condition) {
|
||||||
|
$field = $this->joinFieldObject($condition->field, $this->entityClass);
|
||||||
|
|
||||||
|
# Adding directly
|
||||||
|
if ( $field->entityClass === $entity ) {
|
||||||
|
if ($field->alias === $defaultAlias) {
|
||||||
|
$field->alias = $this->escapeIdentifier($alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
$join->where($field, $this->joinFieldValue($condition->getValue(), $alias), $condition->operator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! in_array(WithOptionEnum::SkipJoinFilter, $options) ) {
|
||||||
|
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ FilterJoin::class ]) as $filter) {
|
||||||
|
call_user_func_array([$this->entityClass, $filter->method], [$join, $item, true]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new \Exception("Referenced field `$item` which do not exist or do not contain a valid @Join or @Relation attribute.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function joinFieldObject(mixed $field, string $entityClass, string $alias = null) : \Stringable
|
||||||
|
{
|
||||||
|
if ( ! is_object($field) ) {
|
||||||
|
return $entityClass::field($field, $alias);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return clone $field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function joinFieldValue(mixed $value, string $alias = null) : mixed
|
||||||
|
{
|
||||||
|
if ( $value instanceof EntityField ) {
|
||||||
|
$value = clone $value;
|
||||||
|
|
||||||
|
$value->alias = $value->alias ?: $this->escapeIdentifier($alias);
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,11 +15,13 @@ class ParsedSearchParameter
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function value() : mixed{
|
public function value() : mixed{
|
||||||
|
$value = $this->value instanceof \BackedEnum ? $this->value->value : $this->value;
|
||||||
|
|
||||||
return match ($this->searchMethod) {
|
return match ($this->searchMethod) {
|
||||||
SearchMethodEnum::Like => "%$this->value%",
|
SearchMethodEnum::Like => "%$value%",
|
||||||
SearchMethodEnum::LikeLeft => "%$this->value",
|
SearchMethodEnum::LikeLeft => "%$value",
|
||||||
SearchMethodEnum::LikeRight => "$this->value%",
|
SearchMethodEnum::LikeRight => "$value%",
|
||||||
default => $this->value,
|
default => $value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -190,7 +190,28 @@ trait SearchRequestFromRequestTrait
|
|||||||
$this->$propertyName = !empty($value);
|
$this->$propertyName = !empty($value);
|
||||||
}
|
}
|
||||||
elseif ($value !== null) {
|
elseif ($value !== null) {
|
||||||
|
|
||||||
$this->$propertyName = $this->parseValue($property, $value);
|
$this->$propertyName = $this->parseValue($property, $value);
|
||||||
|
/*=======
|
||||||
|
foreach ($property->getTypes() as $type) {
|
||||||
|
$enum = $type->type;
|
||||||
|
|
||||||
|
if (enum_exists($enum)) {
|
||||||
|
try {
|
||||||
|
$this->$propertyName = $value instanceof \UnitEnum ? $value : $type->type::from($value);
|
||||||
|
} catch (\ValueError $ex) {
|
||||||
|
$cases = implode(', ', array_map(fn($e) => $e->value, $enum::cases()));
|
||||||
|
|
||||||
|
throw new \ValueError(
|
||||||
|
sprintf("Given value '%s' do not exists within enum '%s'. Try one of those values instead : %s", $value, $enum, $cases)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($type->builtIn || $value instanceof \Stringable ) {
|
||||||
|
$this->$propertyName = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>>>>>>> d917678 (- Moved withJoin() method of repository into a trait)*/
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->parseAttributeMethod($attribute, $field, $propertyName);
|
$this->parseAttributeMethod($attribute, $field, $propertyName);
|
||||||
|
|||||||
Reference in new issue
Block a user