- Added some SearchRequest attributes ; now it's even simpler to add searchable fields
- Began decoupling of QueryBuilder from native MySQL method ; some works needs to be done on for the Repository object splitting also.
This commit is contained in:
		
							parent
							
								
									15be1597b8
								
							
						
					
					
						commit
						4571517dc8
					
				| @ -0,0 +1,7 @@ | ||||
| # Search Request | ||||
| 
 | ||||
| Ulmus comes with a simple search request processor which allows both flexibility and simplicity. | ||||
| 
 | ||||
| ## Quick start | ||||
| 
 | ||||
| Creating a simple user exemple entity: | ||||
							
								
								
									
										0
									
								
								docs/50-search-request.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								docs/50-search-request.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										121
									
								
								src/Adapter/DefaultAdapterTrait.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								src/Adapter/DefaultAdapterTrait.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\Adapter; | ||||
| 
 | ||||
| use Ulmus\{ConnectionAdapter, Entity\InformationSchema\Table, Migration\FieldDefinition, Repository, QueryBuilder\MysqlQueryBuilder}; | ||||
| 
 | ||||
| trait DefaultAdapterTrait | ||||
| { | ||||
|     public function repositoryClass() : string | ||||
|     { | ||||
|         return Repository::class; | ||||
|     } | ||||
| 
 | ||||
|     public function queryBuilderClass() : string | ||||
|     { | ||||
|         return MysqlQueryBuilder::class; | ||||
|     } | ||||
| 
 | ||||
|     public function tableSyntax() : array | ||||
|     { | ||||
|         return [ | ||||
|             'ai' => "AUTO_INCREMENT", | ||||
|             'pk' => "PRIMARY KEY", | ||||
|             'unsigned' => "UNSIGNED", | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     public function databaseName() : string | ||||
|     { | ||||
|         return $this->database; | ||||
|     } | ||||
| 
 | ||||
|     public function schemaTable(ConnectionAdapter $adapter, $databaseName, string $tableName) : null|object | ||||
|     { | ||||
|         return Table::repository(Repository::DEFAULT_ALIAS, $adapter) | ||||
|             ->select(\Ulmus\Common\Sql::raw('this.*')) | ||||
|             ->where($this->escapeIdentifier('table_schema', AdapterInterface::IDENTIFIER_FIELD), $databaseName) | ||||
|             ->loadOneFromField($this->escapeIdentifier('table_name', AdapterInterface::IDENTIFIER_FIELD), $tableName); | ||||
|     } | ||||
| 
 | ||||
|     public function mapFieldType(FieldDefinition $field, bool $typeOnly = false) : string | ||||
|     { | ||||
|         $type = $field->type; | ||||
| 
 | ||||
|         $length = $field->length; | ||||
| 
 | ||||
|         if ( is_a($type, Entity\Field\Date::class, true) ) { | ||||
|             $type = "DATE"; | ||||
|         } | ||||
|         elseif ( is_a($type, Entity\Field\Time::class, true) ) { | ||||
|             $type = "TIME"; | ||||
|         } | ||||
|         elseif ( is_a($type, \DateTime::class, true) ) { | ||||
|             $type = "DATETIME"; | ||||
|         } | ||||
| 
 | ||||
|         switch($type) { | ||||
|             case "bool": | ||||
|                 $type = "TINYINT"; | ||||
|                 $length = 1; | ||||
|                 break; | ||||
| 
 | ||||
|             case "array": | ||||
|             case "string": | ||||
|                 if ($length && $length <= 255) { | ||||
|                     $type = "VARCHAR"; | ||||
|                     break; | ||||
|                 } | ||||
|                 elseif (! $length || ( $length <= 65535 ) ) { | ||||
|                     $type = "TEXT"; | ||||
|                 } | ||||
|                 elseif ( $length <= 16777215 ) { | ||||
|                     $type = "MEDIUMTEXT"; | ||||
|                 } | ||||
|                 elseif ($length <= 4294967295) { | ||||
|                     $type = "LONGTEXT"; | ||||
|                 } | ||||
|                 else { | ||||
|                     throw new \Exception("A column with size bigger than 4GB cannot be created."); | ||||
|                 } | ||||
| 
 | ||||
|                 # Length is unnecessary on TEXT fields
 | ||||
|                 unset($length); | ||||
| 
 | ||||
|                 break; | ||||
| 
 | ||||
|             case "float": | ||||
|                 $type = "DOUBLE"; | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 $type = strtoupper($type); | ||||
|                 break; | ||||
|         } | ||||
| 
 | ||||
|         return $typeOnly ? $type : $type . ( isset($length) ? "($length" . ( ! empty($precision) ? ",$precision" : "" ) . ")" : "" ); | ||||
|     } | ||||
| 
 | ||||
|     public function whitelistAttributes(array &$parameters) : void | ||||
|     { | ||||
|         $parameters = array_intersect_key($parameters, array_flip(static::ALLOWED_ATTRIBUTES)); | ||||
|     } | ||||
| 
 | ||||
|     public function generateAlterColumn(FieldDefinition $definition, array $field) : string|\Stringable | ||||
|     { | ||||
|         if ($field['previous']) { | ||||
|             $position = sprintf('AFTER %s', $this->escapeIdentifier($field['previous']['field'], AdapterInterface::IDENTIFIER_FIELD)); | ||||
|         } | ||||
|         else { | ||||
|             $position = "FIRST"; | ||||
|         } | ||||
| 
 | ||||
|         return implode(" ", [ | ||||
|             strtoupper($field['action']), | ||||
|             $this->escapeIdentifier($definition->getSqlName(), AdapterInterface::IDENTIFIER_FIELD), | ||||
|             $definition->getSqlType(), | ||||
|             $definition->getSqlParams(), | ||||
|             $position, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -230,6 +230,6 @@ class MsSQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface { | ||||
| 
 | ||||
|     public function queryBuilderClass() : string | ||||
|     { | ||||
|         return QueryBuilder\MssqlQueryBuilder::class; | ||||
|         return QueryBuilder\SqlQueryBuilder\MssqlQueryBuilder::class; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -2,15 +2,11 @@ | ||||
| 
 | ||||
| namespace Ulmus\Adapter; | ||||
| 
 | ||||
| use Ulmus\Entity\InformationSchema\Table; | ||||
| use Ulmus\Migration\MigrateInterface; | ||||
| use Ulmus\QueryBuilder; | ||||
| use Ulmus\Repository; | ||||
| use Ulmus\QueryBuilder\Sql; | ||||
| use Ulmus\Common\PdoObject; | ||||
| 
 | ||||
| use Ulmus\Exception\AdapterConfigurationException; | ||||
| use Ulmus\Ulmus; | ||||
| use Ulmus\Migration\FieldDefinition; | ||||
| 
 | ||||
| class MySQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface { | ||||
|     use SqlAdapterTrait; | ||||
| @ -157,4 +153,10 @@ class MySQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface { | ||||
|     { | ||||
|         return "InnoDB"; | ||||
|     } | ||||
| 
 | ||||
|     public function queryBuilderClass() : string | ||||
|     { | ||||
|         return Sql\MysqlQueryBuilder::class; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -5,10 +5,9 @@ namespace Ulmus\Adapter; | ||||
| use Ulmus\Common\PdoObject; | ||||
| use Ulmus\ConnectionAdapter; | ||||
| 
 | ||||
| use Ulmus\Entity\Sqlite\Table; | ||||
| use Ulmus\Exception\AdapterConfigurationException; | ||||
| use Ulmus\Entity; | ||||
| use Ulmus\Migration\FieldDefinition; | ||||
| use Ulmus\{Migration\MigrateInterface, Repository, QueryBuilder, Ulmus}; | ||||
| use Ulmus\{Migration\MigrateInterface, Repository, QueryBuilder}; | ||||
| 
 | ||||
| class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface { | ||||
|     use SqlAdapterTrait; | ||||
| @ -90,9 +89,9 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface | ||||
| 
 | ||||
|     public function schemaTable(ConnectionAdapter $adapter, string $databaseName, string $tableName) : null|object | ||||
|     { | ||||
|         return Table::repository(Repository::DEFAULT_ALIAS, $adapter) | ||||
|         return Entity\Sqlite\Table::repository(Repository::DEFAULT_ALIAS, $adapter) | ||||
|             ->select(\Ulmus\Common\Sql::raw('this.*')) | ||||
|             ->loadOneFromField(Table::field('tableName'), $tableName); | ||||
|             ->loadOneFromField(Entity\Sqlite\Table::field('tableName'), $tableName); | ||||
|     } | ||||
| 
 | ||||
|     public function mapFieldType(FieldDefinition $field, bool $typeOnly = false) : string | ||||
| @ -150,7 +149,7 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface | ||||
| 
 | ||||
|     public function queryBuilderClass() : string | ||||
|     { | ||||
|         return QueryBuilder\SqliteQueryBuilder::class; | ||||
|         return QueryBuilder\Sql\SqliteQueryBuilder::class; | ||||
|     } | ||||
| 
 | ||||
|     public function exportFunctions(PdoObject $pdo) : void | ||||
| @ -192,7 +191,7 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface | ||||
| 
 | ||||
|     public static function registerPragma(PdoObject $pdo, array $pragmaList) : void | ||||
|     { | ||||
|         $builder = new QueryBuilder\SqliteQueryBuilder(); | ||||
|         $builder = new QueryBuilder\Sql\SqliteQueryBuilder(); | ||||
| 
 | ||||
|         foreach($pragmaList as $pragma) { | ||||
|             list($key, $value) = explode('=', $pragma) + [ null, null ]; | ||||
|  | ||||
| @ -7,7 +7,7 @@ use Ulmus\{Common\Sql, | ||||
|     Entity\InformationSchema\Table, | ||||
|     Migration\FieldDefinition, | ||||
|     Repository, | ||||
|     QueryBuilder, | ||||
|     QueryBuilder\SqlQueryBuilder, | ||||
|     Ulmus}; | ||||
| 
 | ||||
| trait SqlAdapterTrait | ||||
| @ -19,7 +19,7 @@ trait SqlAdapterTrait | ||||
| 
 | ||||
|     public function queryBuilderClass() : string | ||||
|     { | ||||
|         return QueryBuilder::class; | ||||
|         return SqlQueryBuilder::class; | ||||
|     } | ||||
| 
 | ||||
|     public function tableSyntax() : array | ||||
|  | ||||
| @ -4,19 +4,16 @@ namespace Ulmus; | ||||
| 
 | ||||
| use Notes\Attribute\Ignore; | ||||
| use Psr\Http\Message\ServerRequestInterface; | ||||
| use Ulmus\{Common\EntityResolver, | ||||
|     Common\EntityField, | ||||
|     Entity\EntityInterface, | ||||
|     Query\QueryBuilderInterface, | ||||
|     SearchRequest\SearchRequestInterface, | ||||
|     SearchRequest\SearchRequestPaginationTrait}; | ||||
| use Ulmus\{Common\EntityResolver, Common\EntityField, Entity\EntityInterface, Query\QueryBuilderInterface}; | ||||
| use Ulmus\SearchRequest\{Attribute\SearchParameter, | ||||
|     SearchMethodEnum, | ||||
|     SearchRequestInterface, | ||||
|     SearchRequestFromRequestTrait, | ||||
|     SearchRequestPaginationTrait}; | ||||
| 
 | ||||
| trait EntityTrait { | ||||
|     use EventTrait; | ||||
| 
 | ||||
|     #[Ignore]
 | ||||
|     public string $loadedFromAdapter; | ||||
| 
 | ||||
|     #[Ignore]
 | ||||
|     protected bool $entityStrictFieldsDeclaration = false; | ||||
| 
 | ||||
| @ -327,6 +324,7 @@ trait EntityTrait { | ||||
|     #[Ignore]
 | ||||
|     public static function field($name, null|string|false $alias = Repository::DEFAULT_ALIAS) : EntityField | ||||
|     { | ||||
| 
 | ||||
|         $default = ( $alias === false ? '' : Repository::DEFAULT_ALIAS ); # bw compatibility, to be deprecated
 | ||||
| 
 | ||||
|         return new EntityField(static::class, $name, $alias ? Ulmus::repository(static::class)->adapter->adapter()->escapeIdentifier($alias, Adapter\AdapterInterface::IDENTIFIER_FIELD) : $default, Ulmus::resolveEntity(static::class)); | ||||
| @ -343,47 +341,17 @@ trait EntityTrait { | ||||
|     #[Ignore]
 | ||||
|     public static function searchRequest(...$arguments) : SearchRequestInterface | ||||
|     { | ||||
|         return new class() implements SearchRequestInterface, \JsonSerializable | ||||
|         { | ||||
|             use SearchRequestPaginationTrait; | ||||
|         return new #[SearchRequest\Attribute\SearchRequestParameter(self::class)] class(... $arguments) extends SearchRequest\SearchRequest {
 | ||||
|             # Define searchable properties here, some ex:
 | ||||
| 
 | ||||
|             public function fromRequest(ServerRequestInterface $request) : self | ||||
|             { | ||||
|                 $get = new \ArrayObject(array_filter($request->getQueryParams(), function($i) { return $i !== ""; })); | ||||
|             #  #[SearchParameter(method: SearchMethodEnum::Where)]
 | ||||
|             #  public ? string $username = null;
 | ||||
| 
 | ||||
|                 $this->page = $get->offsetExists('page') ? $get['page'] : 1; | ||||
|                 $this->limit = 100; | ||||
|             #  #[SearchParameter(method: SearchMethodEnum::Where, toggle: true)]
 | ||||
|             #  public ? string $hidden = null;
 | ||||
| 
 | ||||
|                 return $this; | ||||
|             } | ||||
| 
 | ||||
|             public function filter(Repository $repository) : Repository | ||||
|             { | ||||
|                 return $repository; | ||||
|             } | ||||
| 
 | ||||
|             public function wheres() : iterable | ||||
|             { | ||||
|                 return array_filter([ | ||||
|                     ], fn($i) => ! is_null($i) ) + [ ]; | ||||
|             } | ||||
| 
 | ||||
|             public function likes(): iterable | ||||
|             { | ||||
|                 return array_filter([ | ||||
|                     ], fn($i) => ! is_null($i) ) + []; | ||||
|             } | ||||
| 
 | ||||
|             public function groups(): iterable | ||||
|             { | ||||
|                 return []; | ||||
|             } | ||||
| 
 | ||||
|             public function orders(): iterable | ||||
|             { | ||||
|                 return array_filter([ | ||||
|                 ], fn($e) => ! is_null($e)  && $e !== "" ); | ||||
|             } | ||||
|             #  #[SearchParameter(method: SearchMethodEnum::Like)]
 | ||||
|             #  public ? string $word = null;
 | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| 
 | ||||
| namespace Ulmus\Query; | ||||
| 
 | ||||
| use Ulmus\QueryBuilder; | ||||
| use Ulmus\MysqlQueryBuilder; | ||||
| 
 | ||||
| use Ulmus\Common\EntityField, | ||||
|     Ulmus\Common\Sql; | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| 
 | ||||
| namespace Ulmus\Query; | ||||
| 
 | ||||
| use Ulmus\QueryBuilder; | ||||
| use Ulmus\MysqlQueryBuilder; | ||||
| use Ulmus\Repository\ConditionTrait; | ||||
| 
 | ||||
| class Join extends Fragment | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| 
 | ||||
| namespace Ulmus\Query; | ||||
| 
 | ||||
| use Ulmus\QueryBuilder; | ||||
| use Ulmus\MysqlQueryBuilder; | ||||
| 
 | ||||
| class Set extends Fragment { | ||||
| 
 | ||||
|  | ||||
| @ -10,10 +10,14 @@ class Show extends Fragment { | ||||
| 
 | ||||
|     const SQL_TOKEN_FROM = "FROM"; | ||||
| 
 | ||||
|     const SQL_TOKEN_IN = "IN"; | ||||
| 
 | ||||
|     const SQL_SHOW_DATABASES = "DATABASES"; | ||||
| 
 | ||||
|     const SQL_SHOW_TABLES = "TABLES"; | ||||
| 
 | ||||
|     const SQL_SHOW_COLUMNS = "COLUMNS"; | ||||
| 
 | ||||
|     public string $show; | ||||
| 
 | ||||
|     public string $from; | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| 
 | ||||
| namespace Ulmus\Query; | ||||
| 
 | ||||
| use Ulmus\QueryBuilder; | ||||
| use Ulmus\MysqlQueryBuilder; | ||||
| 
 | ||||
| class Values extends Fragment { | ||||
| 
 | ||||
| @ -12,9 +12,9 @@ class Values extends Fragment { | ||||
|      | ||||
|     public array $rows; | ||||
|      | ||||
|     public QueryBuilder $queryBuilder; | ||||
|     public MysqlQueryBuilder $queryBuilder; | ||||
| 
 | ||||
|     public function __construct(QueryBuilder $queryBuilder) | ||||
|     public function __construct(MysqlQueryBuilder $queryBuilder) | ||||
|     { | ||||
|         $this->queryBuilder = $queryBuilder; | ||||
|     } | ||||
|  | ||||
| @ -2,541 +2,7 @@ | ||||
| 
 | ||||
| namespace Ulmus; | ||||
| 
 | ||||
| use Ulmus\Query\QueryBuilderInterface; | ||||
| 
 | ||||
| class QueryBuilder implements Query\QueryBuilderInterface | ||||
| class QueryBuilder extends QueryBuilder\MysqlQueryBuilder | ||||
| { | ||||
|     public Query\Where $where; | ||||
| 
 | ||||
|     public Query\Having $having; | ||||
| 
 | ||||
|     public QueryBuilderInterface $parent; | ||||
| 
 | ||||
|     /** | ||||
|      * Those are the parameters we are going to bind to PDO. | ||||
|      */ | ||||
|     public array $parameters = []; | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * Those values are to be inserted or updated | ||||
|      */ | ||||
|     public array $values = []; | ||||
| 
 | ||||
|     public string $whereConditionOperator = Query\Where::CONDITION_AND; | ||||
| 
 | ||||
|     public string $havingConditionOperator = Query\Where::CONDITION_AND; | ||||
| 
 | ||||
|     protected int $parameterIndex = 0; | ||||
| 
 | ||||
|     protected array $queryStack = []; | ||||
| 
 | ||||
|     public function __clone() | ||||
|     { | ||||
|         if ($this->where ?? false) { | ||||
|             #$this->where = clone $this->where;
 | ||||
|             #$this->where->queryBuilder = $this;
 | ||||
|         } | ||||
| 
 | ||||
|         if ($this->having ?? false) { | ||||
|             #$this->having = clone $this->having;
 | ||||
|             #$this->having->queryBuiler = $this;
 | ||||
|         } | ||||
| 
 | ||||
|         if ($this->parent ?? false) { | ||||
|             #$this->parent = clone $this->parent;
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function select(string|\Stringable|array $field, bool $distinct = false) : self | ||||
|     { | ||||
|         if ( null !== ( $select = $this->getFragment(Query\Select::class) ) ) { | ||||
|             $select->add($field); | ||||
|         } | ||||
|         else { | ||||
|             $select = new Query\Select(); | ||||
|             $select->set($field); | ||||
|             $this->push($select); | ||||
|         } | ||||
| 
 | ||||
|         $select->distinct = $distinct; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function insert(array $fieldlist, string $table, ? string $alias = null, ? string $database = null, ? string $schema = null, bool $replace = false) : self | ||||
|     { | ||||
|         if ( null === $this->getFragment(Query\Insert::class) ) { | ||||
|             if ( $schema ) { | ||||
|                 $table = "$schema.$table"; | ||||
|             } | ||||
| 
 | ||||
|             if ( $database ) { | ||||
|                 $table = "$database.$table"; | ||||
|             } | ||||
| 
 | ||||
|             $insert = new Query\Insert(); | ||||
|             $this->push($insert); | ||||
| 
 | ||||
|             $insert->replace = $replace; | ||||
|             $insert->fieldlist = $fieldlist; | ||||
|             $insert->alias = $alias; | ||||
|             $insert->table = $table; | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function values(array $dataset) : self | ||||
|     { | ||||
|         if ( null === ( $values = $this->getFragment(Query\Values::class) ) ) { | ||||
|             $values = new Query\Values($this); | ||||
|             $this->push($values); | ||||
|         } | ||||
| 
 | ||||
|         $values->add($dataset); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function update(string $table, ? string $alias = null, ? string $database = null, ? string $schema = null) : self | ||||
|     { | ||||
|         if ( ! $this->getFragment(Query\Update::class) ) { | ||||
|             if ( $schema ) { | ||||
|                 $table = "$schema.$table"; | ||||
|             } | ||||
| 
 | ||||
|             if ( $database ) { | ||||
|                 $table = "$database.$table"; | ||||
|             } | ||||
| 
 | ||||
|             $update = new Query\Update(); | ||||
|             $this->push($update); | ||||
| 
 | ||||
|             $update->alias = $alias; | ||||
|             $update->table = $table; | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function set(array $dataset, ? array $escapedFields = null) : self | ||||
|     { | ||||
|         if ( null === ( $set = $this->getFragment(Query\Set::class) ) ) { | ||||
|             $set = new Query\Set($this); | ||||
|             $this->push($set); | ||||
|         } | ||||
| 
 | ||||
|         $set->set($dataset, $escapedFields); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function delete() : self | ||||
|     { | ||||
|         if ( ! $this->getFragment(Query\Delete::class) ) { | ||||
|             $this->push(new Query\Delete()); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function from(string $table, ? string $alias = null, ? string $database = null, ? string $schema = null) : self | ||||
|     { | ||||
|         if ( $schema ) { | ||||
|             $table = "$schema.$table"; | ||||
|         } | ||||
| 
 | ||||
|         if ( $database ) { | ||||
|             $table = "$database.$table"; | ||||
|         } | ||||
| 
 | ||||
|         if ( null !== ( $from = $this->getFragment(Query\From::class) ) ) { | ||||
|             $from->add($alias ? [ $alias => $table ] : $table); | ||||
|         } | ||||
|         else { | ||||
|             $from = new Query\From($this); | ||||
|             $this->push($from); | ||||
| 
 | ||||
|             $from->set($alias ? [ $alias => $table ] : [ $table ]); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function open(string $condition = Query\Where::CONDITION_AND) : self | ||||
|     { | ||||
|         if ( null !== ($this->where ?? null) ) { | ||||
|             $this->where->conditionList[] = $new = new Query\Where($this, $condition); | ||||
|             $this->where = $new; | ||||
|         } | ||||
|         else { | ||||
|             $this->where = new Query\Where($this, $condition); | ||||
|             $this->push($this->where); | ||||
|             $this->where->conditionList[] = $new = new Query\Where($this, $condition); | ||||
|             $this->where = $new; | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function close() : self | ||||
|     { | ||||
|         if ( null !== ($this->where ?? null) && $this->where->parent ) { | ||||
| 
 | ||||
|             # if an enclosure was opened, and nothing done, we must remove the unused node
 | ||||
|             if ( empty($this->where->conditionList) && (count($this->where->parent->conditionList) === 1) ) { | ||||
|                 unset($this->where->parent->conditionList); | ||||
|             } | ||||
| 
 | ||||
|             $this->where = $this->where->parent; | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function where(string|\Stringable $field, mixed $value, string $operator = Query\Where::OPERATOR_EQUAL, string $condition = Query\Where::CONDITION_AND, bool $not = false) : self | ||||
|     { | ||||
|         # Empty IN case
 | ||||
|         # if ( [] === $value ) {
 | ||||
|         #     return $this;
 | ||||
|         # }
 | ||||
| 
 | ||||
|         if ( $this->where ?? false ) { | ||||
|             $where = $this->where; | ||||
|         } | ||||
|         elseif ( null === ( $where = $this->getFragment(Query\Where::class) ) ) { | ||||
|             $this->where = $where = new Query\Where($this); | ||||
|             $this->push($where); | ||||
|         } | ||||
| 
 | ||||
|         $this->whereConditionOperator = $operator; | ||||
| 
 | ||||
|         $where->add($field, $value, $operator, $condition, $not); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function notWhere(string|\Stringable $field, mixed $value, string $operator = Query\Where::CONDITION_AND) : self | ||||
|     { | ||||
|         return $this->where($field, $value, $operator, true); | ||||
|     } | ||||
| 
 | ||||
|     public function having(string|\Stringable $field, mixed $value, string $operator = Query\Where::OPERATOR_EQUAL, string $condition = Query\Where::CONDITION_AND, bool $not = false) : self | ||||
|     { | ||||
|         if ( $this->having ?? false ) { | ||||
|             $having = $this->having; | ||||
|         } | ||||
|         elseif ( null === ( $having = $this->getFragment(Query\Having::class) ) ) { | ||||
|             $this->having = $having = new Query\Having($this); | ||||
|             $this->push($having); | ||||
|         } | ||||
| 
 | ||||
|         $this->havingConditionOperator = $operator; | ||||
|         $having->add($field, $value, $operator, $condition, $not); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function limit(int $value) : self | ||||
|     { | ||||
|         if ( null === $limit = $this->getFragment(Query\Limit::class) ) { | ||||
|             $limit = new Query\Limit(); | ||||
|             $this->push($limit); | ||||
|         } | ||||
| 
 | ||||
|         $limit->set($value); | ||||
| 
 | ||||
|         if ($value === 0) { | ||||
|             $this->removeFragment(Query\Limit::class); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function offset(int $value) : self | ||||
|     { | ||||
|         if ( null === $offset = $this->getFragment(Query\Offset::class) ) { | ||||
|             $offset = new Query\Offset(); | ||||
|             $this->push($offset); | ||||
| 
 | ||||
|             # A limit is required to match an offset
 | ||||
|             if ( null === $limit = $this->getFragment(Query\Limit::class) ) { | ||||
|                 $this->limit(\PHP_INT_MAX); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         $offset->set($value); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function orderBy(string|\Stringable $field, ? string $direction = null) : self | ||||
|     { | ||||
|         if ( null === $orderBy = $this->getFragment(Query\OrderBy::class) ) { | ||||
|             $orderBy = new Query\OrderBy(); | ||||
|             $this->push($orderBy); | ||||
|         } | ||||
| 
 | ||||
|         $orderBy->add($field, $direction); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function groupBy(string|object $field, ? string $direction = null) : self | ||||
|     { | ||||
|         if ( null === $groupBy = $this->getFragment(Query\GroupBy::class) ) { | ||||
|             $groupBy = new Query\GroupBy(); | ||||
|             $this->push($groupBy); | ||||
|         } | ||||
| 
 | ||||
|         $groupBy->add($field, $direction); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function join(string $type, /*string | QueryBuilder*/ $table, mixed $field, mixed $value, bool $outer = false, ? string $alias = null) : self | ||||
|     { | ||||
|         $this->withJoin(...func_get_args()); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function withJoin(string $type, $table, mixed $field, mixed $value, bool $outer = false, ? string $alias = null) : Query\Join | ||||
|     { | ||||
|         $join = new Query\Join($this); | ||||
| 
 | ||||
|         $this->push($join); | ||||
| 
 | ||||
|         $join->set($type, $table, $field, $value); | ||||
| 
 | ||||
|         $join->outer = $outer; | ||||
| 
 | ||||
|         $join->alias = $alias; | ||||
| 
 | ||||
|         $join->joinOrder = $this->nextJoinOrder(); | ||||
| 
 | ||||
|         return $join; | ||||
|     } | ||||
| 
 | ||||
|     public function showDatabases() : self | ||||
|     { | ||||
|         $show = new Query\Show(); | ||||
| 
 | ||||
|         $this->push($show); | ||||
| 
 | ||||
|         $show->set(Query\Show::SQL_SHOW_DATABASES); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function showTables(? string $database = null) : self | ||||
|     { | ||||
|         $show = new Query\Show(); | ||||
| 
 | ||||
|         $this->push($show); | ||||
| 
 | ||||
|         $show->set(Query\Show::SQL_SHOW_TABLES, $database); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function showColumns(string $table) : self | ||||
|     { | ||||
|         $show = new Query\Show(); | ||||
| 
 | ||||
|         $this->push($show); | ||||
| 
 | ||||
|         $show->set(Query\Show::SQL_SHOW_COLUMNS, $table); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function truncate(string $table, ? string $alias = null, ? string $database = null, ? string $schema = null) : self | ||||
|     { | ||||
|         if ( $schema ) { | ||||
|             $table = "$schema.$table"; | ||||
|         } | ||||
| 
 | ||||
|         if ( $database ) { | ||||
|             $table = "$database.$table"; | ||||
|         } | ||||
| 
 | ||||
|         $truncate = new Query\Truncate($this); | ||||
| 
 | ||||
|         $this->push($truncate); | ||||
| 
 | ||||
|         $truncate->set($table); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function create(Adapter\AdapterInterface $adapter, array $fieldlist, string $table, ? string $database = null, ? string $schema = null) : self | ||||
|     { | ||||
|         if ( null === $this->getFragment(Query\Create::class) ) { | ||||
|             if ( $schema ) { | ||||
|                 $table = "$schema.$table"; | ||||
|             } | ||||
| 
 | ||||
|             if ( $database ) { | ||||
|                 $table = "$database.$table"; | ||||
|             } | ||||
| 
 | ||||
|             $create = new Query\Create($adapter); | ||||
|             $this->push($create); | ||||
| 
 | ||||
|             $create->fieldList = $fieldlist; | ||||
|             $create->table = $table; | ||||
|         } | ||||
|         else { | ||||
|             throw new \Exception("A create SQL fragment was already found within the query builder"); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function alter(Adapter\AdapterInterface $adapter, array $fieldlist, string $table, ? string $database = null, ? string $schema = null) : self | ||||
|     { | ||||
|         if ( null === $this->getFragment(Query\Alter::class) ) { | ||||
|             if ( $schema ) { | ||||
|                 $table = "$schema.$table"; | ||||
|             } | ||||
| 
 | ||||
|             if ( $database ) { | ||||
|                 $table = "$database.$table"; | ||||
|             } | ||||
| 
 | ||||
|             $alter = new Query\Alter($adapter); | ||||
|             $this->push($alter); | ||||
| 
 | ||||
|             $alter->fieldList = $fieldlist; | ||||
|             $alter->table = $table; | ||||
|         } | ||||
|         else { | ||||
|             throw new \Exception("A create SQL fragment was already found within the query builder"); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function engine(string $value) : self | ||||
|     { | ||||
|         if ( null === $engine = $this->getFragment(Query\Engine::class) ) { | ||||
|             $engine = new Query\Engine(); | ||||
|             $this->push($engine); | ||||
|         } | ||||
| 
 | ||||
|         $engine->engine = $value; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function push(Query\Fragment $queryFragment) : self | ||||
|     { | ||||
|         $this->queryStack[] = $queryFragment; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function pull(Query\Fragment $queryFragment) : self | ||||
|     { | ||||
|         return array_shift($this->queryStack); | ||||
|     } | ||||
| 
 | ||||
|     public function render(bool $skipToken = false) /* : mixed */ | ||||
|     { | ||||
|         $sql = []; | ||||
| 
 | ||||
|         usort($this->queryStack, function($q1, $q2) { | ||||
|             return (float) $q1->order() <=> (float) $q2->order(); | ||||
|         }); | ||||
| 
 | ||||
|         foreach($this->queryStack as $fragment) { | ||||
|             $sql[] = $fragment->render($skipToken); | ||||
|         } | ||||
| 
 | ||||
|         return implode(" ", $sql); | ||||
|     } | ||||
| 
 | ||||
|     public function reset() : void | ||||
|     { | ||||
|         $this->parameters = $this->values = $this->queryStack = []; | ||||
|         $this->whereConditionOperator = Query\Where::CONDITION_AND; | ||||
|         $this->havingConditionOperator = Query\Where::CONDITION_AND; | ||||
|         $this->parameterIndex = 0; | ||||
| 
 | ||||
|         unset($this->where, $this->having); | ||||
|     } | ||||
| 
 | ||||
|     public function getFragment(string $class, int $index = 0) : ? Query\Fragment | ||||
|     { | ||||
|         foreach($this->queryStack as $item) { | ||||
|             if ( is_a($item, $class, true) ) { | ||||
|                 if ( $index-- === 0 ) { | ||||
|                     return $item; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     public function removeFragment(Query\Fragment|array|\Stringable|string $fragment) : void | ||||
|     { | ||||
|         is_object($fragment) && $fragment = get_class($fragment); | ||||
| 
 | ||||
|         foreach($this->queryStack as $key => $item) { | ||||
|             if ( get_class($item) === $fragment ) { | ||||
|                 unset($this->queryStack[$key]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ( $fragment === Query\Where::class ) { | ||||
|             unset($this->where); | ||||
|         } | ||||
|         elseif ( $fragment === Query\Having::class ) { | ||||
|             unset($this->having); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function getFragments(Query\Fragment|array|\Stringable|string $fragment = null) : array | ||||
|     { | ||||
|         return $fragment !== null ? array_filter($this->queryStack, fn($e) => is_a($e, $fragment, true) ) : $this->queryStack; | ||||
|     } | ||||
| 
 | ||||
|     public function __toString() : string | ||||
|     { | ||||
|         return $this->render(); | ||||
|     } | ||||
| 
 | ||||
|     public function addParameter(mixed $value, string $key = null) : string | ||||
|     { | ||||
|         if ( $this->parent ?? false ) { | ||||
|             return $this->parent->addParameter($value, $key); | ||||
|         } | ||||
| 
 | ||||
|         if ( $key === null ) { | ||||
|             $key = ":p" . $this->parameterIndex++; | ||||
|         } | ||||
| 
 | ||||
|         $this->parameters[$key] = $value; | ||||
| 
 | ||||
|         return $key; | ||||
|     } | ||||
| 
 | ||||
|     public function addValues(array $values) : void | ||||
|     { | ||||
|         $this->values = $values; | ||||
|     } | ||||
|      | ||||
|     protected function nextJoinOrder() : float | ||||
|     { | ||||
|         $next = 0; | ||||
| 
 | ||||
|         foreach($this->getFragments(Query\Join::class) as $join) { | ||||
|             $next = max($next, $join->joinOrder - Query\Join::ORDER_VALUE); | ||||
|         } | ||||
| 
 | ||||
|         return $next + 1; | ||||
|     } | ||||
| } | ||||
|     # Backward compatibility defaulting on MySQL/MariaDB query builder
 | ||||
| } | ||||
| @ -1,10 +1,12 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\QueryBuilder; | ||||
| namespace Ulmus\QueryBuilder\Sql; | ||||
| 
 | ||||
| use Ulmus\{Query, QueryBuilder }; | ||||
| use Ulmus\{ Query }; | ||||
| use Ulmus\QueryBuilder\SqlQueryBuilder; | ||||
| 
 | ||||
| class MssqlQueryBuilder extends QueryBuilder implements Query\QueryBuilderInterface | ||||
| # Soon to extends SqlQueryBuilder
 | ||||
| class MssqlQueryBuilder extends MysqlQueryBuilder implements Query\QueryBuilderInterface | ||||
| { | ||||
|     public function limit(int $value) : self | ||||
|     { | ||||
							
								
								
									
										543
									
								
								src/QueryBuilder/Sql/MysqlQueryBuilder.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										543
									
								
								src/QueryBuilder/Sql/MysqlQueryBuilder.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,543 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\QueryBuilder\Sql; | ||||
| 
 | ||||
| use Ulmus\{Query, Adapter, QueryBuilder\SqlQueryBuilder}; | ||||
| use Ulmus\Query\QueryBuilderInterface; | ||||
| 
 | ||||
| class MysqlQueryBuilder extends SqlQueryBuilder | ||||
| { | ||||
|     public Query\Where $where; | ||||
| 
 | ||||
|     public Query\Having $having; | ||||
| 
 | ||||
|     public QueryBuilderInterface $parent; | ||||
| 
 | ||||
|     /** | ||||
|      * Those are the parameters we are going to bind to PDO. | ||||
|      */ | ||||
|     public array $parameters = []; | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * Those values are to be inserted or updated | ||||
|      */ | ||||
|     public array $values = []; | ||||
| 
 | ||||
|     public string $whereConditionOperator = Query\Where::CONDITION_AND; | ||||
| 
 | ||||
|     public string $havingConditionOperator = Query\Where::CONDITION_AND; | ||||
| 
 | ||||
|     protected int $parameterIndex = 0; | ||||
| 
 | ||||
|     protected array $queryStack = []; | ||||
| 
 | ||||
|     public function __clone() | ||||
|     { | ||||
|         if ($this->where ?? false) { | ||||
|             #$this->where = clone $this->where;
 | ||||
|             #$this->where->queryBuilder = $this;
 | ||||
|         } | ||||
| 
 | ||||
|         if ($this->having ?? false) { | ||||
|             #$this->having = clone $this->having;
 | ||||
|             #$this->having->queryBuiler = $this;
 | ||||
|         } | ||||
| 
 | ||||
|         if ($this->parent ?? false) { | ||||
|             #$this->parent = clone $this->parent;
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function select(string|\Stringable|array $field, bool $distinct = false) : self | ||||
|     { | ||||
|         if ( null !== ( $select = $this->getFragment(Query\Select::class) ) ) { | ||||
|             $select->add($field); | ||||
|         } | ||||
|         else { | ||||
|             $select = new Query\Select(); | ||||
|             $select->set($field); | ||||
|             $this->push($select); | ||||
|         } | ||||
| 
 | ||||
|         $select->distinct = $distinct; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function insert(array $fieldlist, string $table, ? string $alias = null, ? string $database = null, ? string $schema = null, bool $replace = false) : self | ||||
|     { | ||||
|         if ( null === $this->getFragment(Query\Insert::class) ) { | ||||
|             if ( $schema ) { | ||||
|                 $table = "$schema.$table"; | ||||
|             } | ||||
| 
 | ||||
|             if ( $database ) { | ||||
|                 $table = "$database.$table"; | ||||
|             } | ||||
| 
 | ||||
|             $insert = new Query\Insert(); | ||||
|             $this->push($insert); | ||||
| 
 | ||||
|             $insert->replace = $replace; | ||||
|             $insert->fieldlist = $fieldlist; | ||||
|             $insert->alias = $alias; | ||||
|             $insert->table = $table; | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function values(array $dataset) : self | ||||
|     { | ||||
|         if ( null === ( $values = $this->getFragment(Query\Values::class) ) ) { | ||||
|             $values = new Query\Values($this); | ||||
|             $this->push($values); | ||||
|         } | ||||
| 
 | ||||
|         $values->add($dataset); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function update(string $table, ? string $alias = null, ? string $database = null, ? string $schema = null) : self | ||||
|     { | ||||
|         if ( ! $this->getFragment(Query\Update::class) ) { | ||||
|             if ( $schema ) { | ||||
|                 $table = "$schema.$table"; | ||||
|             } | ||||
| 
 | ||||
|             if ( $database ) { | ||||
|                 $table = "$database.$table"; | ||||
|             } | ||||
| 
 | ||||
|             $update = new Query\Update(); | ||||
|             $this->push($update); | ||||
| 
 | ||||
|             $update->alias = $alias; | ||||
|             $update->table = $table; | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function set(array $dataset, ? array $escapedFields = null) : self | ||||
|     { | ||||
|         if ( null === ( $set = $this->getFragment(Query\Set::class) ) ) { | ||||
|             $set = new Query\Set($this); | ||||
|             $this->push($set); | ||||
|         } | ||||
| 
 | ||||
|         $set->set($dataset, $escapedFields); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function delete() : self | ||||
|     { | ||||
|         if ( ! $this->getFragment(Query\Delete::class) ) { | ||||
|             $this->push(new Query\Delete()); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function from(string $table, ? string $alias = null, ? string $database = null, ? string $schema = null) : self | ||||
|     { | ||||
|         if ( $schema ) { | ||||
|             $table = "$schema.$table"; | ||||
|         } | ||||
| 
 | ||||
|         if ( $database ) { | ||||
|             $table = "$database.$table"; | ||||
|         } | ||||
| 
 | ||||
|         if ( null !== ( $from = $this->getFragment(Query\From::class) ) ) { | ||||
|             $from->add($alias ? [ $alias => $table ] : $table); | ||||
|         } | ||||
|         else { | ||||
|             $from = new Query\From($this); | ||||
|             $this->push($from); | ||||
| 
 | ||||
|             $from->set($alias ? [ $alias => $table ] : [ $table ]); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function open(string $condition = Query\Where::CONDITION_AND) : self | ||||
|     { | ||||
|         if ( null !== ($this->where ?? null) ) { | ||||
|             $this->where->conditionList[] = $new = new Query\Where($this, $condition); | ||||
|             $this->where = $new; | ||||
|         } | ||||
|         else { | ||||
|             $this->where = new Query\Where($this, $condition); | ||||
|             $this->push($this->where); | ||||
|             $this->where->conditionList[] = $new = new Query\Where($this, $condition); | ||||
|             $this->where = $new; | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function close() : self | ||||
|     { | ||||
|         if ( null !== ($this->where ?? null) && $this->where->parent ) { | ||||
| 
 | ||||
|             # if an enclosure was opened, and nothing done, we must remove the unused node
 | ||||
|             if ( empty($this->where->conditionList) && (count($this->where->parent->conditionList) === 1) ) { | ||||
|                 unset($this->where->parent->conditionList); | ||||
|             } | ||||
| 
 | ||||
|             $this->where = $this->where->parent; | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function where(string|\Stringable $field, mixed $value, string $operator = Query\Where::OPERATOR_EQUAL, string $condition = Query\Where::CONDITION_AND, bool $not = false) : self | ||||
|     { | ||||
|         # Empty IN case
 | ||||
|         # if ( [] === $value ) {
 | ||||
|         #     return $this;
 | ||||
|         # }
 | ||||
| 
 | ||||
|         if ( $this->where ?? false ) { | ||||
|             $where = $this->where; | ||||
|         } | ||||
|         elseif ( null === ( $where = $this->getFragment(Query\Where::class) ) ) { | ||||
|             $this->where = $where = new Query\Where($this); | ||||
|             $this->push($where); | ||||
|         } | ||||
| 
 | ||||
|         $this->whereConditionOperator = $operator; | ||||
| 
 | ||||
|         $where->add($field, $value, $operator, $condition, $not); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function notWhere(string|\Stringable $field, mixed $value, string $operator = Query\Where::CONDITION_AND) : self | ||||
|     { | ||||
|         return $this->where($field, $value, $operator, true); | ||||
|     } | ||||
| 
 | ||||
|     public function having(string|\Stringable $field, mixed $value, string $operator = Query\Where::OPERATOR_EQUAL, string $condition = Query\Where::CONDITION_AND, bool $not = false) : self | ||||
|     { | ||||
|         if ( $this->having ?? false ) { | ||||
|             $having = $this->having; | ||||
|         } | ||||
|         elseif ( null === ( $having = $this->getFragment(Query\Having::class) ) ) { | ||||
|             $this->having = $having = new Query\Having($this); | ||||
|             $this->push($having); | ||||
|         } | ||||
| 
 | ||||
|         $this->havingConditionOperator = $operator; | ||||
|         $having->add($field, $value, $operator, $condition, $not); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function limit(int $value) : self | ||||
|     { | ||||
|         if ( null === $limit = $this->getFragment(Query\Limit::class) ) { | ||||
|             $limit = new Query\Limit(); | ||||
|             $this->push($limit); | ||||
|         } | ||||
| 
 | ||||
|         $limit->set($value); | ||||
| 
 | ||||
|         if ($value === 0) { | ||||
|             $this->removeFragment(Query\Limit::class); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function offset(int $value) : self | ||||
|     { | ||||
|         if ( null === $offset = $this->getFragment(Query\Offset::class) ) { | ||||
|             $offset = new Query\Offset(); | ||||
|             $this->push($offset); | ||||
| 
 | ||||
|             # A limit is required to match an offset
 | ||||
|             if ( null === $limit = $this->getFragment(Query\Limit::class) ) { | ||||
|                 $this->limit(\PHP_INT_MAX); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         $offset->set($value); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function orderBy(string|\Stringable $field, ? string $direction = null) : self | ||||
|     { | ||||
|         if ( null === $orderBy = $this->getFragment(Query\OrderBy::class) ) { | ||||
|             $orderBy = new Query\OrderBy(); | ||||
|             $this->push($orderBy); | ||||
|         } | ||||
| 
 | ||||
|         $orderBy->add($field, $direction); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function groupBy(string|object $field, ? string $direction = null) : self | ||||
|     { | ||||
|         if ( null === $groupBy = $this->getFragment(Query\GroupBy::class) ) { | ||||
|             $groupBy = new Query\GroupBy(); | ||||
|             $this->push($groupBy); | ||||
|         } | ||||
| 
 | ||||
|         $groupBy->add($field, $direction); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function join(string $type, /*string | QueryBuilder*/ $table, mixed $field, mixed $value, bool $outer = false, ? string $alias = null) : self | ||||
|     { | ||||
|         $this->withJoin(...func_get_args()); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function withJoin(string $type, $table, mixed $field, mixed $value, bool $outer = false, ? string $alias = null) : Query\Join | ||||
|     { | ||||
|         $join = new Query\Join($this); | ||||
| 
 | ||||
|         $this->push($join); | ||||
| 
 | ||||
|         $join->set($type, $table, $field, $value); | ||||
| 
 | ||||
|         $join->outer = $outer; | ||||
| 
 | ||||
|         $join->alias = $alias; | ||||
| 
 | ||||
|         $join->joinOrder = $this->nextJoinOrder(); | ||||
| 
 | ||||
|         return $join; | ||||
|     } | ||||
| 
 | ||||
|     public function showDatabases() : self | ||||
|     { | ||||
|         $show = new Query\Show(); | ||||
| 
 | ||||
|         $this->push($show); | ||||
| 
 | ||||
|         $show->set(Query\Show::SQL_SHOW_DATABASES); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function showTables(? string $database = null) : self | ||||
|     { | ||||
|         $show = new Query\Show(); | ||||
| 
 | ||||
|         $this->push($show); | ||||
| 
 | ||||
|         $show->set(Query\Show::SQL_SHOW_TABLES, $database); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function showColumns(string $table) : self | ||||
|     { | ||||
|         $show = new Query\Show(); | ||||
| 
 | ||||
|         $this->push($show); | ||||
| 
 | ||||
|         $show->set(Query\Show::SQL_SHOW_COLUMNS, $table); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function truncate(string $table, ? string $alias = null, ? string $database = null, ? string $schema = null) : self | ||||
|     { | ||||
|         if ( $schema ) { | ||||
|             $table = "$schema.$table"; | ||||
|         } | ||||
| 
 | ||||
|         if ( $database ) { | ||||
|             $table = "$database.$table"; | ||||
|         } | ||||
| 
 | ||||
|         $truncate = new Query\Truncate($this); | ||||
| 
 | ||||
|         $this->push($truncate); | ||||
| 
 | ||||
|         $truncate->set($table); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function create(Adapter\AdapterInterface $adapter, array $fieldlist, string $table, ? string $database = null, ? string $schema = null) : self | ||||
|     { | ||||
|         if ( null === $this->getFragment(Query\Create::class) ) { | ||||
|             if ( $schema ) { | ||||
|                 $table = "$schema.$table"; | ||||
|             } | ||||
| 
 | ||||
|             if ( $database ) { | ||||
|                 $table = "$database.$table"; | ||||
|             } | ||||
| 
 | ||||
|             $create = new Query\Create($adapter); | ||||
|             $this->push($create); | ||||
| 
 | ||||
|             $create->fieldList = $fieldlist; | ||||
|             $create->table = $table; | ||||
|         } | ||||
|         else { | ||||
|             throw new \Exception("A create SQL fragment was already found within the query builder"); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function alter(Adapter\AdapterInterface $adapter, array $fieldlist, string $table, ? string $database = null, ? string $schema = null) : self | ||||
|     { | ||||
|         if ( null === $this->getFragment(Query\Alter::class) ) { | ||||
|             if ( $schema ) { | ||||
|                 $table = "$schema.$table"; | ||||
|             } | ||||
| 
 | ||||
|             if ( $database ) { | ||||
|                 $table = "$database.$table"; | ||||
|             } | ||||
| 
 | ||||
|             $alter = new Query\Alter($adapter); | ||||
|             $this->push($alter); | ||||
| 
 | ||||
|             $alter->fieldList = $fieldlist; | ||||
|             $alter->table = $table; | ||||
|         } | ||||
|         else { | ||||
|             throw new \Exception("A create SQL fragment was already found within the query builder"); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function engine(string $value) : self | ||||
|     { | ||||
|         if ( null === $engine = $this->getFragment(Query\Engine::class) ) { | ||||
|             $engine = new Query\Engine(); | ||||
|             $this->push($engine); | ||||
|         } | ||||
| 
 | ||||
|         $engine->engine = $value; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function push(Query\Fragment $queryFragment) : self | ||||
|     { | ||||
|         $this->queryStack[] = $queryFragment; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function pull(Query\Fragment $queryFragment) : self | ||||
|     { | ||||
|         return array_shift($this->queryStack); | ||||
|     } | ||||
| 
 | ||||
|     public function render(bool $skipToken = false) /* : mixed */ | ||||
|     { | ||||
|         $sql = []; | ||||
| 
 | ||||
|         usort($this->queryStack, function($q1, $q2) { | ||||
|             return (float) $q1->order() <=> (float) $q2->order(); | ||||
|         }); | ||||
| 
 | ||||
|         foreach($this->queryStack as $fragment) { | ||||
|             $sql[] = $fragment->render($skipToken); | ||||
|         } | ||||
| 
 | ||||
|         return implode(" ", $sql); | ||||
|     } | ||||
| 
 | ||||
|     public function reset() : void | ||||
|     { | ||||
|         $this->parameters = $this->values = $this->queryStack = []; | ||||
|         $this->whereConditionOperator = Query\Where::CONDITION_AND; | ||||
|         $this->havingConditionOperator = Query\Where::CONDITION_AND; | ||||
|         $this->parameterIndex = 0; | ||||
| 
 | ||||
|         unset($this->where, $this->having); | ||||
|     } | ||||
| 
 | ||||
|     public function getFragment(string $class, int $index = 0) : ? Query\Fragment | ||||
|     { | ||||
|         foreach($this->queryStack as $item) { | ||||
|             if ( is_a($item, $class, true) ) { | ||||
|                 if ( $index-- === 0 ) { | ||||
|                     return $item; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     public function removeFragment(Query\Fragment|array|\Stringable|string $fragment) : void | ||||
|     { | ||||
|         is_object($fragment) && $fragment = get_class($fragment); | ||||
| 
 | ||||
|         foreach($this->queryStack as $key => $item) { | ||||
|             if ( get_class($item) === $fragment ) { | ||||
|                 unset($this->queryStack[$key]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ( $fragment === Query\Where::class ) { | ||||
|             unset($this->where); | ||||
|         } | ||||
|         elseif ( $fragment === Query\Having::class ) { | ||||
|             unset($this->having); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function getFragments(Query\Fragment|array|\Stringable|string $fragment = null) : array | ||||
|     { | ||||
|         return $fragment !== null ? array_filter($this->queryStack, fn($e) => is_a($e, $fragment, true) ) : $this->queryStack; | ||||
|     } | ||||
| 
 | ||||
|     public function __toString() : string | ||||
|     { | ||||
|         return $this->render(); | ||||
|     } | ||||
| 
 | ||||
|     public function addParameter(mixed $value, string $key = null) : string | ||||
|     { | ||||
|         if ( $this->parent ?? false ) { | ||||
|             return $this->parent->addParameter($value, $key); | ||||
|         } | ||||
| 
 | ||||
|         if ( $key === null ) { | ||||
|             $key = ":p" . $this->parameterIndex++; | ||||
|         } | ||||
| 
 | ||||
|         $this->parameters[$key] = $value; | ||||
| 
 | ||||
|         return $key; | ||||
|     } | ||||
| 
 | ||||
|     public function addValues(array $values) : void | ||||
|     { | ||||
|         $this->values = $values; | ||||
|     } | ||||
|      | ||||
|     protected function nextJoinOrder() : float | ||||
|     { | ||||
|         $next = 0; | ||||
| 
 | ||||
|         foreach($this->getFragments(Query\Join::class) as $join) { | ||||
|             $next = max($next, $join->joinOrder - Query\Join::ORDER_VALUE); | ||||
|         } | ||||
| 
 | ||||
|         return $next + 1; | ||||
|     } | ||||
| } | ||||
| @ -1,12 +1,12 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\QueryBuilder; | ||||
| 
 | ||||
| use Ulmus\QueryBuilder; | ||||
| namespace Ulmus\QueryBuilder\Sql; | ||||
| 
 | ||||
| use Ulmus\Query; | ||||
| use Ulmus\QueryBuilder\SqlQueryBuilder; | ||||
| 
 | ||||
| class SqliteQueryBuilder extends QueryBuilder implements Query\QueryBuilderInterface | ||||
| # Soon to extends SqlQueryBuilder
 | ||||
| class SqliteQueryBuilder extends MysqlQueryBuilder implements Query\QueryBuilderInterface | ||||
| { | ||||
|     public function pragma(/*object|Stringable*/ $name, $value = null, bool $callable = false) : self | ||||
|     { | ||||
| @ -21,7 +21,7 @@ class SqliteQueryBuilder extends QueryBuilder implements Query\QueryBuilderInter | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     public function showColumns(string $table) : self | ||||
|     { | ||||
|         $this->pragma('table_info', $table, true); | ||||
							
								
								
									
										42
									
								
								src/QueryBuilder/SqlQueryBuilder.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/QueryBuilder/SqlQueryBuilder.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\QueryBuilder; | ||||
| 
 | ||||
| use Ulmus\Query; | ||||
| use Ulmus\Query\Fragment; | ||||
| use Ulmus\Query\QueryBuilderInterface; | ||||
| 
 | ||||
| # TODO -> Extract from MysqlQueryBuilder to build an ISO/IEC 9075:2023 compatible layer for a basic SQL QueryBuilder
 | ||||
| class SqlQueryBuilder implements Query\QueryBuilderInterface | ||||
| { | ||||
| 
 | ||||
|     public function push(Fragment $queryFragment): Query\QueryBuilderInterface | ||||
|     { | ||||
|         // TODO: Implement push() method.
 | ||||
|     } | ||||
| 
 | ||||
|     public function pull(Fragment $queryFragment): Query\QueryBuilderInterface | ||||
|     { | ||||
|         // TODO: Implement pull() method.
 | ||||
|     } | ||||
| 
 | ||||
|     public function render(bool $skipToken = false) | ||||
|     { | ||||
|         // TODO: Implement render() method.
 | ||||
|     } | ||||
| 
 | ||||
|     public function reset(): void | ||||
|     { | ||||
|         // TODO: Implement reset() method.
 | ||||
|     } | ||||
| 
 | ||||
|     public function getFragment(string $class, int $index = 0): ?Fragment | ||||
|     { | ||||
|         // TODO: Implement getFragment() method.
 | ||||
|     } | ||||
| 
 | ||||
|     public function removeFragment(Fragment|\Stringable|array|string $fragment): void | ||||
|     { | ||||
|         // TODO: Implement removeFragment() method.
 | ||||
|     } | ||||
| } | ||||
| @ -2,7 +2,7 @@ | ||||
| 
 | ||||
| namespace Ulmus\Repository; | ||||
| 
 | ||||
| use Ulmus\{Common\EntityResolver, ConnectionAdapter, QueryBuilder, Repository, Query, Ulmus}; | ||||
| use Ulmus\{Common\EntityResolver, ConnectionAdapter, MysqlQueryBuilder, Repository, Query, Ulmus}; | ||||
| 
 | ||||
| class MysqlRepository extends Repository { | ||||
| 
 | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| 
 | ||||
| namespace Ulmus\Repository; | ||||
| 
 | ||||
| use Ulmus\{ConnectionAdapter, QueryBuilder, Repository, Query, Ulmus, Entity}; | ||||
| use Ulmus\{ConnectionAdapter, MysqlQueryBuilder, Repository, Query, Ulmus, Entity}; | ||||
| 
 | ||||
| class SqliteRepository extends Repository { | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										6
									
								
								src/SearchRequest/Attribute/SearchEqual.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/SearchRequest/Attribute/SearchEqual.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\SearchRequest\Attribute; | ||||
| 
 | ||||
| #[\Attribute(\Attribute::TARGET_PROPERTY)]
 | ||||
| class SearchEqual extends SearchWhere {} | ||||
							
								
								
									
										16
									
								
								src/SearchRequest/Attribute/SearchGroupBy.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/SearchRequest/Attribute/SearchGroupBy.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\SearchRequest\Attribute; | ||||
| 
 | ||||
| use Ulmus\SearchRequest\SearchMethodEnum; | ||||
| 
 | ||||
| #[\Attribute(\Attribute::TARGET_PROPERTY)]
 | ||||
| class SearchGroupBy extends SearchParameter | ||||
| { | ||||
|     public function __construct( | ||||
|         public ? string $parameter = null, | ||||
|         public ? string $field = null, | ||||
|         public bool $toggle = false, | ||||
|         public SearchMethodEnum $method = SearchMethodEnum::GroupBy, | ||||
|     ) {} | ||||
| } | ||||
							
								
								
									
										16
									
								
								src/SearchRequest/Attribute/SearchLike.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/SearchRequest/Attribute/SearchLike.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\SearchRequest\Attribute; | ||||
| 
 | ||||
| use Ulmus\SearchRequest\SearchMethodEnum; | ||||
| 
 | ||||
| #[\Attribute(\Attribute::TARGET_PROPERTY)]
 | ||||
| class SearchLike extends SearchParameter | ||||
| { | ||||
|     public function __construct( | ||||
|         public ? string $parameter = null, | ||||
|         public ? string $field = null, | ||||
|         public bool $toggle = false, | ||||
|         public SearchMethodEnum $method = SearchMethodEnum::Like, | ||||
|     ) {} | ||||
| } | ||||
							
								
								
									
										16
									
								
								src/SearchRequest/Attribute/SearchOrderBy.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/SearchRequest/Attribute/SearchOrderBy.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\SearchRequest\Attribute; | ||||
| 
 | ||||
| use Ulmus\SearchRequest\SearchMethodEnum; | ||||
| 
 | ||||
| #[\Attribute(\Attribute::TARGET_PROPERTY)]
 | ||||
| class SearchOrderBy extends SearchParameter | ||||
| { | ||||
|     public function __construct( | ||||
|         public ? string $parameter = null, | ||||
|         public ? string $field = null, | ||||
|         public bool $toggle = false, | ||||
|         public SearchMethodEnum $method = SearchMethodEnum::OrderByAsc, | ||||
|     ) {} | ||||
| } | ||||
							
								
								
									
										16
									
								
								src/SearchRequest/Attribute/SearchParameter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/SearchRequest/Attribute/SearchParameter.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\SearchRequest\Attribute; | ||||
| 
 | ||||
| use Ulmus\SearchRequest\SearchMethodEnum; | ||||
| 
 | ||||
| #[\Attribute(\Attribute::TARGET_PROPERTY)]
 | ||||
| class SearchParameter | ||||
| { | ||||
|     public function __construct( | ||||
|         public ? string $parameter = null, | ||||
|         public ? string $field = null, | ||||
|         public bool $toggle = false, | ||||
|         public SearchMethodEnum $method = SearchMethodEnum::Manual, | ||||
|     ) {} | ||||
| } | ||||
							
								
								
									
										13
									
								
								src/SearchRequest/Attribute/SearchRequestParameter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/SearchRequest/Attribute/SearchRequestParameter.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\SearchRequest\Attribute; | ||||
| 
 | ||||
| use Ulmus\SearchRequest\SearchMethodEnum; | ||||
| 
 | ||||
| #[\Attribute(\Attribute::TARGET_CLASS)]
 | ||||
| class SearchRequestParameter | ||||
| { | ||||
|     public function __construct( | ||||
|         public string $class, | ||||
|     ) {} | ||||
| } | ||||
							
								
								
									
										16
									
								
								src/SearchRequest/Attribute/SearchWhere.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/SearchRequest/Attribute/SearchWhere.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\SearchRequest\Attribute; | ||||
| 
 | ||||
| use Ulmus\SearchRequest\SearchMethodEnum; | ||||
| 
 | ||||
| #[\Attribute(\Attribute::TARGET_PROPERTY)]
 | ||||
| class SearchWhere extends SearchParameter | ||||
| { | ||||
|     public function __construct( | ||||
|         public ? string $parameter = null, | ||||
|         public ? string $field = null, | ||||
|         public bool $toggle = false, | ||||
|         public SearchMethodEnum $method = SearchMethodEnum::Where, | ||||
|     ) {} | ||||
| } | ||||
							
								
								
									
										15
									
								
								src/SearchRequest/SearchMethodEnum.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/SearchRequest/SearchMethodEnum.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\SearchRequest; | ||||
| 
 | ||||
| enum SearchMethodEnum | ||||
| { | ||||
|     case Manual; | ||||
|     case Where; | ||||
|     case Like; | ||||
|     case LikeLeft; | ||||
|     case LikeRight; | ||||
|     case OrderByAsc; | ||||
|     case OrderByDesc; | ||||
|     case GroupBy; | ||||
| } | ||||
							
								
								
									
										41
									
								
								src/SearchRequest/SearchRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/SearchRequest/SearchRequest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\SearchRequest; | ||||
| 
 | ||||
| use Psr\Http\Message\ServerRequestInterface; | ||||
| use Ulmus\Repository; | ||||
| 
 | ||||
| class SearchRequest implements SearchRequestInterface | ||||
| { | ||||
|     use SearchRequestPaginationTrait, SearchRequestFromRequestTrait; | ||||
| 
 | ||||
|     public function filter(Repository $repository) : Repository | ||||
|     { | ||||
|         return $repository; | ||||
|     } | ||||
| 
 | ||||
|     public function wheres() : iterable | ||||
|     { | ||||
|         return array_filter($this->wheres + [ | ||||
|         ], fn($i) => ! is_null($i) ) + [ ]; | ||||
|     } | ||||
| 
 | ||||
|     public function likes(): iterable | ||||
|     { | ||||
|         return array_filter($this->likes + [ | ||||
|         ], fn($i) => ! is_null($i) ) + []; | ||||
|     } | ||||
| 
 | ||||
|     public function groups(): iterable | ||||
|     { | ||||
|         return array_filter($this->groups + [ | ||||
|         ], fn($e) => ! is_null($e)  && $e !== "" ); | ||||
|     } | ||||
| 
 | ||||
|     public function orders(): iterable | ||||
|     { | ||||
|         return array_filter($this->orders + [ | ||||
|         ], fn($e) => ! is_null($e)  && $e !== "" ); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										83
									
								
								src/SearchRequest/SearchRequestFromRequestTrait.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/SearchRequest/SearchRequestFromRequestTrait.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\SearchRequest; | ||||
| 
 | ||||
| use Psr\Http\Message\ServerRequestInterface; | ||||
| use Ulmus\SearchRequest\Attribute\SearchParameter; | ||||
| use Ulmus\SearchRequest\Attribute\SearchWhere; | ||||
| 
 | ||||
| trait SearchRequestFromRequestTrait | ||||
| { | ||||
|     protected array $wheres = []; | ||||
| 
 | ||||
|     protected array $likes = []; | ||||
| 
 | ||||
|     protected array $groups = []; | ||||
| 
 | ||||
|     protected array $orders = []; | ||||
| 
 | ||||
|     public function fromRequest(ServerRequestInterface $request) | ||||
|     { | ||||
|         $get = new \ArrayObject(array_filter($request->getQueryParams(), function($i) { return $i !== ""; })); | ||||
| 
 | ||||
|         $this->page = $get->offsetExists('page') ? $get['page'] : 1; | ||||
| 
 | ||||
|         $classReflection = new \ReflectionClass($this); | ||||
| 
 | ||||
|         foreach($classReflection->getProperties() as $property) { | ||||
| 
 | ||||
|             foreach($property->getAttributes() as $attributeReflection) { | ||||
|                 $attribute = $attributeReflection->newInstance(); | ||||
| 
 | ||||
|                 # We can't simply pass this class as first arguments of getAttributes() since it do not check for inheritance
 | ||||
|                 if (! $attribute instanceof Attribute\SearchParameter) { | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 $propertyName =  $property->getName(); | ||||
|                 $fieldName =  $attribute->field ?? $property->getName(); | ||||
|                 $queryParamName = $attribute->parameter ?? $property->getName(); | ||||
| 
 | ||||
|                 if ($attribute->toggle) { | ||||
|                     $this->$propertyName = $get->offsetExists('rental'); | ||||
|                 } else { | ||||
|                     $this->$propertyName = $get->offsetExists($queryParamName) ? $get[$queryParamName] : null; | ||||
|                 } | ||||
| 
 | ||||
|                 $field = (string) $fieldName; | ||||
| 
 | ||||
|                 switch ($attribute->method) { | ||||
|                     case SearchMethodEnum::Where: | ||||
|                         $this->wheres[$field] = $this->$propertyName; | ||||
|                         break; | ||||
| 
 | ||||
|                     case SearchMethodEnum::Like: | ||||
|                         $this->likes[$field] = "%{$this->$propertyName}%"; | ||||
|                         break; | ||||
| 
 | ||||
|                     case SearchMethodEnum::LikeLeft: | ||||
|                         $this->likes[$field] = "%{$this->$propertyName}"; | ||||
|                         break; | ||||
| 
 | ||||
|                     case SearchMethodEnum::LikeRight: | ||||
|                         $this->likes[$field] = "{$this->$propertyName}%"; | ||||
|                         break; | ||||
| 
 | ||||
|                     case SearchMethodEnum::OrderByAsc: | ||||
|                         $this->orders[$field] = "ASC"; | ||||
|                         break; | ||||
| 
 | ||||
|                     case SearchMethodEnum::OrderByDesc: | ||||
|                         $this->orders[$field] = "DESC"; | ||||
|                         break; | ||||
| 
 | ||||
|                     case SearchMethodEnum::GroupBy: | ||||
|                         $this->groups[$field] = "DESC"; | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user