diff --git a/src/Adapter/AdapterInterface.php b/src/Adapter/AdapterInterface.php index 06c40e0..8449e2f 100644 --- a/src/Adapter/AdapterInterface.php +++ b/src/Adapter/AdapterInterface.php @@ -7,4 +7,5 @@ use Ulmus\Common\PdoObject; interface AdapterInterface { public function connect() : PdoObject; public function buildDataSourceName() : string; + public function setup(array $configuration) : void; } diff --git a/src/Adapter/MsSQL.php b/src/Adapter/MsSQL.php new file mode 100644 index 0000000..99e2626 --- /dev/null +++ b/src/Adapter/MsSQL.php @@ -0,0 +1,188 @@ +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']; + } + } +} \ No newline at end of file diff --git a/src/Adapter/MySQL.php b/src/Adapter/MySQL.php index 4b0c5e0..fe857e6 100644 --- a/src/Adapter/MySQL.php +++ b/src/Adapter/MySQL.php @@ -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']; + } + } } diff --git a/src/Annotation/AnnotationReader.php b/src/Annotation/AnnotationReader.php index 76abd00..924cd6f 100644 --- a/src/Annotation/AnnotationReader.php +++ b/src/Annotation/AnnotationReader.php @@ -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) { diff --git a/src/Annotation/Classes/Table.php b/src/Annotation/Classes/Table.php index 2d10134..659dd15 100644 --- a/src/Annotation/Classes/Table.php +++ b/src/Annotation/Classes/Table.php @@ -6,6 +6,8 @@ class Table implements \Ulmus\Annotation\Annotation { public string $name; + public string $schema; + public function __construct($name = null) { if ( $name !== null ) { diff --git a/src/Annotation/Property/Field.php b/src/Annotation/Property/Field.php index 7887111..1a4efcc 100644 --- a/src/Annotation/Property/Field.php +++ b/src/Annotation/Property/Field.php @@ -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; } } } diff --git a/src/Annotation/Property/OrderBy.php b/src/Annotation/Property/OrderBy.php index 0bf9175..a2ad525 100644 --- a/src/Annotation/Property/OrderBy.php +++ b/src/Annotation/Property/OrderBy.php @@ -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; } } } diff --git a/src/Annotation/Property/Relation.php b/src/Annotation/Property/Relation.php index 634d8e5..22465f9 100644 --- a/src/Annotation/Property/Relation.php +++ b/src/Annotation/Property/Relation.php @@ -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(); + } } diff --git a/src/Annotation/Property/Where.php b/src/Annotation/Property/Where.php index 27a457f..f7b0d35 100644 --- a/src/Annotation/Property/Where.php +++ b/src/Annotation/Property/Where.php @@ -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; } } } diff --git a/src/Common/EntityField.php b/src/Common/EntityField.php index a15df85..1af3f96 100644 --- a/src/Common/EntityField.php +++ b/src/Common/EntityField.php @@ -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 diff --git a/src/Common/EntityResolver.php b/src/Common/EntityResolver.php index 42a95b9..2c17984 100644 --- a/src/Common/EntityResolver.php +++ b/src/Common/EntityResolver.php @@ -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 { diff --git a/src/Common/ObjectReflection.php b/src/Common/ObjectReflection.php index 65be665..c17ff38 100644 --- a/src/Common/ObjectReflection.php +++ b/src/Common/ObjectReflection.php @@ -100,6 +100,7 @@ class ObjectReflection { if ( $property->hasType() ) { $current['type'] = $property->getType()->getName(); + $current['builtin'] = $property->getType()->isBuiltIn(); $current['nullable'] = $property->getType()->allowsNull(); } diff --git a/src/Common/PdoObject.php b/src/Common/PdoObject.php index 5af04dd..306e933 100644 --- a/src/Common/PdoObject.php +++ b/src/Common/PdoObject.php @@ -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; } diff --git a/src/ConnectionAdapter.php b/src/ConnectionAdapter.php index cc4fe67..fd7d804 100644 --- a/src/ConnectionAdapter.php +++ b/src/ConnectionAdapter.php @@ -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(); } } diff --git a/src/Entity/ObjectInstanciator.php b/src/Entity/ObjectInstanciator.php new file mode 100644 index 0000000..94e00aa --- /dev/null +++ b/src/Entity/ObjectInstanciator.php @@ -0,0 +1,33 @@ +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]); + } +} diff --git a/src/EntityCollection.php b/src/EntityCollection.php index e9d6f3f..b040872 100644 --- a/src/EntityCollection.php +++ b/src/EntityCollection.php @@ -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; + } } \ No newline at end of file diff --git a/src/EntityTrait.php b/src/EntityTrait.php index 5396cab..543c026 100644 --- a/src/EntityTrait.php +++ b/src/EntityTrait.php @@ -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); } /** diff --git a/src/Exception/AdapterConfigurationException.php b/src/Exception/AdapterConfigurationException.php new file mode 100644 index 0000000..19450e9 --- /dev/null +++ b/src/Exception/AdapterConfigurationException.php @@ -0,0 +1,5 @@ +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); diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index ba12135..4f86c84 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -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); } diff --git a/src/Repository.php b/src/Repository.php index 0fc00f8..1be4c0e 100644 --- a/src/Repository.php +++ b/src/Repository.php @@ -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("
", $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; diff --git a/src/Ulmus.php b/src/Ulmus.php index 0869103..3ca3ca3 100644 --- a/src/Ulmus.php +++ b/src/Ulmus.php @@ -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();