diff --git a/composer.json b/composer.json index 258b0d7..7b6cdd2 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "phpstan/phpstan": "^0.12.0@dev", "phpunit/phpunit": "8.*", "sebastian/phpcpd": "^4.1", - "triagens/arangodb": "3.5.*" + "triagens/arangodb": "^3.5" }, "autoload": { "psr-4": { diff --git a/src/API/hasGraphClauses.php b/src/API/hasGraphClauses.php index b319d34..aa7a08e 100644 --- a/src/API/hasGraphClauses.php +++ b/src/API/hasGraphClauses.php @@ -29,7 +29,7 @@ public function with() : QueryBuilder $collections = func_get_args(); foreach ($collections as $key => $collection) { $this->registerCollections($collection, 'read'); - $collections[$key] = $this->normalizeArgument($collection, 'collection'); + $collections[$key] = $this->normalizeArgument($collection, 'Collection'); } $this->addCommand(new WithClause($collections)); @@ -50,11 +50,11 @@ public function with() : QueryBuilder */ public function traverse($fromVertex, $inDirection = 'outbound', $toVertex = null, $kShortestPaths = false) : QueryBuilder { - $fromVertex = $this->normalizeArgument($fromVertex, 'id'); - $inDirection = $this->normalizeArgument($inDirection, 'direction'); + $fromVertex = $this->normalizeArgument($fromVertex, 'Id'); + $inDirection = $this->normalizeArgument($inDirection, 'Direction'); if ($toVertex !== null) { - $toVertex = $this->normalizeArgument($toVertex, 'id'); + $toVertex = $this->normalizeArgument($toVertex, 'Id'); } @@ -105,7 +105,7 @@ public function kShortestPaths($fromVertex, $inDirection, $toVertex) : QueryBuil */ public function graph(string $graphName) : QueryBuilder { - $graphName = $this->normalizeArgument($graphName, 'graph'); + $graphName = $this->normalizeArgument($graphName, 'Graph'); $this->addCommand(new GraphClause($graphName)); diff --git a/src/API/hasMiscellaneousFunctions.php b/src/API/hasMiscellaneousFunctions.php index ba4a601..3955e96 100644 --- a/src/API/hasMiscellaneousFunctions.php +++ b/src/API/hasMiscellaneousFunctions.php @@ -2,9 +2,7 @@ namespace LaravelFreelancerNL\FluentAQL\API; use LaravelFreelancerNL\FluentAQL\Expressions\FunctionExpression; -use LaravelFreelancerNL\FluentAQL\Expressions\KeyExpression; use LaravelFreelancerNL\FluentAQL\Expressions\ListExpression; -use LaravelFreelancerNL\FluentAQL\Expressions\LiteralExpression; /** * Trait hasFunctions @@ -21,7 +19,7 @@ trait hasMiscellaneousFunctions * @link https://www.arangodb.com/docs/stable/aql/functions-miscellaneous.html#document * * @param $collection - * @param null|string|array|KeyExpression|ListExpression $id + * @param null|string|array|ListExpression $id * @return FunctionExpression */ public function document($collection, $id = null) @@ -34,9 +32,9 @@ public function document($collection, $id = null) } if ($collection !== null) { - $arguments['collection'] = $this->normalizeArgument($collection, ['collection', 'id']); + $arguments['collection'] = $this->normalizeArgument($collection, ['Collection', 'Id']); } - $arguments['id'] = $this->normalizeArgument($id, ['query', 'list', 'key', 'id']); + $arguments['id'] = $this->normalizeArgument($id, ['Id', 'Key', 'Query', 'List']); return new FunctionExpression('DOCUMENT', $arguments); } diff --git a/src/API/hasQueryClauses.php b/src/API/hasQueryClauses.php index 360b4ba..123c556 100644 --- a/src/API/hasQueryClauses.php +++ b/src/API/hasQueryClauses.php @@ -5,7 +5,6 @@ use LaravelFreelancerNL\FluentAQL\Clauses\CollectClause; use LaravelFreelancerNL\FluentAQL\Clauses\FilterClause; use LaravelFreelancerNL\FluentAQL\Clauses\GroupClause; -use LaravelFreelancerNL\FluentAQL\Clauses\InClause; use LaravelFreelancerNL\FluentAQL\Clauses\KeepClause; use LaravelFreelancerNL\FluentAQL\Clauses\LimitClause; use LaravelFreelancerNL\FluentAQL\Clauses\OptionsClause; @@ -14,9 +13,7 @@ use LaravelFreelancerNL\FluentAQL\Clauses\ReturnClause; use LaravelFreelancerNL\FluentAQL\Clauses\SearchClause; use LaravelFreelancerNL\FluentAQL\Clauses\SortClause; -use LaravelFreelancerNL\FluentAQL\Clauses\WithClause; use LaravelFreelancerNL\FluentAQL\Clauses\WithCountClause; -use LaravelFreelancerNL\FluentAQL\Expressions\NullExpression; use LaravelFreelancerNL\FluentAQL\QueryBuilder; /** @@ -33,12 +30,23 @@ trait hasQueryClauses * You HAVE TO prepare user input yourself or be open to injection attacks. * * @param string $aql - * @param null $bindings - * @param null $collections - * @return $this + * @param null $binds + * @param array|null $collections + * @return QueryBuilder */ - public function raw(string $aql, $bindings = [], $collections = []) : QueryBuilder + public function raw(string $aql, $binds = null, $collections = null) : QueryBuilder { + if (is_array($binds)) { + foreach ($binds as $key => $value) { + $this->bind($value, $key); + } + } + if (is_array($binds)) { + foreach ($collections as $mode => $modeCollections) { + $this->registerCollections($modeCollections, $mode); + } + } + $this->addCommand(new RawClause($aql)); return $this; @@ -46,7 +54,7 @@ public function raw(string $aql, $bindings = [], $collections = []) : QueryBuild public function options($options) : QueryBuilder { - $options = $this->normalizeArgument($options, 'document'); + $options = $this->normalizeArgument($options, 'Object'); $this->addCommand(new OptionsClause($options)); @@ -56,7 +64,7 @@ public function options($options) : QueryBuilder /** * Create a for clause - * @link https://www.arangodb.com/docs/3.4/aql/operations-for.html + * @link https://www.arangodb.com/docs/stable/aql/operations-for.html * * @param string|array $variableName * @param mixed $in @@ -69,11 +77,12 @@ public function for($variableName, $in = null) : QueryBuilder } foreach ($variableName as $key => $value) { - $variableName[$key] = $this->normalizeArgument($value, 'variable'); + $variableName[$key] = $this->normalizeArgument($value, 'Variable'); + $this->registerVariable($variableName[$key]); } if ($in !== null) { - $in = $this->normalizeArgument($in, ['collection', 'range', 'list', 'query']); + $in = $this->normalizeArgument($in, ['Collection', 'Range', 'List', 'Query']); } $this->addCommand(new ForClause($variableName, $in)); @@ -142,10 +151,10 @@ public function search($attribute, $comparisonOperator = '==', $value = null, $l public function collect($variableName = null, $expression = null) : QueryBuilder { if (isset($variableName)) { - $variableName = $this->normalizeArgument($variableName, 'variable'); + $variableName = $this->normalizeArgument($variableName, 'Variable'); } if (isset($expression)) { - $expression = $this->normalizeArgument($expression, ['attribute', 'function', 'query', 'bind']); + $expression = $this->normalizeArgument($expression, ['VariableAttribute', 'Function', 'Query', 'Bind']); } $this->addCommand(new CollectClause($variableName, $expression)); @@ -164,9 +173,11 @@ public function collect($variableName = null, $expression = null) : QueryBuilder */ public function group($groupsVariable, $projectionExpression = null) : QueryBuilder { - $groupsVariable = $this->normalizeArgument($groupsVariable, 'variable'); + $groupsVariable = $this->normalizeArgument($groupsVariable, 'Variable'); + $this->registerVariable($groupsVariable); + if (isset($projectionExpression)) { - $projectionExpression = $this->normalizeArgument($projectionExpression, ['attribute', 'document', 'function', 'query', 'bind']); + $projectionExpression = $this->normalizeArgument($projectionExpression, ['VariableAttribute', 'Object', 'Function', 'Query', 'Bind']); } $this->addCommand(new GroupClause($groupsVariable, $projectionExpression)); @@ -184,7 +195,8 @@ public function group($groupsVariable, $projectionExpression = null) : QueryBuil */ public function keep($keepVariable) : QueryBuilder { - $keepVariable = $this->normalizeArgument($keepVariable, 'variable'); + $keepVariable = $this->normalizeArgument($keepVariable, 'Variable'); + $this->registerVariable($keepVariable); $this->addCommand(new KeepClause($keepVariable)); @@ -203,7 +215,8 @@ public function keep($keepVariable) : QueryBuilder */ public function withCount($countVariableName) : QueryBuilder { - $countVariableName = $this->normalizeArgument($countVariableName, 'variable'); + $countVariableName = $this->normalizeArgument($countVariableName, 'Variable'); + $this->registerVariable($countVariableName); $this->addCommand(new WithCountClause($countVariableName)); @@ -221,8 +234,10 @@ public function withCount($countVariableName) : QueryBuilder */ public function aggregate($variableName, $aggregateExpression) : QueryBuilder { - $variableName = $this->normalizeArgument($variableName, 'variable'); - $aggregateExpression = $this->normalizeArgument($aggregateExpression, 'attribute', 'function', 'query', 'bind'); + $variableName = $this->normalizeArgument($variableName, 'Variable'); + $this->registerVariable($variableName); + + $aggregateExpression = $this->normalizeArgument($aggregateExpression, ['VariableAttribute', 'Function', 'Query', 'Bind']); $this->addCommand(new AggregateClause($variableName, $aggregateExpression)); @@ -261,15 +276,14 @@ public function sort($sortBy = null, $direction = null) : QueryBuilder * Limit results * @link https://www.arangodb.com/docs/stable/aql/operations-limit.html * - * @param $offsetOrCount - * @param null $count + * @param int $offsetOrCount + * @param int $count * @return $this */ - public function limit($offsetOrCount, $count = null) + public function limit(int $offsetOrCount, int $count = null) { - $offsetOrCount = $this->normalizeArgument($offsetOrCount, 'numeric'); if ($count !== null) { - $count = $this->normalizeArgument($count, 'numeric'); + $count = (int) $count; } $this->addCommand(new LimitClause($offsetOrCount, $count)); @@ -287,6 +301,8 @@ public function limit($offsetOrCount, $count = null) */ public function return($expression, $distinct = false) : QueryBuilder { + $expression = $this->normalizeArgument($expression, ['Variable', 'VariableAttribute', 'Object', 'Function', 'Query', 'Bind']); + $this->addCommand(new ReturnClause($expression, $distinct)); return $this; diff --git a/src/API/hasStatementClauses.php b/src/API/hasStatementClauses.php index 911cc26..a6bf2c6 100644 --- a/src/API/hasStatementClauses.php +++ b/src/API/hasStatementClauses.php @@ -19,7 +19,7 @@ trait hasStatementClauses { /** * Assign a value to a variable. - * @link https://www.arangodb.com/docs/3.4/aql/operations-let.html + * @link https://www.arangodb.com/docs/stable/aql/operations-let.html * * @param $variableName * @param $expression @@ -27,8 +27,10 @@ trait hasStatementClauses */ public function let($variableName, $expression) { - $variableName = $this->normalizeArgument($variableName, 'variable'); - $expression = $this->normalizeArgument($expression, ['query', 'list', 'range', 'numeric', 'bind']); + $variableName = $this->normalizeArgument($variableName, 'Variable'); + $this->registerVariable($variableName); + + $expression = $this->normalizeArgument($expression, ['List', 'Object', 'Query', 'Range', 'Number', 'Bind']); $this->addCommand(new LetClause($variableName, $expression)); @@ -37,7 +39,7 @@ public function let($variableName, $expression) /** * Insert a document in a collection - * @link https://www.arangodb.com/docs/3.4/aql/operations-insert.html + * @link https://www.arangodb.com/docs/stable/aql/operations-insert.html * * @param $document * @param string $collection @@ -45,10 +47,10 @@ public function let($variableName, $expression) */ public function insert($document, string $collection) : QueryBuilder { - $document = $this->normalizeArgument($document, ['key', 'variable', 'bind']); + $document = $this->normalizeArgument($document, ['Key', 'Variable', 'Bind']); $this->registerCollections($collection); - $collection = $this->normalizeArgument($collection, ['collection', 'bind']); + $collection = $this->normalizeArgument($collection, ['Collection', 'Bind']); $this->addCommand(new InsertClause($document, $collection)); @@ -57,7 +59,7 @@ public function insert($document, string $collection) : QueryBuilder /** * Update a document in a collection with the supplied data - * @link https://www.arangodb.com/docs/3.4/aql/operations-update.html + * @link https://www.arangodb.com/docs/stable/aql/operations-update.html * * @param $document * @param $with @@ -66,10 +68,10 @@ public function insert($document, string $collection) : QueryBuilder */ public function update($document, $with, $collection) : QueryBuilder { - $document = $this->normalizeArgument($document, ['key', 'variable', 'bind']); - $with = $this->normalizeArgument($with, ['numeric', 'bind']); + $document = $this->normalizeArgument($document, ['Key', 'Variable', 'Bind']); + $with = $this->normalizeArgument($with, ['Number', 'Bind']); $this->registerCollections($collection); - $collection = $this->normalizeArgument($collection, ['collection', 'bind']); + $collection = $this->normalizeArgument($collection, ['Collection', 'Bind']); $this->addCommand(new UpdateClause($document, $with, $collection)); @@ -78,7 +80,7 @@ public function update($document, $with, $collection) : QueryBuilder /** * Replace a document in a collection with the supplied data - * @link https://www.arangodb.com/docs/3.4/aql/operations-replace.html + * @link https://www.arangodb.com/docs/stable/aql/operations-replace.html * * @param $document * @param $with @@ -87,10 +89,10 @@ public function update($document, $with, $collection) : QueryBuilder */ public function replace($document, $with, string $collection) : QueryBuilder { - $document = $this->normalizeArgument($document, ['key', 'variable', 'bind']); - $with = $this->normalizeArgument($with, ['numeric', 'bind']); + $document = $this->normalizeArgument($document, ['Key', 'Variable', 'Bind']); + $with = $this->normalizeArgument($with, ['Number', 'Bind']); $this->registerCollections($collection); - $collection = $this->normalizeArgument($collection, ['collection', 'bind']); + $collection = $this->normalizeArgument($collection, ['Collection', 'Bind']); $this->addCommand(new ReplaceClause($document, $with, $collection)); @@ -99,7 +101,7 @@ public function replace($document, $with, string $collection) : QueryBuilder /** * Update, replace or insert documents in a collection with the supplied data. - * @link https://www.arangodb.com/docs/3.4/aql/operations-upsert.html + * @link https://www.arangodb.com/docs/stable/aql/operations-upsert.html * * @param mixed $search * @param mixed $insert @@ -110,11 +112,11 @@ public function replace($document, $with, string $collection) : QueryBuilder */ public function upsert($search, $insert, $with, string $collection, bool $replace = false) : QueryBuilder { - $search = $this->normalizeArgument($search, ['key', 'variable', 'bind']); - $insert = $this->normalizeArgument($insert, ['key', 'variable', 'bind']); - $with = $this->normalizeArgument($with, ['numeric', 'bind']); + $search = $this->normalizeArgument($search, ['Key', 'Variable', 'Bind']); + $insert = $this->normalizeArgument($insert, ['Key', 'Variable', 'Bind']); + $with = $this->normalizeArgument($with, ['Number', 'Bind']); + $collection = $this->normalizeArgument($collection, ['Collection', 'Bind']); $this->registerCollections($collection); - $collection = $this->normalizeArgument($collection, ['collection', 'bind']); $this->addCommand(new UpsertClause($search, $insert, $with, $collection, $replace)); @@ -123,7 +125,7 @@ public function upsert($search, $insert, $with, string $collection, bool $replac /** * Remove a document from a collection - * @link https://www.arangodb.com/docs/3.4/aql/operations-remove.html + * @link https://www.arangodb.com/docs/stable/aql/operations-remove.html * * @param mixed $document * @param string $collection @@ -131,9 +133,9 @@ public function upsert($search, $insert, $with, string $collection, bool $replac */ public function remove($document, string $collection) : QueryBuilder { - $document = $this->normalizeArgument($document, ['key', 'variable', 'bind']); + $document = $this->normalizeArgument($document, ['Key', 'Variable', 'Bind']); $this->registerCollections($collection); - $collection = $this->normalizeArgument($collection, ['collection', 'bind']); + $collection = $this->normalizeArgument($collection, ['Collection', 'Bind']); $this->addCommand(new RemoveClause($document, $collection)); diff --git a/src/Clauses/AggregateClause.php b/src/Clauses/AggregateClause.php index a81c7ba..1632a8e 100644 --- a/src/Clauses/AggregateClause.php +++ b/src/Clauses/AggregateClause.php @@ -1,9 +1,6 @@ count = $offsetOrCount; diff --git a/src/Clauses/RawClause.php b/src/Clauses/RawClause.php index 720268e..ad646f1 100644 --- a/src/Clauses/RawClause.php +++ b/src/Clauses/RawClause.php @@ -1,8 +1,6 @@ direction = $direction; $this->startVertex = $startVertex; diff --git a/src/Expressions/BooleanExpression.php b/src/Expressions/BooleanExpression.php index 8afa68d..a799ad2 100644 --- a/src/Expressions/BooleanExpression.php +++ b/src/Expressions/BooleanExpression.php @@ -4,7 +4,7 @@ /** * AQL literal expression */ -class BooleanExpression extends Expression implements ExpressionInterface +class BooleanExpression extends LiteralExpression implements ExpressionInterface { /** * Compile expression output diff --git a/src/Expressions/CollectionExpression.php b/src/Expressions/CollectionExpression.php deleted file mode 100644 index 27675c9..0000000 --- a/src/Expressions/CollectionExpression.php +++ /dev/null @@ -1,9 +0,0 @@ -expression, JSON_UNESCAPED_SLASHES); - } -} diff --git a/src/Expressions/GraphExpression.php b/src/Expressions/GraphExpression.php deleted file mode 100644 index 75bb49f..0000000 --- a/src/Expressions/GraphExpression.php +++ /dev/null @@ -1,9 +0,0 @@ -expression = $expression; + } + public function compile() { - return json_encode($this->expression, JSON_UNESCAPED_SLASHES); + return '['.implode(',', $this->expression).']'; } } diff --git a/src/Expressions/NullExpression.php b/src/Expressions/NullExpression.php index 18b79d9..b0fc8fa 100644 --- a/src/Expressions/NullExpression.php +++ b/src/Expressions/NullExpression.php @@ -4,7 +4,7 @@ /** * AQL literal expression */ -class NullExpression extends Expression implements ExpressionInterface +class NullExpression extends LiteralExpression implements ExpressionInterface { /** * Create an expression diff --git a/src/Expressions/NumericExpression.php b/src/Expressions/NumericExpression.php deleted file mode 100644 index 2010a4c..0000000 --- a/src/Expressions/NumericExpression.php +++ /dev/null @@ -1,9 +0,0 @@ -expression as $key => $value) { + if ($output != '') { + $output .= ","; + } + $output .= '"'.$key.'":'.$value; + } + return "{".$output."}"; + } +} diff --git a/src/Expressions/RangeExpression.php b/src/Expressions/RangeExpression.php deleted file mode 100644 index 506a03a..0000000 --- a/src/Expressions/RangeExpression.php +++ /dev/null @@ -1,9 +0,0 @@ -expression.'"'; + return json_encode($this->expression, JSON_UNESCAPED_SLASHES); } } diff --git a/src/Grammar.php b/src/Grammar.php index a8fd745..afdc49b 100644 --- a/src/Grammar.php +++ b/src/Grammar.php @@ -5,8 +5,6 @@ * Provides AQL syntax functions */ -use Exception; -use LaravelFreelancerNL\FluentAQL\Exceptions\BindException; use LaravelFreelancerNL\FluentAQL\Expressions\FunctionExpression; class Grammar @@ -17,18 +15,55 @@ class Grammar * @var array */ protected $comparisonOperators = [ - '==', '!=', '<', '>', '<=', '>=', 'IN', 'NOT IN', 'LIKE', '~', '!~', - 'ALL ==', 'ALL !=', 'ALL <', 'ALL >', 'ALL <=', 'ALL >=', 'ALL IN', - 'ANY ==', 'ANY !=', 'ANY <', 'ANY >', 'ANY <=', 'ANY >=', 'ANY IN', - 'NONE ==', 'NONE !=', 'NONE <', 'NONE >', 'NONE <=', 'NONE >=', 'NONE IN' + '==' => 1, + '!=' => 1, + '<' => 1, + '>' => 1, + '<=' => 1, + '>=' => 1, + 'IN' => 1, + 'NOT IN' => 1, + 'LIKE' => 1, + '~' => 1, + '!~' => 1, + 'ALL ==' => 1, + 'ALL !=' => 1, + 'ALL <' => 1, + 'ALL >' => 1, + 'ALL <=' => 1, + 'ALL >=' => 1, + 'ALL IN' => 1, + 'ANY ==' => 1, + 'ANY !=' => 1, + 'ANY <' => 1, + 'ANY >' => 1, + 'ANY <=' => 1, + 'ANY >=' => 1, + 'ANY IN' => 1, + 'NONE ==' => 1, + 'NONE !=' => 1, + 'NONE <' => 1, + 'NONE >' => 1, + 'NONE <=' => 1, + 'NONE >=' => 1, + 'NONE IN' => 1 ]; protected $arithmeticOperators = [ - '+', '-', '*', '/', '%' + '+' => 1, + '-' => 1, + '*' => 1, + '/' => 1, + '%' => 1 ]; protected $logicalOperators = [ - 'AND', '&&', 'OR', '||', 'NOT', '!' + 'AND' => 1, + '&&' => 1, + 'OR' => 1, + '||' => 1, + 'NOT' => 1, + '!' => 1 ]; protected $rangeOperator = '..'; @@ -41,50 +76,64 @@ class Grammar 'FALSE', 'TRUE', 'NULL', ]; + /* + * List of recognizable data and the accompanying Expression type it will be mapped too. + * Strings of an unrecognized nature are always bound. + */ + protected $argumentTypeExpressionMap = [ + 'AssociativeArray' => 'Object', + 'Attribute' => 'Literal', + 'Bind' => 'Bind', + 'Boolean' => 'Boolean', + 'Collection' => 'Literal', + 'Constant' => 'Constant', + 'Direction' => 'Constant', + 'Document' => 'Object', + 'Function' => 'Function', + 'Graph' => 'String', + 'Id' => 'String', + 'IndexedArray' => 'List', + 'Key' => 'String', + 'List' => 'List', + 'Name' => 'String', + 'Number' => 'Literal', + 'Null' => 'Literal', + 'Variable' => 'Literal', + 'VariableAttribute' => 'Literal', + 'Object' => 'Object', + 'Range' => 'Literal', + 'String' => 'Bind', + ]; - public function wrap($value) + /* + * List of default allowed Data Types + * The order matters in the compilation of the data + * String should always go last to trap any remaining unrecognized data in a bind. + */ + protected $defaultAllowedExpressionTypes = [ + 'Number' => 'Number', + 'Boolean' => 'Boolean', + 'VariableAttribute' => 'VariableAttribute', + 'Id' => 'Id', + 'Key' => 'Key', + 'Bind' => 'Bind' + ]; + + public function wrap($value) : string { return '`'.addcslashes($value, '`').'`'; } - - /** - * @param $data - * @return array|false|string - * @throws Exception - */ - public function prepareDataToBind($data) + public function mapArgumentTypeToExpressionType($argumentType) : string { - $bind = false; - - if (is_scalar($data)) { - $bind = true; - } - if ($data instanceof \DateTime) { - $data = $data->format(\DateTime::ATOM); - $bind = true; - } - if (is_iterable($data)) { - $data = array_map([$this, 'prepareDataToBind'], $data); - $bind = true; - } - if (is_object($data)) { - $data = json_encode($data, JSON_UNESCAPED_SLASHES); - $bind = true; - } - - if ($bind) { - return $data; - } - - throw new BindException("'Data type is not allowed for a binding (scalar, DateTime, object or array): ".var_export($data, true)); + return $this->argumentTypeExpressionMap[$argumentType]; } /** * @param $value * @return bool */ - public function is_bind($value) + public function isBind($value) { if (is_string($value)) { return true; @@ -99,7 +148,7 @@ public function is_bind($value) * @param $value * @return bool */ - public function is_range($value) + public function isRange($value) : bool { if (is_string($value) && preg_match('/^[0-9]+(?:\.[0-9]+)?+\.{2}[0-9]+(?:\.[0-9]+)?$/', $value)) { return true; @@ -111,58 +160,75 @@ public function is_range($value) * @param $value * @return bool */ - public function is_numeric($value) + public function isBoolean($value) : bool { - return is_numeric($value); + return (is_bool($value) || $value === 'true' || $value === 'false'); } - + + /** * @param $value * @return bool */ - public function is_list($value) + public function isNull($value) : bool { - return is_array($value) && $this->arrayIsNumeric($value); + return ($value === null || $value == 'null'); } /** * @param $value * @return bool */ - public function is_query($value) + public function isNumber($value) : bool + { + return is_numeric($value); + } + + /** + * @param $value + * @return bool + */ + public function isList($value) : bool + { + return is_array($value) && $this->isIndexedArray($value); + } + + public function isQuery($value) : bool { return $value instanceof QueryBuilder; } - public function is_logicalOperator($operator) + public function isFunction($value) : bool { - return in_array($operator, $this->logicalOperators); + return $value instanceof FunctionExpression; } - public function is_comparisonOperator($operator) + public function isLogicalOperator($operator) : bool { - return in_array($operator, $this->comparisonOperators); + return isset($this->logicalOperators[$operator]); } - public function is_sortDirection($value) + public function isComparisonOperator($operator) : bool { - if (preg_match('/asc|desc/i', $value)) { - return true; - } - return false; + return isset($this->comparisonOperators[$operator]); } - public function is_direction($value) + public function isArithmeticOperators($operator) : bool { - if (preg_match('/outbound|inbound|any/i', $value)) { + return isset($this->arithmeticOperators[$operator]); + } + + public function isSortDirection($value) : bool + { + if (preg_match('/asc|desc/i', $value)) { return true; } return false; } - public function is_function($value) : bool + public function isDirection($value) : bool { - if ($value instanceof FunctionExpression) { + if (preg_match('/outbound|inbound|any/i', $value)) { return true; } return false; @@ -172,7 +238,7 @@ public function is_function($value) : bool * @param $value * @return bool */ - public function is_collection($value) + public function isCollection($value) : bool { if (is_string($value) && preg_match('/^[a-zA-Z0-9_-]+$/', $value)) { return true; @@ -180,12 +246,12 @@ public function is_collection($value) return false; } - public function is_graph($value) + public function isGraph($value) : bool { - return $this->is_collection($value); + return $this->isCollection($value); } - public function is_key($value) + public function isKey($value) : bool { if (is_string($value) && preg_match("/^[a-zA-Z0-9_-]+\/?[a-zA-Z0-9_\-\:\.\@\(\)\+\,\=\;\$\!\*\'\%]+$/", $value)) { return true; @@ -193,7 +259,7 @@ public function is_key($value) return false; } - public function is_id($value) + public function isId($value) : bool { if (is_string($value) && preg_match("/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_-]+\/?[a-zA-Z0-9_\-\:\.\@\(\)\+\,\=\;\$\!\*\'\%]+$/", $value)) { return true; @@ -205,7 +271,7 @@ public function is_id($value) * @param $value * @return bool */ - public function is_variable($value) + public function isVariable($value) { if (is_string($value) && preg_match('/^\$?[a-zA-Z_][a-zA-Z0-9_]*+$/', $value)) { return true; @@ -217,7 +283,7 @@ public function is_variable($value) * @param $value * @return bool */ - public function is_attribute($value) + public function isAttribute($value) : bool { if (is_string($value) && preg_match('/^(@?[\d\w_]+|`@?[\d\w_]+`)(\[\`.+\`\]|\[[\d\w\*]*\])*(\.(\`.+\`|@?[\d\w]*)(\[\`.+\`\]|\[[\d\w\*]*\])*)*$/', $value)) { return true; @@ -225,19 +291,41 @@ public function is_attribute($value) return false; } + + /** + * @param mixed $value + * @param array $registeredVariables + * @return bool + */ + public function isVariableAttribute($value, $registeredVariables = []) : bool + { + if (empty($registeredVariables)) { + return false; + } + $variables = implode('|', $registeredVariables); + if ( + is_string($value) + && preg_match('/^('.$variables.')(\[\`.+\`\]|\[[\d\w\*]*\])*(\.(\`.+\`|@?[\d\w]*)(\[\`.+\`\]|\[[\d\w\*]*\])*)*$/', $value) + ) { + return true; + } + return false; + } + + /** * @param $value * @return bool */ - public function is_document($value) + public function isObject($value) : bool { - if (is_object($value) || (is_array($value) && $this->arrayIsAssociative($value))) { + if (is_object($value) || (is_array($value) && $this->isAssociativeArray($value))) { return true; } return false; } - public function validateBindParameterSyntax($bindParameter) + public function validateBindParameterSyntax($bindParameter) : bool { if (preg_match('/^@?[a-zA-Z0-9][a-zA-Z0-9_]*$/', $bindParameter)) { return true; @@ -251,7 +339,7 @@ public function validateBindParameterSyntax($bindParameter) * @param array $array * @return bool */ - public function arrayIsAssociative(array $array) + public function isAssociativeArray(array $array) { if (empty($array)) { return true; @@ -265,7 +353,7 @@ public function arrayIsAssociative(array $array) * @param array $array * @return bool */ - public function arrayIsNumeric(array $array) + public function isIndexedArray(array $array) { if (empty($array)) { return true; @@ -285,4 +373,9 @@ public function formatBind(string $bindVariableName, bool $collection = null) } return $prefix.$bindVariableName; } + + public function getAllowedExpressionTypes() + { + return $this->defaultAllowedExpressionTypes; + } } diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 4412da6..7cd7da5 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -9,7 +9,12 @@ use LaravelFreelancerNL\FluentAQL\Exceptions\BindException; use LaravelFreelancerNL\FluentAQL\Exceptions\ExpressionTypeException; use LaravelFreelancerNL\FluentAQL\Expressions\BindExpression; +use LaravelFreelancerNL\FluentAQL\Expressions\ExpressionInterface; +use LaravelFreelancerNL\FluentAQL\Expressions\ListExpression; +use LaravelFreelancerNL\FluentAQL\Expressions\NullExpression; +use LaravelFreelancerNL\FluentAQL\Expressions\ObjectExpression; use LaravelFreelancerNL\FluentAQL\Expressions\PredicateExpression; +use LaravelFreelancerNL\FluentAQL\Expressions\StringExpression; /** * Class QueryBuilder @@ -51,10 +56,14 @@ class QueryBuilder /** * List of commands to be compiled into a query - * */ protected $commands = []; + /** + * Registry of variable names used in this query + */ + protected $variables = []; + /** * ID of the query * Used as prefix for automatically generated bindings. @@ -71,6 +80,8 @@ class QueryBuilder protected $isSubQuery = false; + + public function __construct($queryId = 1) { $this->grammar = new Grammar(); @@ -78,25 +89,68 @@ public function __construct($queryId = 1) $this->queryId = $queryId; } - protected function normalizeArgument($argument, $allowedExpressionTypes) + protected function normalizeArgument($argument, $allowedExpressionTypes = null) + { + if (is_scalar($argument)) { + return $this->normalizeScalar($argument, $allowedExpressionTypes); + } + if (is_null($argument) && isset($allowedExpressionTypes['null'])) { + return new NullExpression(); + } + return $this->normalizeCompound($argument, $allowedExpressionTypes); + } + + /** + * @param $argument + * @param $allowedExpressionTypes + * @return BindExpression + * @throws ExpressionTypeException + */ + protected function normalizeScalar($argument, $allowedExpressionTypes) { - $expressionType = null; + $argumentType = $this->determineArgumentType($argument, $allowedExpressionTypes); - $expressionType = $this->determineExpressionType($argument, $allowedExpressionTypes); + // $argument = $this->handleArgumentByType($argument, $argumentType); - //Handle bindings. Replace argument data with bind variable. - if ($expressionType == 'bind') { - $argument = $this->bind($argument); - } + return $this->createExpression($argument, $argumentType); + } + + protected function createExpression($argument, $argumentType) + { + $expressionType = $this->grammar->mapArgumentTypeToExpressionType($argumentType); - if (!isset($expressionType)) { - throw new ExpressionTypeException("Not a valid expression type."); + if ($expressionType == 'Bind') { + return $this->bind($argument); } - //Return expression - $expressionClass = '\LaravelFreelancerNL\FluentAQL\Expressions\\' . ucfirst(strtolower($expressionType)) . 'Expression'; + + $expressionClass = '\LaravelFreelancerNL\FluentAQL\Expressions\\'.$expressionType.'Expression'; return new $expressionClass($argument); } + protected function normalizeCompound($argument, $allowedExpressionTypes = null) + { + if (is_array($argument)) { + return $this->normalizeArray($argument, $allowedExpressionTypes); + } + if (! is_iterable($argument)) { + return $this->normalizeObject($argument, $allowedExpressionTypes); + } + return new ObjectExpression($this->normalizeIterable($argument, $allowedExpressionTypes)); + } + + /** + * @param array|object $argument + * @param null $allowedExpressionTypes + * @return array + */ + protected function normalizeIterable($argument, $allowedExpressionTypes = null) + { + foreach ($argument as $attribute => $value) { + $argument[$attribute] = $this->normalizeArgument($value); + } + return $argument; + } + protected function normalizeSortExpression($sortExpression = null, $direction = null) : array { if (is_string($sortExpression)) { @@ -107,8 +161,8 @@ protected function normalizeSortExpression($sortExpression = null, $direction = return $sortExpression; } if (is_array($sortExpression) && ! empty($sortExpression)) { - $sortExpression[0] = $this->normalizeArgument($sortExpression[0], 'attribute'); - if (isset($sortExpression[1]) && ! $this->grammar->is_sortDirection($sortExpression[1])) { + $sortExpression[0] = $this->normalizeArgument($sortExpression[0], 'VariableAttribute'); + if (isset($sortExpression[1]) && ! $this->grammar->isSortDirection($sortExpression[1])) { unset($sortExpression[1]); } return $sortExpression; @@ -120,12 +174,12 @@ protected function normalizeSortExpression($sortExpression = null, $direction = protected function normalizeEdgeCollections($edgeCollection) : array { if (is_string($edgeCollection)) { - $edgeCollection = [$this->normalizeArgument($edgeCollection, 'collection')]; + $edgeCollection = [$this->normalizeArgument($edgeCollection, 'Collection')]; return $edgeCollection; } if (is_array($edgeCollection) && ! empty($edgeCollection)) { - $edgeCollection[0] = $this->normalizeArgument($edgeCollection[0], 'collection'); - if (isset($edgeCollection[1]) && ! $this->grammar->is_direction($edgeCollection[1])) { + $edgeCollection[0] = $this->normalizeArgument($edgeCollection[0], 'Collection'); + if (isset($edgeCollection[1]) && ! $this->grammar->isDirection($edgeCollection[1])) { unset($edgeCollection[1]); } return $edgeCollection; @@ -150,7 +204,7 @@ protected function normalizePredicates($predicates) : array $logicalOperator = null; $lastElement = end($predicate); - if ($this->grammar->is_logicalOperator($lastElement)) { + if ($this->grammar->isLogicalOperator($lastElement)) { array_pop($predicate); $logicalOperator = $lastElement; } @@ -178,21 +232,21 @@ protected function normalizePredicate($predicate) } // if $rightOperand is empty and $comparisonOperator is not a valid operate, then the operation defaults to '==' - if (! $this->grammar->is_comparisonOperator($operator) && $value == null) { + if (! $this->grammar->isComparisonOperator($operator) && $value == null) { $value = $operator; $operator = '=='; } - if ($this->grammar->is_comparisonOperator($operator) && $value == null) { + if ($this->grammar->isComparisonOperator($operator) && $value == null) { $operator = '=='; $value = 'null'; } // leftOperands can only be attributes - $attribute = $this->normalizeArgument($attribute, ['attribute', 'bind']); + $attribute = $this->normalizeArgument($attribute, ['VariableAttribute', 'Bind']); // rightOperands can be any type of data: attributes, values, queries etc - $value = $this->normalizeArgument($value, ['attribute', 'query', 'function', 'bind']); + $value = $this->normalizeArgument($value, ['VariableAttribute', 'Boolean', 'Null', 'Query', 'Function', 'Bind']); $normalizedPredicate[] = new PredicateExpression($attribute, $operator, $value); if (isset($predicate['logicalOperator'])) { @@ -208,19 +262,35 @@ protected function normalizePredicate($predicate) * @param string|iterable $argument * @param $allowedExpressionTypes * @return mixed + * @throws ExpressionTypeException */ - protected function determineExpressionType($argument, $allowedExpressionTypes) + protected function determineArgumentType($argument, $allowedExpressionTypes = null) { if (is_string($allowedExpressionTypes)) { $allowedExpressionTypes = [$allowedExpressionTypes]; } + if ($allowedExpressionTypes == null) { + $allowedExpressionTypes = $this->grammar->getAllowedExpressionTypes(); + } foreach ($allowedExpressionTypes as $allowedExpressionType) { - $check = 'is_'.$allowedExpressionType; + $check = 'is'.$allowedExpressionType; + if ($allowedExpressionType == 'VariableAttribute') { + if ($this->grammar->$check($argument, $this->variables)) { + return $allowedExpressionType; + } + } if ($this->grammar->$check($argument)) { return $allowedExpressionType; } } + + //Fallback to BindExpression if allowed + if (isset($allowedExpressionTypes['Bind'])) { + return 'Bind'; + } + + throw new ExpressionTypeException("This data does not match one of these expression types: ".implode(', ', $allowedExpressionTypes).'.'); } protected function setSubQuery() @@ -304,9 +374,22 @@ public function registerCollections($collections, $mode = 'write') : QueryBuilde return $this; } + /** + * Register variables on declaration for later data normalization. + * + * @param string $variableName + * @return QueryBuilder + */ + protected function registerVariable(string $variableName) : QueryBuilder + { + $this->variables[$variableName] = $variableName; + + return $this; + } + public function bind($data, $to = null, $collection = false) : BindExpression { - $data = $this->grammar->prepareDataToBind($data); + $data = json_encode($data, JSON_UNESCAPED_SLASHES); if ($to == null) { $to = $this->queryId.'_'.(count($this->binds)+1); @@ -377,4 +460,39 @@ public function __toString() { return $this->toAql(); } + + public function wrap($value) + { + return $this->grammar->wrap($value); + } + + /** + * @param $argument + * @param $allowedExpressionTypes + * @return ListExpression|ObjectExpression + */ + protected function normalizeArray($argument, $allowedExpressionTypes) + { + if ($this->grammar->isAssociativeArray($argument)) { + return new ObjectExpression($this->normalizeIterable($argument, $allowedExpressionTypes)); + } + return new ListExpression($this->normalizeIterable($argument, $allowedExpressionTypes)); + } + + /** + * @param $argument + * @param $allowedExpressionTypes + * @return ObjectExpression|StringExpression + */ + protected function normalizeObject($argument, $allowedExpressionTypes) + { + if ($argument instanceof \DateTimeInterface) { + return new StringExpression($argument->format(\DateTime::ATOM)); + } + if ($argument instanceof ExpressionInterface) { + //Fixme: check for queryBuilders, functions, binds etc and handle them accordingly + return $argument; + } + return new ObjectExpression($this->normalizeIterable((array)$argument, $allowedExpressionTypes)); + } } diff --git a/tests/unit/ExpressionsTest.php b/tests/unit/ExpressionsTest.php index 5e1f803..eaf2ecb 100644 --- a/tests/unit/ExpressionsTest.php +++ b/tests/unit/ExpressionsTest.php @@ -1,6 +1,7 @@ grammar->is_range('0..1'); + $result = $this->grammar->isRange('0..1'); self::assertTrue($result); - $result = $this->grammar->is_range('2..1'); + $result = $this->grammar->isRange('2..1'); self::assertTrue($result); - $result = $this->grammar->is_range('00..111'); + $result = $this->grammar->isRange('00..111'); self::assertTrue($result); - $result = $this->grammar->is_range('0.1..1'); + $result = $this->grammar->isRange('0.1..1'); self::assertTrue($result); - $result = $this->grammar->is_range('0.1.'); + $result = $this->grammar->isRange('0.1.'); self::assertFalse($result); - $result = $this->grammar->is_range('0..1.'); + $result = $this->grammar->isRange('0..1.'); self::assertFalse($result); - $result = $this->grammar->is_range('a..1'); + $result = $this->grammar->isRange('a..1'); self::assertFalse($result); } @@ -66,34 +66,34 @@ public function is_range() */ public function is_collection() { - $result = $this->grammar->is_collection('col'); + $result = $this->grammar->isCollection('col'); self::assertTrue($result); - $result = $this->grammar->is_collection('_col'); + $result = $this->grammar->isCollection('_col'); self::assertTrue($result); - $result = $this->grammar->is_collection('c_ol'); + $result = $this->grammar->isCollection('c_ol'); self::assertTrue($result); - $result = $this->grammar->is_collection('co-l'); + $result = $this->grammar->isCollection('co-l'); self::assertTrue($result); - $result = $this->grammar->is_collection('col-'); + $result = $this->grammar->isCollection('col-'); self::assertTrue($result); - $result = $this->grammar->is_collection('col-1'); + $result = $this->grammar->isCollection('col-1'); self::assertTrue($result); - $result = $this->grammar->is_collection('@col-1'); + $result = $this->grammar->isCollection('@col-1'); self::assertFalse($result); - $result = $this->grammar->is_collection('colö'); + $result = $this->grammar->isCollection('colö'); self::assertFalse($result); - $result = $this->grammar->is_collection('col.1'); + $result = $this->grammar->isCollection('col.1'); self::assertFalse($result); - $result = $this->grammar->is_collection('col`1'); + $result = $this->grammar->isCollection('col`1'); self::assertFalse($result); } @@ -103,19 +103,19 @@ public function is_collection() */ public function is_key() { - $result = $this->grammar->is_key('_key'); + $result = $this->grammar->isKey('_key'); self::assertTrue($result); - $result = $this->grammar->is_key('_key'); + $result = $this->grammar->isKey('_key'); self::assertTrue($result); - $result = $this->grammar->is_key('100'); + $result = $this->grammar->isKey('100'); self::assertTrue($result); - $result = $this->grammar->is_key('Aݔ'); + $result = $this->grammar->isKey('Aݔ'); self::assertFalse($result); - $result = $this->grammar->is_key('Aä'); + $result = $this->grammar->isKey('Aä'); self::assertFalse($result); } @@ -125,22 +125,22 @@ public function is_key() */ public function is_id() { - $result = $this->grammar->is_id('Characters/BranStark'); + $result = $this->grammar->isId('Characters/BranStark'); self::assertTrue($result); - $result = $this->grammar->is_id('col-1/Aa'); + $result = $this->grammar->isId('col-1/Aa'); self::assertTrue($result); - $result = $this->grammar->is_id('col/_key'); + $result = $this->grammar->isId('col/_key'); self::assertTrue($result); - $result = $this->grammar->is_id('col1-1/100'); + $result = $this->grammar->isId('col1-1/100'); self::assertTrue($result); - $result = $this->grammar->is_id('@col-1/_key'); + $result = $this->grammar->isId('@col-1/_key'); self::assertFalse($result); - $result = $this->grammar->is_id('col/Aä'); + $result = $this->grammar->isId('col/Aä'); self::assertFalse($result); } @@ -150,37 +150,37 @@ public function is_id() */ public function validate_variable_name_syntax() { - $result = $this->grammar->is_variable('doc'); + $result = $this->grammar->isVariable('doc'); self::assertTrue($result); - $result = $this->grammar->is_variable('dOc'); + $result = $this->grammar->isVariable('dOc'); self::assertTrue($result); - $result = $this->grammar->is_variable('Doc0'); + $result = $this->grammar->isVariable('Doc0'); self::assertTrue($result); - $result = $this->grammar->is_variable('_doc'); + $result = $this->grammar->isVariable('_doc'); self::assertTrue($result); - $result = $this->grammar->is_variable('$doc'); + $result = $this->grammar->isVariable('$doc'); self::assertTrue($result); - $result = $this->grammar->is_variable('$$doc'); + $result = $this->grammar->isVariable('$$doc'); self::assertFalse($result); - $result = $this->grammar->is_variable('$doc$'); + $result = $this->grammar->isVariable('$doc$'); self::assertFalse($result); - $result = $this->grammar->is_variable('doc-eat-dog'); + $result = $this->grammar->isVariable('doc-eat-dog'); self::assertFalse($result); - $result = $this->grammar->is_variable('-doc'); + $result = $this->grammar->isVariable('-doc'); self::assertFalse($result); - $result = $this->grammar->is_variable('d"oc'); + $result = $this->grammar->isVariable('d"oc'); self::assertFalse($result); - $result = $this->grammar->is_variable('döc'); + $result = $this->grammar->isVariable('döc'); self::assertFalse($result); } @@ -190,76 +190,95 @@ public function validate_variable_name_syntax() */ public function is_attribute() { - $result = $this->grammar->is_attribute('`_key`'); + $result = $this->grammar->isAttribute('`_key`'); self::assertTrue($result); - $result = $this->grammar->is_attribute('@shizzle'); + $result = $this->grammar->isAttribute('@shizzle'); self::assertTrue($result); - $result = $this->grammar->is_attribute('shizzle%'); + $result = $this->grammar->isAttribute('shizzle%'); self::assertFalse($result); - $result = $this->grammar->is_attribute('@shizzle%'); + $result = $this->grammar->isAttribute('@shizzle%'); self::assertFalse($result); - $result = $this->grammar->is_attribute('`FOR`[whatever]'); + $result = $this->grammar->isAttribute('`FOR`[whatever]'); self::assertTrue($result); - $result = $this->grammar->is_attribute('`KEYWORD`[whatever][*][*]'); + $result = $this->grammar->isAttribute('`KEYWORD`[whatever][*][*]'); self::assertTrue($result); - $result = $this->grammar->is_attribute('`KEYWORD`[`.fgdfg.`]'); + $result = $this->grammar->isAttribute('`KEYWORD`[`.fgdfg.`]'); self::assertTrue($result); - $result = $this->grammar->is_attribute('`KEYWORD`[whatever][*][*].what'); + $result = $this->grammar->isAttribute('`KEYWORD`[whatever][*][*].what'); self::assertTrue($result); - $result = $this->grammar->is_attribute('_from[*]'); + $result = $this->grammar->isAttribute('_from[*]'); self::assertTrue($result); - $result = $this->grammar->is_attribute('_from[**]'); + $result = $this->grammar->isAttribute('_from[**]'); self::assertTrue($result); - $result = $this->grammar->is_attribute('_key[2]'); + $result = $this->grammar->isAttribute('_key[2]'); self::assertTrue($result); - $result = $this->grammar->is_attribute('_from[*][**]'); + $result = $this->grammar->isAttribute('_from[*][**]'); self::assertTrue($result); - $result = $this->grammar->is_attribute('u.active'); + $result = $this->grammar->isAttribute('u.active'); self::assertTrue($result); - $result = $this->grammar->is_attribute('u.@active'); + $result = $this->grammar->isAttribute('u.@active'); self::assertTrue($result); - $result = $this->grammar->is_attribute('u.@active.age'); + $result = $this->grammar->isAttribute('u.@active.age'); self::assertTrue($result); - $result = $this->grammar->is_attribute('u.@active.@age'); + $result = $this->grammar->isAttribute('u.@active.@age'); self::assertTrue($result); - $result = $this->grammar->is_attribute('u.`@active`'); + $result = $this->grammar->isAttribute('u.`@active`'); self::assertTrue($result); - $result = $this->grammar->is_attribute('u.`@active`.@age'); + $result = $this->grammar->isAttribute('u.`@active`.@age'); self::assertTrue($result); - $result = $this->grammar->is_attribute('doc.text[2]'); + $result = $this->grammar->isAttribute('doc.text[2]'); self::assertTrue($result); - $result = $this->grammar->is_attribute('doc.@text[2]'); + $result = $this->grammar->isAttribute('doc.@text[2]'); self::assertTrue($result); - $result = $this->grammar->is_attribute('doc[name]'); + $result = $this->grammar->isAttribute('doc[name]'); self::assertTrue($result); - $result = $this->grammar->is_attribute('attributes[*].name'); + $result = $this->grammar->isAttribute('attributes[*].name'); self::assertTrue($result); - $result = $this->grammar->is_attribute('u.friends[*][*].name'); + $result = $this->grammar->isAttribute('u.friends[*][*].name'); self::assertTrue($result); } + /** + * is variableAttribute + * @test + */ + public function is_variable_attribute() + { + $registeredVariables = ['doc', 'u']; + + $result = $this->grammar->isVariableAttribute('doc[name]', $registeredVariables); + self::assertTrue($result); + + $result = $this->grammar->isVariableAttribute('u.[*]', $registeredVariables); + self::assertTrue($result); + + $result = $this->grammar->isVariableAttribute('smurf[name]', $registeredVariables); + self::assertFalse($result); + } + + /** * is document * @test @@ -268,17 +287,17 @@ public function is_document() { $doc = new stdClass(); $doc->attribute1 = 'test'; - $result = $this->grammar->is_document($doc); + $result = $this->grammar->isObject($doc); self::assertTrue($result); $associativeArray = [ 'attribute1' => 'test' ]; - $result = $this->grammar->is_document($associativeArray); + $result = $this->grammar->isObject($associativeArray); self::assertTrue($result); $listArray = ['test']; - $result = $this->grammar->is_document($listArray); + $result = $this->grammar->isObject($listArray); self::assertFalse($result); } @@ -288,13 +307,13 @@ public function is_document() */ public function is_list() { - $result = $this->grammar->is_list([1, 2, 3]); + $result = $this->grammar->isList([1, 2, 3]); self::assertTrue($result); - $result = $this->grammar->is_list(1); + $result = $this->grammar->isList(1); self::assertFalse($result); - $result = $this->grammar->is_list('a string'); + $result = $this->grammar->isList('a string'); self::assertFalse($result); } @@ -304,13 +323,13 @@ public function is_list() */ public function is_numeric() { - $result = $this->grammar->is_numeric(4); + $result = $this->grammar->isNumber(4); self::assertTrue($result); - $result = $this->grammar->is_numeric(4.4); + $result = $this->grammar->isNumber(4.4); self::assertTrue($result); - $result = $this->grammar->is_numeric('string'); + $result = $this->grammar->isNumber('string'); self::assertFalse($result); } @@ -355,13 +374,13 @@ public function validate_bind_parameter_syntax() */ public function is_sort_direction() { - $result = $this->grammar->is_sortDirection('asc'); + $result = $this->grammar->isSortDirection('asc'); self::assertTrue($result); - $result = $this->grammar->is_sortDirection('csa'); + $result = $this->grammar->isSortDirection('csa'); self::assertFalse($result); - $result = $this->grammar->is_sortDirection('aSc'); + $result = $this->grammar->isSortDirection('aSc'); self::assertTrue($result); - $result = $this->grammar->is_sortDirection('desc'); + $result = $this->grammar->isSortDirection('desc'); self::assertTrue($result); } @@ -371,16 +390,16 @@ public function is_sort_direction() */ public function is_graph_direction() { - $result = $this->grammar->is_direction('outbound'); + $result = $this->grammar->isDirection('outbound'); self::assertTrue($result); - $result = $this->grammar->is_direction('inbound'); + $result = $this->grammar->isDirection('inbound'); self::assertTrue($result); - $result = $this->grammar->is_direction('ANY'); + $result = $this->grammar->isDirection('ANY'); self::assertTrue($result); - $result = $this->grammar->is_direction('dfhdrf'); + $result = $this->grammar->isDirection('dfhdrf'); self::assertFalse($result); } @@ -390,7 +409,7 @@ public function is_graph_direction() */ public function is_function() { - $result = $this->grammar->is_function(AQB::document('Characters/123')); + $result = $this->grammar->isFunction(AQB::document('Characters/123')); self::assertTrue($result); } @@ -400,45 +419,16 @@ public function is_function() */ public function is_logical_operator() { - $result = $this->grammar->is_logicalOperator('AND'); + $result = $this->grammar->isLogicalOperator('AND'); self::assertTrue($result); - $result = $this->grammar->is_logicalOperator('!'); + $result = $this->grammar->isLogicalOperator('!'); self::assertTrue($result); - $result = $this->grammar->is_logicalOperator('whatever'); + $result = $this->grammar->isLogicalOperator('whatever'); self::assertFalse($result); } - /** - * prepareDataToBind - * @test - */ - public function prepare_data_to_bind() - { - $data = 'shizzle'; - $preparedData = $this->grammar->prepareDataToBind($data); - self::assertEquals('shizzle', $preparedData); - - $data = 666; - $preparedData = $this->grammar->prepareDataToBind($data); - self::assertEquals(666, $preparedData); - - $data = [1, 2]; - $preparedData = $this->grammar->prepareDataToBind($data); - self::assertEquals($data, $preparedData); - - $data = (object) ['locations/123-456', 'locations/234-567', 'locations/345-678']; - $preparedData = $this->grammar->prepareDataToBind($data); - self::assertEquals('{"0":"locations/123-456","1":"locations/234-567","2":"locations/345-678"}', $preparedData); - - $data = [1, 2, (object) [ 'attribute 1' => "One piece!!!", 'attribute 2' => "` backtick party"]]; - $preparedData = $this->grammar->prepareDataToBind($data); - $controlData = [1, 2, '{"attribute 1":"One piece!!!","attribute 2":"` backtick party"}']; - self::assertNotEquals($data, $preparedData); - self::assertEquals($controlData, $preparedData); - } - /** * is array associative or numeric * @test @@ -462,16 +452,16 @@ public function is_array_associative() "employer" => 'The Realm' ]; - $result = $this->grammar->arrayIsAssociative($emptyArray); + $result = $this->grammar->isAssociativeArray($emptyArray); self::assertTrue($result); - $result = $this->grammar->arrayIsAssociative($associativeArray); + $result = $this->grammar->isAssociativeArray($associativeArray); self::assertTrue($result); - $result = $this->grammar->arrayIsAssociative($mixedArray); + $result = $this->grammar->isAssociativeArray($mixedArray); self::assertTrue($result); - $result = $this->grammar->arrayIsAssociative($numericArray); + $result = $this->grammar->isAssociativeArray($numericArray); self::assertFalse($result); } @@ -498,16 +488,16 @@ public function is_array_associative_or_numeric() "employer" => 'The Realm' ]; - $result = $this->grammar->arrayIsNumeric($emptyArray); + $result = $this->grammar->isIndexedArray($emptyArray); self::assertTrue($result); - $result = $this->grammar->arrayIsNumeric($associativeArray); + $result = $this->grammar->isIndexedArray($associativeArray); self::assertFalse($result); - $result = $this->grammar->arrayIsNumeric($mixedArray); + $result = $this->grammar->isIndexedArray($mixedArray); self::assertFalse($result); - $result = $this->grammar->arrayIsNumeric($numericArray); + $result = $this->grammar->isIndexedArray($numericArray); self::assertTrue($result); } } diff --git a/tests/unit/GraphClausesTest.php b/tests/unit/GraphClausesTest.php index a128721..8216aab 100644 --- a/tests/unit/GraphClausesTest.php +++ b/tests/unit/GraphClausesTest.php @@ -93,19 +93,19 @@ public function edge_collection_list_clause() */ public function prune_clause_syntax() { - $result = AQB::prune('u.active', '==', 'true')->get(); - self::assertEquals('PRUNE u.active == true', $result->query); + $result = AQB::for('u', 'Users')->prune('u.active', '==', 'true')->get(); + self::assertEquals('FOR u IN Users PRUNE u.active == true', $result->query); - $result = AQB::prune('u.active', '==', 'true', 'OR')->get(); - self::assertEquals('PRUNE u.active == true', $result->query); + $result = AQB::for('u', 'Users')->prune('u.active', '==', 'true', 'OR')->get(); + self::assertEquals('FOR u IN Users PRUNE u.active == true', $result->query); - $result = AQB::prune('u.active', 'true')->get(); - self::assertEquals('PRUNE u.active == true', $result->query); + $result = AQB::for('u', 'Users')->prune('u.active', 'true')->get(); + self::assertEquals('FOR u IN Users PRUNE u.active == true', $result->query); - $result = AQB::prune('u.active')->get(); - self::assertEquals('PRUNE u.active == null', $result->query); + $result = AQB::for('u', 'Users')->prune('u.active')->get(); + self::assertEquals('FOR u IN Users PRUNE u.active == null', $result->query); - $result = AQB::prune([['u.active', '==', 'true'], ['u.age']])->get(); - self::assertEquals('PRUNE u.active == true AND u.age == null', $result->query); + $result = AQB::for('u', 'Users')->prune([['u.active', '==', 'true'], ['u.age']])->get(); + self::assertEquals('FOR u IN Users PRUNE u.active == true AND u.age == null', $result->query); } } diff --git a/tests/unit/QueryBuilderTest.php b/tests/unit/QueryBuilderTest.php index 76595f8..1eae54d 100644 --- a/tests/unit/QueryBuilderTest.php +++ b/tests/unit/QueryBuilderTest.php @@ -1,11 +1,6 @@ binds['1_1']); - self::assertEquals(121, strlen($qb->binds['1_1'])); + self::assertEquals(153, strlen($qb->binds['1_1'])); } /** diff --git a/tests/unit/QueryClausesTest.php b/tests/unit/QueryClausesTest.php index 6226c3b..5229fe5 100644 --- a/tests/unit/QueryClausesTest.php +++ b/tests/unit/QueryClausesTest.php @@ -31,10 +31,10 @@ public function collect_clause() self::assertEquals('COLLECT', $result->query); $result = AQB::collect('doc', 'expression')->get(); - self::assertEquals('COLLECT doc = expression', $result->query); + self::assertEquals('COLLECT doc = @1_1', $result->query); - $result = AQB::collect('hometown', 'u.city')->get(); - self::assertEquals('COLLECT hometown = u.city', $result->query); + $result = AQB::for('u', 'Users')->collect('hometown', 'u.city')->get(); + self::assertEquals('FOR u IN Users COLLECT hometown = u.city', $result->query); } /** @@ -47,7 +47,7 @@ public function group_clause() self::assertEquals('INTO groupsVariable', $result->query); $result = AQB::group('groupsVariable', 'projectionExpression')->get(); - self::assertEquals('INTO groupsVariable = projectionExpression', $result->query); + self::assertEquals('INTO groupsVariable = @1_1', $result->query); $result = AQB::group('groupsVariable', '{ "name" : u.name, @@ -63,7 +63,7 @@ public function group_clause() public function aggregate_clause() { $result = AQB::aggregate('variableName', 'aggregateExpression')->get(); - self::assertEquals('AGGREGATE variableName = aggregateExpression', $result->query); + self::assertEquals('AGGREGATE variableName = @1_1', $result->query); } /** @@ -111,7 +111,7 @@ public function for_clause_syntax() $result = AQB::for('u', 'users')->get(); self::assertEquals('FOR u IN users', $result->query); - $result = AQB::for('u', )->get(); + $result = AQB::for('u')->get(); self::assertEquals('FOR u IN', $result->query); $result = AQB::for(['v', 'e', 'p'], 'graph')->get(); @@ -124,20 +124,20 @@ public function for_clause_syntax() */ public function filter_clause_syntax() { - $result = AQB::filter('u.active', '==', 'true')->get(); - self::assertEquals('FILTER u.active == true', $result->query); + $result = AQB::for('u', 'Users')->filter('u.active', '==', 'true')->get(); + self::assertEquals('FOR u IN Users FILTER u.active == true', $result->query); - $result = AQB::filter('u.active', '==', 'true', 'OR')->get(); - self::assertEquals('FILTER u.active == true', $result->query); + $result = AQB::for('u', 'Users')->filter('u.active', '==', 'true', 'OR')->get(); + self::assertEquals('FOR u IN Users FILTER u.active == true', $result->query); - $result = AQB::filter('u.active', 'true')->get(); - self::assertEquals('FILTER u.active == true', $result->query); + $result = AQB::for('u', 'Users')->filter('u.active', 'true')->get(); + self::assertEquals('FOR u IN Users FILTER u.active == true', $result->query); - $result = AQB::filter('u.active')->get(); - self::assertEquals('FILTER u.active == null', $result->query); + $result = AQB::for('u', 'Users')->filter('u.active')->get(); + self::assertEquals('FOR u IN Users FILTER u.active == null', $result->query); - $result = AQB::filter([['u.active', '==', 'true'], ['u.age']])->get(); - self::assertEquals('FILTER u.active == true AND u.age == null', $result->query); + $result = AQB::for('u', 'Users')->filter([['u.active', '==', 'true'], ['u.age']])->get(); + self::assertEquals('FOR u IN Users FILTER u.active == true AND u.age == null', $result->query); } /** @@ -146,20 +146,20 @@ public function filter_clause_syntax() */ public function search_clause_syntax() { - $result = AQB::search('u.active', '==', 'true')->get(); - self::assertEquals('SEARCH u.active == true', $result->query); + $result = AQB::for('u', 'Users')->search('u.active', '==', 'true')->get(); + self::assertEquals('FOR u IN Users SEARCH u.active == true', $result->query); - $result = AQB::search('u.active', '==', 'true', 'OR')->get(); - self::assertEquals('SEARCH u.active == true', $result->query); + $result = AQB::for('u', 'Users')->search('u.active', '==', 'true', 'OR')->get(); + self::assertEquals('FOR u IN Users SEARCH u.active == true', $result->query); - $result = AQB::search('u.active', 'true')->get(); - self::assertEquals('SEARCH u.active == true', $result->query); + $result = AQB::for('u', 'Users')->search('u.active', 'true')->get(); + self::assertEquals('FOR u IN Users SEARCH u.active == true', $result->query); - $result = AQB::search('u.active')->get(); - self::assertEquals('SEARCH u.active == null', $result->query); + $result = AQB::for('u', 'Users')->search('u.active')->get(); + self::assertEquals('FOR u IN Users SEARCH u.active == null', $result->query); - $result = AQB::search([['u.active', '==', 'true'], ['u.age']])->get(); - self::assertEquals('SEARCH u.active == true AND u.age == null', $result->query); + $result = AQB::for('u', 'Users')->search([['u.active', '==', 'true'], ['u.age']])->get(); + self::assertEquals('FOR u IN Users SEARCH u.active == true AND u.age == null', $result->query); } @@ -169,8 +169,8 @@ public function search_clause_syntax() */ public function sort_clause_syntax() { - $result = AQB::sort('u.name', 'DESC')->get(); - self::assertEquals('SORT u.name DESC', $result->query); + $result = AQB::for('u', 'Users')->sort('u.name', 'DESC')->get(); + self::assertEquals('FOR u IN Users SORT u.name DESC', $result->query); $result = AQB::sort('null')->get(); self::assertEquals('SORT null', $result->query); @@ -178,20 +178,20 @@ public function sort_clause_syntax() $result = AQB::sort()->get(); self::assertEquals('SORT null', $result->query); - $result = AQB::sort(['u.name'])->get(); - self::assertEquals('SORT u.name', $result->query); + $result = AQB::for('u', 'Users')->sort(['u.name'])->get(); + self::assertEquals('FOR u IN Users SORT u.name', $result->query); - $result = AQB::sort(['u.name', 'u.age'])->get(); - self::assertEquals('SORT u.name, u.age', $result->query); + $result = AQB::for('u', 'Users')->sort(['u.name', 'u.age'])->get(); + self::assertEquals('FOR u IN Users SORT u.name, u.age', $result->query); - $result = AQB::sort([['u.age', 'DESC']])->get(); - self::assertEquals('SORT u.age DESC', $result->query); + $result = AQB::for('u', 'Users')->sort([['u.age', 'DESC']])->get(); + self::assertEquals('FOR u IN Users SORT u.age DESC', $result->query); - $result = AQB::sort(['u.name', ['u.age', 'DESC']])->get(); - self::assertEquals('SORT u.name, u.age DESC', $result->query); + $result = AQB::for('u', 'Users')->sort(['u.name', ['u.age', 'DESC']])->get(); + self::assertEquals('FOR u IN Users SORT u.name, u.age DESC', $result->query); - $result = AQB::sort(['u.name', 'DESC'])->get(); - self::assertNotEquals('SORT u.name DESC', $result->query); + $result = AQB::for('u', 'Users')->sort(['u.name', 'DESC'])->get(); + self::assertNotEquals('FOR u IN Users SORT u.name DESC', $result->query); } /** @@ -214,12 +214,15 @@ public function limit_clause_syntax() public function return_clause_syntax() { $result = AQB::return('u.name')->get(); - self::assertEquals('RETURN u.name', $result->query); + self::assertEquals('RETURN @1_1', $result->query); + + $result = AQB::for('u', 'Users')->return('u.name')->get(); + self::assertEquals('FOR u IN Users RETURN u.name', $result->query); $result = AQB::return("1 + 1")->get(); - self::assertEquals('RETURN 1 + 1', $result->query); + self::assertEquals('RETURN @1_1', $result->query); $result = AQB::return("1 + 1", true)->get(); - self::assertEquals('RETURN DISTINCT 1 + 1', $result->query); + self::assertEquals('RETURN DISTINCT @1_1', $result->query); } } diff --git a/tests/unit/StatementClausesTest.php b/tests/unit/StatementClausesTest.php index cb3d4bb..637d3a4 100644 --- a/tests/unit/StatementClausesTest.php +++ b/tests/unit/StatementClausesTest.php @@ -37,7 +37,7 @@ public function let_statement() $object->age = 40; $object->traits = ['D', 'H', 'C']; $result = $qb->let('x', $object)->get(); - self::assertEquals('LET x = @1_1', $result->query); + self::assertEquals('LET x = {"name":"Catelyn","surname":"Stark","alive":false,"age":40,"traits":[@1_1,@1_2,@1_3]}', $result->query); $result = AQB::let('x', 'y')->get(); self::assertEquals('LET x = @1_1', $result->query);