- Added a new MsSQL adapter
- Some bugfixes were made within annotations. - Added a new Object Instanciator which allows to manipulate and define how an object from an entity must be instanciated with given value.
This commit is contained in:
parent
9df7e65dcf
commit
5bd60129ea
|
@ -7,4 +7,5 @@ use Ulmus\Common\PdoObject;
|
|||
interface AdapterInterface {
|
||||
public function connect() : PdoObject;
|
||||
public function buildDataSourceName() : string;
|
||||
public function setup(array $configuration) : void;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Adapter;
|
||||
|
||||
use Ulmus\Common\PdoObject;
|
||||
|
||||
use Ulmus\Exception\AdapterConfigurationException;
|
||||
|
||||
class MsSQL implements AdapterInterface {
|
||||
|
||||
public int $port;
|
||||
|
||||
public int $transactionIsolation;
|
||||
|
||||
public int $connectionPooling = 0;
|
||||
|
||||
public bool $encrypt;
|
||||
|
||||
public bool $multipleActiveResultSets;
|
||||
|
||||
public bool $traceOn;
|
||||
|
||||
public string $app;
|
||||
|
||||
public string $server;
|
||||
|
||||
public string $database;
|
||||
|
||||
public string $username;
|
||||
|
||||
public string $password;
|
||||
|
||||
public string $traceFile;
|
||||
|
||||
public string $failoverPartner;
|
||||
|
||||
public string $wsid;
|
||||
|
||||
public function __construct(
|
||||
?string $server = null,
|
||||
?string $database = null,
|
||||
?string $username = null,
|
||||
?string $password = null,
|
||||
?int $port = null
|
||||
) {
|
||||
if ($server) {
|
||||
$this->server = $server;
|
||||
}
|
||||
|
||||
if ($database) {
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
if ($port) {
|
||||
$this->port = $port;
|
||||
}
|
||||
|
||||
if ($username) {
|
||||
$this->username = $username;
|
||||
}
|
||||
|
||||
if ($password) {
|
||||
$this->password = $password;
|
||||
}
|
||||
}
|
||||
|
||||
public function connect() : PdoObject
|
||||
{
|
||||
try {
|
||||
$pdo = new PdoObject($this->buildDataSourceName(), $this->username, $this->password);
|
||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
|
||||
$pdo->setAttribute(\PDO::SQLSRV_ATTR_ENCODING, \PDO::SQLSRV_ENCODING_UTF8);
|
||||
}
|
||||
catch(PDOException $ex){
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
public function buildDataSourceName() : string
|
||||
{
|
||||
$parts[] = "Server={$this->server}" . ( isset($this->port) ? ",{$this->port}" : "" );
|
||||
$parts[] = "Database={$this->database}";
|
||||
$parts[] = "ConnectionPooling={$this->connectionPooling}";
|
||||
|
||||
if ( $this->app ?? false ) {
|
||||
$parts[] = "APP={$this->app}";
|
||||
}
|
||||
|
||||
if ( $this->encrypt ?? false ) {
|
||||
$parts[] = "Encrypt=1";
|
||||
}
|
||||
|
||||
if ( $this->failoverPartner ?? false ) {
|
||||
$parts[] = "Failover_Partner={$this->failoverPartner}";
|
||||
}
|
||||
|
||||
if ( $this->loginTimeout ?? false ) {
|
||||
$parts[] = "LoginTimeout={$this->loginTimeout}";
|
||||
}
|
||||
|
||||
if ( $this->multipleActiveResultSets ?? false ) {
|
||||
$parts[] = "MultipleActiveResultSets=1";
|
||||
}
|
||||
|
||||
if ( $this->quotedId ?? false ) {
|
||||
$parts[] = "QuotedId=1";
|
||||
}
|
||||
|
||||
if ( $this->traceFile ?? false ) {
|
||||
$parts[] = "TraceFile={$this->traceFile}";
|
||||
}
|
||||
|
||||
if ( $this->traceOn ?? false ) {
|
||||
$parts[] = "TraceOn=1";
|
||||
}
|
||||
|
||||
if ( $this->transactionIsolation ?? false ) {
|
||||
$parts[] = "TransactionIsolation={$this->transactionIsolation}";
|
||||
}
|
||||
|
||||
if ( $this->trustServerCertificate ?? false ) {
|
||||
$parts[] = "TrustServerCertificate=1";
|
||||
}
|
||||
|
||||
if ( $this->WSID ?? false ) {
|
||||
$parts[] = "WSID={$this->wsid}";
|
||||
}
|
||||
|
||||
return "sqlsrv:" . implode(';', $parts);
|
||||
}
|
||||
|
||||
public function setup(array $configuration) : void
|
||||
{
|
||||
$configuration = array_change_key_case($configuration, \CASE_LOWER);
|
||||
|
||||
if ( false === ( $this->server = $configuration['server'] ?? false ) ) {
|
||||
throw new AdapterConfigurationException("Your `server` setting is missing. It is a mandatory parameter for this driver.");
|
||||
}
|
||||
elseif ( false === ( $this->database = $configuration['database'] ?? false ) ) {
|
||||
throw new AdapterConfigurationException("Your `database` setting is missing. The adapter won't connect without it.");
|
||||
}
|
||||
elseif ( false === ( $this->username = $configuration['username'] ?? false ) ) {
|
||||
throw new AdapterConfigurationException("Your `username` is missing from your configuration array");
|
||||
}
|
||||
elseif ( false === ( $this->password = $configuration['password'] ?? false ) ) {
|
||||
throw new AdapterConfigurationException("Your `password` is missing from your configuration array");
|
||||
}
|
||||
|
||||
if ( false !== ( $configuration['app'] ?? false ) ) {
|
||||
$this->app = $configuration['app'];
|
||||
}
|
||||
|
||||
if ( false !== ( $configuration['port'] ?? false ) ) {
|
||||
$this->port = $configuration['port'];
|
||||
}
|
||||
|
||||
if ( false !== ( $configuration['failover_partner'] ?? false ) ) {
|
||||
$this->failoverPartner = $configuration['failover_partner'];
|
||||
}
|
||||
|
||||
if ( false !== ( $configuration['wsid'] ?? false ) ) {
|
||||
$this->wsid = $configuration['wsid'];
|
||||
}
|
||||
|
||||
if ( false !== ( $configuration['transaction_isolation'] ?? false ) ) {
|
||||
$this->transactionIsolation = $configuration['transaction_isolation'];
|
||||
}
|
||||
|
||||
if ( false !== ( $configuration['connection_pooling'] ?? false ) ) {
|
||||
$this->connectionPooling = $configuration['connection_pooling'];
|
||||
}
|
||||
|
||||
if ( false !== ( $configuration['encrypt'] ?? false ) ) {
|
||||
$this->encrypt = $configuration['encrypt'];
|
||||
}
|
||||
|
||||
if ( false !== ( $configuration['multiple_active_result_sets'] ?? false ) ) {
|
||||
$this->multipleActiveResultSets = $configuration['multiple_active_result_sets'];
|
||||
}
|
||||
|
||||
if ( false !== ( $configuration['trace_on'] ?? false ) ) {
|
||||
$this->traceOn = $configuration['trace_on'];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,13 +4,22 @@ namespace Ulmus\Adapter;
|
|||
|
||||
use Ulmus\Common\PdoObject;
|
||||
|
||||
use Ulmus\Exception\AdapterConfigurationException;
|
||||
|
||||
class MySQL implements AdapterInterface {
|
||||
|
||||
public string $hostname;
|
||||
|
||||
public string $database;
|
||||
|
||||
public string $username;
|
||||
|
||||
public string $password;
|
||||
|
||||
public string $charset;
|
||||
|
||||
public string $unixSocket;
|
||||
|
||||
public int $port;
|
||||
|
||||
public function __construct(
|
||||
|
@ -49,20 +58,34 @@ class MySQL implements AdapterInterface {
|
|||
public function connect() : PdoObject
|
||||
{
|
||||
try {
|
||||
$obj = new PdoObject($this->buildDataSourceName(), $this->username, $this->password);
|
||||
$pdo = new PdoObject($this->buildDataSourceName(), $this->username, $this->password);
|
||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
|
||||
$pdo->setAttribute(\PDO::ATTR_AUTOCOMMIT, false);
|
||||
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
|
||||
$pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
|
||||
}
|
||||
catch(PDOException $ex){
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
return $obj;
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
|
||||
public function buildDataSourceName() : string
|
||||
{
|
||||
$parts[] = "host={$this->hostname}";
|
||||
if ( false !== ($this->unixSocket ?? false) ) {
|
||||
|
||||
}
|
||||
else {
|
||||
$parts[] = "host={$this->hostname}";
|
||||
|
||||
if ( false !== ( $this->port ?? false ) ) {
|
||||
$parts[] = "port={$this->port}";
|
||||
}
|
||||
}
|
||||
|
||||
$parts[] = "dbname={$this->database}";
|
||||
$parts[] = "port={$this->port}";
|
||||
|
||||
if ( $this->socket ?? false ) {
|
||||
$parts[] = "socket={$this->socket}";
|
||||
|
@ -74,4 +97,34 @@ class MySQL implements AdapterInterface {
|
|||
|
||||
return "mysql:" . implode(';', $parts);
|
||||
}
|
||||
|
||||
public function setup(array $configuration) : void
|
||||
{
|
||||
$configuration = array_change_key_case($configuration, \CASE_LOWER);
|
||||
|
||||
# Either Unix Socket is used, or Host / Port
|
||||
if ( false !== ( $this->unixSocket = $connection['unix_socket'] ?? false ) ) {
|
||||
if ( false === ( $this->hostname = $connection['host'] ?? false ) ) {
|
||||
throw new AdapterConfigurationException("Your `host` name is missing from your configuration array");
|
||||
}
|
||||
|
||||
if ( false !== ( $configuration['port'] ?? false ) ) {
|
||||
$this->port = $configuration['port'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( false === ( $this->database = $connection['database'] ?? false ) ) {
|
||||
throw new AdapterConfigurationException("Your `database` name is missing from your configuration array");
|
||||
}
|
||||
elseif ( false === ( $this->username = $connection['username'] ?? false ) ) {
|
||||
throw new AdapterConfigurationException("Your `username` is missing from your configuration array");
|
||||
}
|
||||
elseif ( false === ( $this->password = $connection['password'] ?? false ) ) {
|
||||
throw new AdapterConfigurationException("Your `password` is missing from your configuration array");
|
||||
}
|
||||
|
||||
if ( false !== ( $configuration['charset'] ?? false ) ) {
|
||||
$this->charset = $configuration['charset'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ class AnnotationReader
|
|||
try {
|
||||
$tags[] = [
|
||||
'tag' => substr($line, 0, $open - 1),
|
||||
'arguments' => eval("namespace $namespace; return [ $arguments ];"),
|
||||
'arguments' => eval("namespace $namespace; return [ $arguments ];" ),
|
||||
];
|
||||
}
|
||||
catch(\Throwable $error) {
|
||||
|
|
|
@ -6,6 +6,8 @@ class Table implements \Ulmus\Annotation\Annotation {
|
|||
|
||||
public string $name;
|
||||
|
||||
public string $schema;
|
||||
|
||||
public function __construct($name = null)
|
||||
{
|
||||
if ( $name !== null ) {
|
||||
|
|
|
@ -16,16 +16,14 @@ class Field implements \Ulmus\Annotation\Annotation {
|
|||
|
||||
public bool $nullable = false;
|
||||
|
||||
public function __construct(string $type = null, int $length = null)
|
||||
public function __construct(? string $type = null, ? int $length = null)
|
||||
{
|
||||
switch(true) {
|
||||
case $type !== null:
|
||||
$this->type = $type;
|
||||
break;
|
||||
|
||||
case $length !== null:
|
||||
$this->length = $length;
|
||||
break;
|
||||
if ( $type !== null ) {
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
if ( $length !== null ) {
|
||||
$this->length = $length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,15 +9,13 @@ class OrderBy implements \Ulmus\Annotation\Annotation {
|
|||
public string $order = "ASC";
|
||||
|
||||
public function __construct(string $field = null, string $order = null)
|
||||
{
|
||||
switch(true) {
|
||||
case $field !== null:
|
||||
$this->field = $field;
|
||||
break;
|
||||
|
||||
case $order !== null:
|
||||
$this->order = $order;
|
||||
break;
|
||||
{
|
||||
if ( $field !== null ) {
|
||||
$this->field = $field;
|
||||
}
|
||||
|
||||
if ( $order !== null ) {
|
||||
$this->order = $order;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,19 @@ class Relation implements \Ulmus\Annotation\Annotation {
|
|||
public string $bridgeKey;
|
||||
|
||||
public string $bridgeForeignKey;
|
||||
|
||||
public string $entity;
|
||||
|
||||
public function __construct(string $type = null)
|
||||
{
|
||||
switch(true) {
|
||||
case $type !== null:
|
||||
$this->type = $type;
|
||||
break;
|
||||
if ( $type !== null ) {
|
||||
$this->type = $type;
|
||||
}
|
||||
}
|
||||
|
||||
public function entity() {
|
||||
$e = $this->entity;
|
||||
|
||||
return new $e();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,31 @@
|
|||
|
||||
namespace Ulmus\Annotation\Property;
|
||||
|
||||
use Ulmus\Query;
|
||||
|
||||
class Where implements \Ulmus\Annotation\Annotation {
|
||||
|
||||
public array $comparisons = [];
|
||||
|
||||
public function __construct(...$comparisons)
|
||||
public string $field;
|
||||
|
||||
public $value;
|
||||
|
||||
public string $operator;
|
||||
|
||||
public function __construct(? string $field = null, $value = null, ? string $operator = null)
|
||||
{
|
||||
if ( $comparisons ) {
|
||||
$this->comparisons = $comparisons;
|
||||
if ( $field !== null ) {
|
||||
$this->field = $field;
|
||||
}
|
||||
|
||||
if ( $value !== null ) {
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
if ( $operator !== null ) {
|
||||
$this->operator = $operator;
|
||||
}
|
||||
else {
|
||||
$this->operator = Query\Where::OPERATOR_EQUAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class EntityField
|
|||
# Must use REFLECTION before throwing this value.
|
||||
# Should first check if it's a relation field, and if it is,
|
||||
# it's real key must be returned (PK usually)
|
||||
return $useAlias ? "{$this->alias}.`{$this->name}`" : $this->name;
|
||||
return $useAlias ? "{$this->alias}.\"{$this->name}\"" : "\"{$this->name}\"";
|
||||
}
|
||||
|
||||
public static function isScalarType($type) : bool
|
||||
|
@ -46,7 +46,8 @@ class EntityField
|
|||
|
||||
public static function isObjectType($type) : bool
|
||||
{
|
||||
return strpos($type, "\\") !== false;
|
||||
# @ Should be fixed with isBuiltIn() instead, it won't be correct based only on name
|
||||
# return strpos($type, "\\") !== false;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
|
|
|
@ -4,7 +4,8 @@ namespace Ulmus\Common;
|
|||
|
||||
use Ulmus\Annotation\Annotation,
|
||||
Ulmus\Annotation\Classes\Table,
|
||||
Ulmus\Annotation\Property\Field;
|
||||
Ulmus\Annotation\Property\Field,
|
||||
Ulmus\Annotation\Property\Relation;
|
||||
|
||||
class EntityResolver {
|
||||
|
||||
|
@ -78,6 +79,49 @@ class EntityResolver {
|
|||
return $fieldList;
|
||||
}
|
||||
|
||||
public function relation(string $name) : ? array
|
||||
{
|
||||
try{
|
||||
if ( null !== ( $this->properties[$name] ?? null ) ) {
|
||||
foreach($this->properties[$name]['tags'] ?? [] as $tag) {
|
||||
if ( $tag['object'] instanceof Relation ) {
|
||||
return $this->properties[$name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
catch(\Throwable $e) {
|
||||
if ( $throwException) {
|
||||
throw new \InvalidArgumentException("Can't find entity relation's column named `$name` from entity {$this->entityClass}");
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function searchFieldAnnotation(string $field, Annotation $annotationType) : ? Annotation
|
||||
{
|
||||
$found = $this->searchFieldAnnotationList($field, $annotationType);
|
||||
return $found ? $found[0] : null;
|
||||
}
|
||||
|
||||
public function searchFieldAnnotationList(string $field, Annotation $annotationType) : array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
if ( null !== ( $this->properties[$field] ?? null ) ) {
|
||||
foreach($this->properties[$field]['tags'] ?? [] as $tag) {
|
||||
if ( $tag['object'] instanceof $annotationType ) {
|
||||
$list[] = $tag['object'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function tableName() : string
|
||||
{
|
||||
if ( null === $table = $this->getAnnotationFromClassname( Table::class ) ) {
|
||||
|
@ -91,6 +135,19 @@ class EntityResolver {
|
|||
return $table->name;
|
||||
}
|
||||
|
||||
public function schemaName() : ? string
|
||||
{
|
||||
if ( null === $table = $this->getAnnotationFromClassname( Table::class ) ) {
|
||||
throw new \LogicException("Your entity {$this->entityClass} seems to be missing a @Table() annotation");
|
||||
}
|
||||
|
||||
if ( $table->name === "" ) {
|
||||
throw new \ArgumentCountError("Your entity {$this->entityClass} seems to be missing a `schema` argument for your @Table() annotation");
|
||||
}
|
||||
|
||||
return $table->schema ?? null;
|
||||
}
|
||||
|
||||
public function primaryKeys() : array
|
||||
{
|
||||
|
||||
|
|
|
@ -100,6 +100,7 @@ class ObjectReflection {
|
|||
|
||||
if ( $property->hasType() ) {
|
||||
$current['type'] = $property->getType()->getName();
|
||||
$current['builtin'] = $property->getType()->isBuiltIn();
|
||||
$current['nullable'] = $property->getType()->allowsNull();
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,13 @@ class PdoObject extends PDO {
|
|||
|
||||
public function select(string $sql, array $parameters = []) : PDOStatement
|
||||
{
|
||||
#dump($sql, $parameters);
|
||||
|
||||
try {
|
||||
if ( false !== ( $statement = $this->prepare($sql) ) ) {
|
||||
$statement = $this->execute($statement, $parameters, true);
|
||||
$statement = $this->execute($statement, $parameters, false);
|
||||
$statement->setFetchMode(\PDO::FETCH_ASSOC);
|
||||
|
||||
return $statement;
|
||||
}
|
||||
} catch (\PDOException $e) { throw $e; }
|
||||
|
@ -19,7 +22,6 @@ class PdoObject extends PDO {
|
|||
|
||||
public function runQuery(string $sql, array $parameters = []) : PDOStatement
|
||||
{
|
||||
# var_dump($sql, $parameters);die();
|
||||
try {
|
||||
if ( false !== ( $statement = $this->prepare($sql) ) ) {
|
||||
return $this->execute($statement, $parameters, true);
|
||||
|
@ -35,9 +37,9 @@ class PdoObject extends PDO {
|
|||
}
|
||||
|
||||
if ( empty($parameters) ? $statement->execute() : $statement->execute($parameters) ) {
|
||||
# if ( $commit ) {
|
||||
if ( $commit ) {
|
||||
$this->commit();
|
||||
# }
|
||||
}
|
||||
|
||||
return $statement;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ class ConnectionAdapter
|
|||
public function __construct(string $name = "default", array $configuration = [])
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
$this->configuration = $configuration;
|
||||
|
||||
if ( $name === "default" ) {
|
||||
|
@ -26,62 +27,45 @@ class ConnectionAdapter
|
|||
}
|
||||
}
|
||||
|
||||
public function resolveConfiguration()
|
||||
public function resolveConfiguration() : void
|
||||
{
|
||||
$connection = $this->configuration['connections'][$this->name] ?? [];
|
||||
|
||||
if ( $adapterName = $connection['adapter'] ?? false ) {
|
||||
if ( false !== ( $adapterName = $connection['adapter'] ?? false ) ) {
|
||||
$this->adapter = $this->instanciateAdapter($adapterName);
|
||||
}
|
||||
else {
|
||||
throw new \InvalidArgumentException("Adapter not found within your configuration array.");
|
||||
}
|
||||
|
||||
if ( false === $this->adapter->hostname = $connection['host'] ?? false ) {
|
||||
throw new \InvalidArgumentException("Your `host` name is missing from your configuration array");
|
||||
}
|
||||
|
||||
if ( false === $this->adapter->port = $connection['port'] ?? false ) {
|
||||
throw new \InvalidArgumentException("Your `port` number is missing from your configuration array");
|
||||
}
|
||||
|
||||
if ( false === $this->adapter->database = $connection['database'] ?? false ) {
|
||||
throw new \InvalidArgumentException("Your `database` name is missing from your configuration array");
|
||||
}
|
||||
|
||||
if ( false === $this->adapter->username = $connection['username'] ?? false ) {
|
||||
throw new \InvalidArgumentException("Your `username` is missing from your configuration array");
|
||||
}
|
||||
|
||||
if ( false === $this->adapter->password = $connection['password'] ?? false ) {
|
||||
throw new \InvalidArgumentException("Your `password` is missing from your configuration array");
|
||||
}
|
||||
|
||||
$this->adapter->setup($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect the adapter
|
||||
* @return self
|
||||
*/
|
||||
public function connect()
|
||||
public function connect() : self
|
||||
{
|
||||
$this->pdo = $this->adapter->connect();
|
||||
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
$this->pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
|
||||
$this->pdo->setAttribute(\PDO::ATTR_AUTOCOMMIT, false);
|
||||
$this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
|
||||
$this->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function adapter() : AdapterInterface
|
||||
{
|
||||
return $this->adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instanciate an adapter which interact with the data source
|
||||
* @param string $name An Ulmus adapter or full class name implementing AdapterInterface
|
||||
* @return AdapterInterface
|
||||
*/
|
||||
protected function instanciateAdapter($name)
|
||||
protected function instanciateAdapter($name) : AdapterInterface
|
||||
{
|
||||
$class = substr($name, 0, 2) === "\\" ? $name : "\\Ulmus\\Adapter\\$name";
|
||||
|
||||
return new $class();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Entity;
|
||||
|
||||
class ObjectInstanciator {
|
||||
|
||||
protected array $objectCallbackDefinition;
|
||||
|
||||
public function instanciate(string $type, array $arguments) : object
|
||||
{
|
||||
if ( isset($this->objectCallbackDefinition[$type]) ) {
|
||||
return $this->objectCallbackDefinition[$type](...$arguments);
|
||||
}
|
||||
else {
|
||||
return new $type(...$arguments);
|
||||
}
|
||||
}
|
||||
|
||||
public function registerObject(string $type, Callable $callback) : void
|
||||
{
|
||||
$this->objectCallbackDefinition[$type] = $callback;
|
||||
}
|
||||
|
||||
public function unregisterObject(string $type) : void
|
||||
{
|
||||
unset($this->objectCallbackDefinition[$type]);
|
||||
}
|
||||
|
||||
public function isObjectRegistered(string $type) : bool
|
||||
{
|
||||
return isset($this->objectCallbackDefinition[$type]);
|
||||
}
|
||||
}
|
|
@ -4,9 +4,7 @@ namespace Ulmus;
|
|||
|
||||
use Generator;
|
||||
|
||||
#class EntityCollection implements \Countable, \ArrayAccess
|
||||
class EntityCollection extends \ArrayObject {
|
||||
# use Common\ArrayObjectTrait;
|
||||
|
||||
public function filters(Callable $callback) : Generator
|
||||
{
|
||||
|
@ -20,4 +18,15 @@ class EntityCollection extends \ArrayObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function buildArray(string $keyColumn, string $valueColumn) : array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach($this as $key => $item) {
|
||||
$list[$item->$keyColumn] = $item->$valueColumn;
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
|
@ -12,16 +12,73 @@ use Ulmus\Annotation\Property\Field\{ Id, ForeignKey, CreatedAt, UpdatedAt, };
|
|||
|
||||
trait EntityTrait {
|
||||
|
||||
/**
|
||||
* @Ignore
|
||||
*/
|
||||
protected bool $strictEntityFieldsDeclaration = true;
|
||||
|
||||
/**
|
||||
* @Ignore
|
||||
*/
|
||||
protected array $unmatchedEntityDatasetFields = [];
|
||||
|
||||
/**
|
||||
* @Ignore
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
$entityResolver= $this->resolveEntity();
|
||||
|
||||
# Resolve relations here if one is called
|
||||
if ( null !== ( $relation = $entityResolver->searchFieldAnnotation($name, new Relation() ) ) ) {
|
||||
$order = $entityResolver->searchFieldAnnotationList($name, new OrderBy() );
|
||||
$where = $entityResolver->searchFieldAnnotationList($name, new Where() );
|
||||
|
||||
$baseEntity = $relation->entity();
|
||||
$repository = $baseEntity->repository();
|
||||
|
||||
foreach($where as $condition) {
|
||||
$repository->where($condition->field, $condition->value, $condition->operator);
|
||||
}
|
||||
|
||||
foreach($order as $item) {
|
||||
$repository->orderBy($item->field, $item->order);
|
||||
}
|
||||
|
||||
$field = $relation->key;
|
||||
|
||||
if ( method_exists($this, $filterMethod = "filterRelation$name") ) {
|
||||
$this->$filterMethod($repository);
|
||||
}
|
||||
|
||||
switch($relation->type) {
|
||||
case 'oneToMany':
|
||||
$repository->where( $baseEntity->field($relation->foreignKey), 866); # <<<<<<<<< CHANGE $THIS->ID WITH PROPER NOMENCLATURE
|
||||
|
||||
return $this->$name = $repository->loadAll();
|
||||
|
||||
case 'oneToOne':
|
||||
$repository->where( $baseEntity->field($relation->foreignKey), $this->$field );
|
||||
|
||||
$result = $repository->loadAll();
|
||||
|
||||
if ( count($result) === 0 ) {
|
||||
return $baseEntity;
|
||||
}
|
||||
|
||||
return $this->$name = $result[0];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Ignore
|
||||
*/
|
||||
public function entityFillFromDataset($dataset) : self
|
||||
{
|
||||
$fields = Ulmus::resolveEntity(static::class);
|
||||
$fields = $this->resolveEntity();
|
||||
|
||||
foreach($dataset as $key => $value) {
|
||||
$key = strtolower($key);
|
||||
|
@ -43,20 +100,28 @@ trait EntityTrait {
|
|||
elseif ( EntityField::isScalarType($field['type']) ) {
|
||||
$this->{$field['name']} = $value;
|
||||
}
|
||||
elseif ( EntityField::isObjectType($field['type']) ) {
|
||||
$this->{$field['name']} = new $field['type']();
|
||||
elseif ( ! $field['builtin'] ) {
|
||||
$this->{$field['name']} = Ulmus::instanciateObject($field['type'], [ $value ]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Ignore
|
||||
*/
|
||||
public function resolveEntity() : EntityResolver
|
||||
{
|
||||
return Ulmus::resolveEntity(static::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Ignore
|
||||
*/
|
||||
public static function repository() : Repository
|
||||
public static function repository(string $alias = Repository::DEFAULT_ALIAS) : Repository
|
||||
{
|
||||
return Ulmus::repository(static::class);
|
||||
return Ulmus::repository(static::class, $alias, Ulmus::$defaultAdapter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Exception;
|
||||
|
||||
class AdapterConfigurationException extends \Exception {}
|
|
@ -2,12 +2,21 @@
|
|||
|
||||
namespace Ulmus\Query;
|
||||
|
||||
use Ulmus\QueryBuilder;
|
||||
|
||||
class From extends Fragment {
|
||||
|
||||
public int $order = -80;
|
||||
|
||||
protected $tables = [];
|
||||
|
||||
public QueryBuilder $queryBuilder;
|
||||
|
||||
public function __construct(QueryBuilder $queryBuilder)
|
||||
{
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
}
|
||||
|
||||
public function set(array $tables) : self
|
||||
{
|
||||
$this->tables = $tables;
|
||||
|
@ -35,7 +44,7 @@ class From extends Fragment {
|
|||
$list = [];
|
||||
|
||||
foreach((array) $this->tables as $alias => $table) {
|
||||
$list[] = ! is_numeric($alias) ? "`$table` $alias" : "`$table`";
|
||||
$list[] = ! is_numeric($alias) ? "$table $alias" : $table;
|
||||
}
|
||||
|
||||
return implode(", ", $list);
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
|
||||
namespace Ulmus;
|
||||
|
||||
|
||||
class QueryBuilder
|
||||
{
|
||||
|
||||
public Query\Where $where;
|
||||
|
||||
/**
|
||||
|
@ -14,20 +12,14 @@ class QueryBuilder
|
|||
public array $parameters = [];
|
||||
|
||||
public string $conditionOperator = Query\Where::CONDITION_AND;
|
||||
|
||||
public string $entityClass;
|
||||
|
||||
|
||||
protected int $parameterIndex = 0;
|
||||
|
||||
protected array $queryStack = [];
|
||||
|
||||
public function __construct($entityClass = "") {
|
||||
$this->entityClass = $entityClass;
|
||||
}
|
||||
|
||||
public function select($field) : self
|
||||
{
|
||||
if ( $select = $this->has(Query\Select::class) ) {
|
||||
if ( false !== ( $select = $this->has(Query\Select::class) ) ) {
|
||||
$select->add($field);
|
||||
}
|
||||
else {
|
||||
|
@ -52,17 +44,23 @@ class QueryBuilder
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function from($table, $alias = null, $database = null) : self
|
||||
public function from(string $table, ? string $alias = null, ? string $database = null, ? string $schema = null) : self
|
||||
{
|
||||
$table = "\"$table\"";
|
||||
|
||||
if ( $database ) {
|
||||
$table = "`$database`.".$table;
|
||||
$table = "\"$database\".$table";
|
||||
}
|
||||
|
||||
if ( $schema ) {
|
||||
$table = "\"$schema\".$table";
|
||||
}
|
||||
|
||||
if ( $from = $this->has(Query\From::class) ) {
|
||||
if ( false !== ( $from = $this->has(Query\From::class) ) ) {
|
||||
$from->add($alias ? [ $alias => $table ] : $table);
|
||||
}
|
||||
else {
|
||||
$from = new Query\From();
|
||||
$from = new Query\From($this);
|
||||
$this->push($from);
|
||||
$from->set($alias ? [ $alias => $table ] : $table);
|
||||
}
|
||||
|
@ -94,7 +92,7 @@ class QueryBuilder
|
|||
if ( $this->where ?? false ) {
|
||||
$where = $this->where;
|
||||
}
|
||||
elseif ( false === $where = $this->has(Query\Where::class) ) {
|
||||
elseif ( false === ( $where = $this->has(Query\Where::class) ) ) {
|
||||
$this->where = $where = new Query\Where($this);
|
||||
$this->push($where);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ class Repository
|
|||
$this->entityClass = $entity;
|
||||
$this->alias = $alias;
|
||||
$this->adapter = $adapter;
|
||||
$this->queryBuilder = new QueryBuilder();
|
||||
$this->queryBuilder = new QueryBuilder( $adapter->adapter() );
|
||||
$this->entityResolver = Ulmus::resolveEntity($entity);
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,6 @@ class Repository
|
|||
|
||||
public function loadFromPk($value, $primaryKey = "id") : EntityCollection
|
||||
{
|
||||
# var_dump("<pre>", $primaryKey);die();
|
||||
return $this->where($primaryKey, $value)->loadOne();
|
||||
}
|
||||
|
||||
|
@ -70,21 +69,21 @@ class Repository
|
|||
public function select($fields) : self
|
||||
{
|
||||
$this->queryBuilder->select($fields);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function delete() : self
|
||||
{
|
||||
$this->queryBuilder->delete($this->alias);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function from($table) : self
|
||||
public function from(string $table, string $alias, ? string $schema) : self
|
||||
{
|
||||
foreach((array) $table as $alias => $table) {
|
||||
$this->queryBuilder->from($table, is_numeric($alias) ? null : $alias);
|
||||
}
|
||||
|
||||
$this->queryBuilder->from($table, $alias, null, $schema);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -96,6 +95,7 @@ class Repository
|
|||
public function open(string $condition = Query\Where::CONDITION_AND) : self
|
||||
{
|
||||
$this->queryBuilder->open($condition);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -107,12 +107,14 @@ class Repository
|
|||
public function close() : self
|
||||
{
|
||||
$this->queryBuilder->close();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function where($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_AND);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -124,18 +126,21 @@ class Repository
|
|||
public function or($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_OR);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function notWhere(array $condition) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_AND, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orNot($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->notWhere($condition, Query\Where::CONDITION_OR, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -152,18 +157,21 @@ class Repository
|
|||
public function in($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orIn($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_OR);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function notIn($field, $value) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, Query\Where::OPERATOR_NOT_EQUAL);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -175,12 +183,14 @@ class Repository
|
|||
public function like($field, $value) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, Query\Where::OPERATOR_LIKE, Query\Where::CONDITION_AND);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function notLike($field, $value) : self
|
||||
{
|
||||
$this->queryBuilder->where($field, $value, Query\Where::OPERATOR_LIKE, Query\Where::CONDITION_AND, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -263,7 +273,7 @@ class Repository
|
|||
}
|
||||
|
||||
if ( ! $this->queryBuilder->has(Query\From::class) ) {
|
||||
$this->from([ $this->alias => $this->entityResolver->tableName() ]);
|
||||
$this->from($this->entityResolver->tableName(), $this->alias, $this->entityResolver->schemaName());
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
@ -276,7 +286,7 @@ class Repository
|
|||
}
|
||||
|
||||
if ( ! $this->queryBuilder->has(Query\From::class) ) {
|
||||
$this->from([ $this->alias => $this->entityResolver->tableName() ]);
|
||||
$this->from($this->entityResolver->tableName(), $this->alias, $this->entityResolver->schemaName());
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
|
|
@ -12,6 +12,8 @@ abstract class Ulmus
|
|||
|
||||
public static ConnectionAdapter $defaultAdapter;
|
||||
|
||||
public static Entity\ObjectInstanciator $objectInstanciator;
|
||||
|
||||
public static array $resolved = [];
|
||||
|
||||
public static function iterateQueryBuilder(QueryBuilder $queryBuilder, ?ConnectionAdapter $adapter = null) : Generator
|
||||
|
@ -50,6 +52,11 @@ abstract class Ulmus
|
|||
return new static::$queryBuilderClass(...$arguments);
|
||||
}
|
||||
|
||||
public static function instanciateObject(string $type, array $arguments) : object
|
||||
{
|
||||
return ( static::$objectInstanciator ?? static::$objectInstanciator = new Entity\ObjectInstanciator() )->instanciate($type, $arguments);
|
||||
}
|
||||
|
||||
protected static function fetchQueryBuilder(QueryBuilder $queryBuilder, ?ConnectionAdapter $adapter = null) : array
|
||||
{
|
||||
return ( $adapter ?: static::$defaultAdapter )->pdo->select($queryBuilder->render(), $queryBuilder->parameters ?? [])->fetchAll();
|
||||
|
|
Loading…
Reference in New Issue