diff --git a/src/Client/Query.php b/src/Client/Query.php index c03ee6d..bca3d7d 100644 --- a/src/Client/Query.php +++ b/src/Client/Query.php @@ -2,6 +2,8 @@ namespace OramaCloud\Client; +use OramaCloud\Client\QueryParams\SortBy; +use OramaCloud\Client\QueryParams\SortByOrder; use OramaCloud\Client\QueryParams\Where; class Query @@ -10,6 +12,7 @@ class Query private $mode; private $limit; private $offset; + private $sortBy; private $where = []; public function __construct($term = '', $mode = 'fulltext') @@ -30,19 +33,25 @@ public function mode($mode) return $this; } - public function where($property, $operator, $value) + public function where(string $property, $operator, $value) { $this->where[] = new Where($property, $operator, $value); return $this; } - public function limit($limit) + public function sortBy(string $property, $order = SortByOrder::ASC) + { + $this->sortBy = new SortBy($property, $order); + return $this; + } + + public function limit(int $limit) { $this->limit = $limit; return $this; } - public function offset($offset) + public function offset(int $offset) { $this->offset = $offset; return $this; @@ -76,12 +85,11 @@ public function toArray() } } - return $array; - } + if (!is_null($this->sortBy)) { + $array['sortBy'] = $this->sortBy->toArray(); + } - public function toJson() - { - return json_encode($this->toArray()); + return $array; } public static function fromArray($array) @@ -97,6 +105,10 @@ public static function fromArray($array) } } + if (isset($array['sortBy']) && !is_null($array['sortBy'])) { + $query->sortBy($array['sortBy']['property'], $array['sortBy']['order']); + } + if (isset($array['limit']) && !is_null($array['limit'])) { $query->limit($array['limit']); } @@ -107,4 +119,19 @@ public static function fromArray($array) return $query; } + + public function toJson() + { + return json_encode($this->toArray()); + } + + public static function fromJson($json) + { + return Query::fromArray(json_decode($json, true)); + } + + public function toQueryString() + { + return http_build_query($this->toArray()); + } } diff --git a/src/Client/QueryParams/SortBy.php b/src/Client/QueryParams/SortBy.php new file mode 100644 index 0000000..f3a2d0c --- /dev/null +++ b/src/Client/QueryParams/SortBy.php @@ -0,0 +1,36 @@ +property = $property; + $this->order = strtoupper($order); + + $this->validate(); + } + + public function toArray() + { + return [ + 'property' => $this->property, + 'order' => $this->order + ]; + } + + private function validate() + { + if (!in_array($this->order, $this->availableOrders)) { + throw new \InvalidArgumentException('Invalid $order parameter in SortBy'); + } + } +} diff --git a/src/Client/QueryParams/SortByOrder.php b/src/Client/QueryParams/SortByOrder.php new file mode 100644 index 0000000..2218692 --- /dev/null +++ b/src/Client/QueryParams/SortByOrder.php @@ -0,0 +1,10 @@ +property = $property; $this->operator = $operator; $this->value = $value; + + $this->validate(); } public function toArray() @@ -24,4 +36,19 @@ public function toArray() ] ]; } + + private function validate() + { + if (!in_array($this->operator, $this->availableOperators)) { + throw new \InvalidArgumentException("Invalid operator {$this->operator}"); + } + + if (in_array($this->operator, [ + WhereOperator::BETWEEN, + WhereOperator::IN, + WhereOperator::NIN + ]) && !is_array($this->value)) { + throw new \InvalidArgumentException('Where $value parameter must be an array'); + } + } } diff --git a/src/Client/QueryParams/WhereOperator.php b/src/Client/QueryParams/WhereOperator.php new file mode 100644 index 0000000..d18bb78 --- /dev/null +++ b/src/Client/QueryParams/WhereOperator.php @@ -0,0 +1,22 @@ +term('red shoes') - ->mode('fulltext') - ->where('price', 'gte', 99.99) - ->where('category', 'eq', 'shoes'); - - $result = $query->toArray(); - - $this->assertEquals($result['term'], 'red shoes'); - $this->assertEquals($result['mode'], 'fulltext'); - $this->assertEquals($result['where'], [ - 'category' => [ - 'eq' => 'shoes' - ], - 'price' => [ - 'gte' => 99.99 - ] - ]); -}); - -test('default query params from array', function () { - $query = Query::fromArray([]); - - $this->assertEquals($query->toArray(), [ - 'term' => '', - 'mode' => 'fulltext', - ]); -}); - -test('query params from array', function () { - $params = [ - 'term' => 'mock-term', - 'mode' => 'mock-mode', - 'where' => [ - 'foo' => [ - 'eq' => 99 +describe('Query builder', function () { + it('should configure query params', function () { + $query = new Query(); + $query + ->term('red shoes') + ->mode('fulltext') + ->where('price', 'gte', 99.99) + ->where('category', 'eq', 'shoes') + ->sortBy('price', 'desc'); + + $result = $query->toArray(); + + $this->assertEquals($result['term'], 'red shoes'); + $this->assertEquals($result['mode'], 'fulltext'); + $this->assertEquals($result['where'], [ + 'category' => [ + 'eq' => 'shoes' ], - 'bar' => [ - 'gt' => 10 + 'price' => [ + 'gte' => 99.99 ] - ] - ]; - - $query = Query::fromArray($params); - - $this->assertEquals($params, $query->toArray()); -}); - -test('query params as json', function () { - $params = [ - 'term' => 'mock-term', - 'mode' => 'mock-mode' - ]; + ]); + $this->assertEquals($result['sortBy'], [ + 'property' => 'price', + 'order' => 'DESC' + ]); + + $this->assertEquals($query->toJson(), json_encode($result)); + }); + + it('defaults query params from array', function () { + $query = Query::fromArray([]); + + $this->assertEquals($query->toArray(), [ + 'term' => '', + 'mode' => 'fulltext', + ]); + }); + + it('accepts query params from array', function () { + $params = [ + 'term' => 'mock-term', + 'mode' => 'mock-mode', + 'where' => [ + 'foo' => [ + 'eq' => 99 + ], + 'bar' => [ + 'gt' => 10 + ] + ] + ]; - $query = Query::fromArray($params); + $query = Query::fromArray($params); - $this->assertEquals($params, json_decode($query->toJson(), true)); + $this->assertEquals($params, $query->toArray()); + $this->assertEquals($params, json_decode($query->toJson(), true)); + }); }); diff --git a/tests/Unit/SortByTest.php b/tests/Unit/SortByTest.php new file mode 100644 index 0000000..a9ede81 --- /dev/null +++ b/tests/Unit/SortByTest.php @@ -0,0 +1,41 @@ +toArray())->toBe([ + 'property' => 'name', + 'order' => 'ASC' + ]); + }); + + it('should throw an exception when invalid order is passed', function () { + $closure = function () { + new SortBy('name', 'INVALID'); + }; + + expect($closure)->toThrow(new \InvalidArgumentException('Invalid $order parameter in SortBy')); + }); + + it('should create a sort by object with default order', function () { + $sortBy = new SortBy('metadata.title'); + + expect($sortBy->toArray())->toBe([ + 'property' => 'metadata.title', + 'order' => 'ASC' + ]); + }); + + it('should create a sort by object with DESC order', function () { + $sortBy = new SortBy('name', SortByOrder::DESC); + + expect($sortBy->toArray())->toBe([ + 'property' => 'name', + 'order' => 'DESC' + ]); + }); +}); diff --git a/tests/Unit/WhereTest.php b/tests/Unit/WhereTest.php new file mode 100644 index 0000000..7fdbfb2 --- /dev/null +++ b/tests/Unit/WhereTest.php @@ -0,0 +1,98 @@ +toArray())->toBe([ + 'name' => [ + 'eq' => 'mock-expected-value' + ] + ]); + }); + + it('should throw an exception when invalid operator is passed', function () { + $closure = function () { + new Where('name', 'INVALID', 'mock-expected-value'); + }; + + expect($closure)->toThrow(new \InvalidArgumentException('Invalid operator INVALID')); + }); + + it('should throw an exception when value is not an array for BETWEEN operator', function () { + $closure = function () { + new Where('age', WhereOperator::BETWEEN, 20); + }; + + expect($closure)->toThrow(new \InvalidArgumentException('Where $value parameter must be an array')); + }); + + it('should throw an exception when value is not an array for IN operator', function () { + $closure = function () { + new Where('age', WhereOperator::IN, 20); + }; + + expect($closure)->toThrow(new \InvalidArgumentException('Where $value parameter must be an array')); + }); + + it('should throw an exception when value is not an array for NIN operator', function () { + $closure = function () { + new Where('age', WhereOperator::NIN, 20); + }; + + expect($closure)->toThrow(new \InvalidArgumentException('Where $value parameter must be an array')); + }); + + it('should return the expected array for BETWEEN operator', function () { + $where = new Where('age', WhereOperator::BETWEEN, [20, 30]); + + expect($where->toArray())->toBe([ + 'age' => [ + 'between' => [20, 30] + ] + ]); + }); + + it('should return the expected array for IN operator', function () { + $where = new Where('years', WhereOperator::IN, [1984, 1994, 2004]); + + expect($where->toArray())->toBe([ + 'years' => [ + 'in' => [1984, 1994, 2004] + ] + ]); + }); + + it('should return the expected array for NIN operator', function () { + $where = new Where('age', WhereOperator::NIN, [20, 30]); + + expect($where->toArray())->toBe([ + 'age' => [ + 'nin' => [20, 30] + ] + ]); + }); + + it('should return the expected array for GT operator', function () { + $where = new Where('age', WhereOperator::GT, 20); + + expect($where->toArray())->toBe([ + 'age' => [ + 'gt' => 20 + ] + ]); + }); + + it('should return the expected array for EQ operator', function () { + $where = new Where('year', WhereOperator::EQ, 2024); + + expect($where->toArray())->toBe([ + 'year' => [ + 'eq' => 2024 + ] + ]); + }); +});