diff --git a/readme.md b/readme.md index a46b0c8d..c1c25f82 100644 --- a/readme.md +++ b/readme.md @@ -61,14 +61,14 @@ That's all, just search 😃 query { # WHERE name = "LastDragon" users(where: { - name: {eq: "LastDragon"} + name: {equal: "LastDragon"} }) { id } # WHERE name != "LastDragon" users(where: { - name: {eq: "LastDragon", not: yes} + name: {notEqual: "LastDragon"} }) { id } @@ -76,8 +76,8 @@ query { # WHERE name = "LastDragon" or name = "Aleksei" users(where: { anyOf: [ - {name: {eq: "LastDragon"}} - {name: {eq: "Aleksei"}} + {name: {equal: "LastDragon"}} + {name: {equal: "Aleksei"}} ] }) { id @@ -86,8 +86,8 @@ query { # WHERE NOT (name = "LastDragon" or name = "Aleksei") users(where: { anyOf: [ - {name: {eq: "LastDragon"}} - {name: {eq: "Aleksei"}} + {name: {equal: "LastDragon"}} + {name: {equal: "Aleksei"}} ] not: yes }) { @@ -118,7 +118,7 @@ query { where: { date: {between: {min: "2021-01-01", max: "2021-04-01"}} } - eq: 2 + equal: 2 } }) { id @@ -154,7 +154,7 @@ input SearchByConditionCommentsQuery { anyOf: [SearchByConditionCommentsQuery!] """Not.""" - not: SearchByFlag + not: SearchByConditionCommentsQuery """Property condition.""" text: SearchByScalarString @@ -177,7 +177,7 @@ input SearchByConditionUsersQuery { anyOf: [SearchByConditionUsersQuery!] """Not.""" - not: SearchByFlag + not: SearchByConditionUsersQuery """Property condition.""" id: SearchByScalarID @@ -193,11 +193,30 @@ enum SearchByFlag { """Relation condition for input UsersQuery.""" input SearchByRelationUsersQuery { - """Conditions for the related objects.""" + """ + Conditions for the related objects (`has()`). + + See also: + * https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-existence + * https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-absence + """ where: SearchByConditionUsersQuery! + """ + Shortcut for `doesntHave()`, same as: + + \``` + where: [...] + lt: 1 + \``` + """ + not: Boolean! = false + """Equal (`=`).""" - eq: Int + equal: Int + + """Not Equal (`!=`).""" + notEqual: Int """Less than (`<`).""" lt: Int @@ -210,9 +229,6 @@ input SearchByRelationUsersQuery { """Greater than or equal to (`>=`).""" gte: Int - - """Not.""" - not: SearchByFlag } input SearchByScalarDateOperatorBetween { @@ -220,12 +236,20 @@ input SearchByScalarDateOperatorBetween { max: Date! } +input SearchByScalarDateOperatorNotBetween { + min: Date! + max: Date! +} + """ Available operators for scalar Date (only one operator allowed at a time). """ input SearchByScalarDateOrNull { """Equal (`=`).""" - eq: Date + equal: Date + + """Not Equal (`!=`).""" + notEqual: Date """Less than (`<`).""" lt: Date @@ -242,14 +266,20 @@ input SearchByScalarDateOrNull { """Within a set of values.""" in: [Date!] + """Outside a set of values.""" + notIn: [Date!] + """Within a range.""" between: SearchByScalarDateOperatorBetween + """Outside a range.""" + notBetween: SearchByScalarDateOperatorNotBetween + """Is NULL?""" isNull: SearchByFlag - """Not.""" - not: SearchByFlag + """Is NOT NULL?""" + isNotNull: SearchByFlag } """ @@ -257,13 +287,16 @@ Available operators for scalar ID! (only one operator allowed at a time). """ input SearchByScalarID { """Equal (`=`).""" - eq: ID + equal: ID + + """Not Equal (`!=`).""" + notEqual: ID """Within a set of values.""" in: [ID!] - """Not.""" - not: SearchByFlag + """Outside a set of values.""" + notIn: [ID!] } """ @@ -271,22 +304,29 @@ Available operators for scalar String! (only one operator allowed at a time). """ input SearchByScalarString { """Equal (`=`).""" - eq: String + equal: String + + """Not Equal (`!=`).""" + notEqual: String """Like.""" like: String + """Not like.""" + notLike: String + """Within a set of values.""" in: [String!] - """Not.""" - not: SearchByFlag + """Outside a set of values.""" + notIn: [String!] } input UsersQuery { id: ID! name: String! } + ``` diff --git a/src/SearchBy/AstManipulator.php b/src/SearchBy/AstManipulator.php index 0cb8cd55..444eda3f 100644 --- a/src/SearchBy/AstManipulator.php +++ b/src/SearchBy/AstManipulator.php @@ -10,24 +10,21 @@ use GraphQL\Language\AST\ScalarTypeDefinitionNode; use GraphQL\Language\Parser; use Illuminate\Contracts\Container\Container; -use Illuminate\Support\Arr; use Illuminate\Support\Str; use LastDragon_ru\LaraASP\GraphQL\AstManipulator as BaseAstManipulator; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\Operator; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\OperatorHasTypes; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\OperatorHasTypesForScalar; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\OperatorHasTypesForScalarNullable; -use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\OperatorNegationable; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Comparison\IsNotNull; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Comparison\IsNull; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Complex\Relation; -use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Not; use Nuwave\Lighthouse\Schema\AST\ASTHelper; use Nuwave\Lighthouse\Schema\AST\DocumentAST; use function array_map; use function array_merge; use function implode; -use function is_a; use function is_array; use function is_null; use function sprintf; @@ -394,11 +391,7 @@ protected function getScalarOperators(string $scalar, bool $nullable): array { // Add `null` for nullable if ($nullable) { $operators[] = IsNull::class; - } - - // Add `not` for negationable - if (Arr::first($operators, static fn(string $o) => is_a($o, OperatorNegationable::class, true))) { - $operators[] = Not::class; + $operators[] = IsNotNull::class; } // Return diff --git a/src/SearchBy/Contracts/OperatorNegationable.php b/src/SearchBy/Contracts/OperatorNegationable.php deleted file mode 100644 index 478d75d4..00000000 --- a/src/SearchBy/Contracts/OperatorNegationable.php +++ /dev/null @@ -1,7 +0,0 @@ - [ Equal::class, + NotEqual::class, In::class, + NotIn::class, ], 'Int' => [ Equal::class, + NotEqual::class, LessThan::class, LessThanOrEqual::class, GreaterThan::class, GreaterThanOrEqual::class, In::class, + NotIn::class, Between::class, + NotBetween::class, ], 'Float' => 'Int', 'Boolean' => [ @@ -69,22 +78,29 @@ class Directive extends BaseDirective implements ArgManipulator, ArgBuilderDirec ], 'String' => [ Equal::class, + NotEqual::class, Like::class, + NotLike::class, In::class, + NotIn::class, ], // Special types self::Enum => [ Equal::class, + NotEqual::class, In::class, + NotIn::class, ], self::Logic => [ AllOf::class, AnyOf::class, + Not::class, ], self::Relation => [ Relation::class, Equal::class, + NotEqual::class, LessThan::class, LessThanOrEqual::class, GreaterThan::class, @@ -146,7 +162,6 @@ public function handleBuilder($builder, $value): EloquentBuilder|QueryBuilder { return (new SearchBuilder( $this->translator, (new Collection($this->scalars)) - ->add(Not::class) ->flatten() ->unique() ->filter(static function (string $operator): bool { diff --git a/src/SearchBy/DirectiveTest.php b/src/SearchBy/DirectiveTest.php index 18f2cffd..c17d6cf6 100644 --- a/src/SearchBy/DirectiveTest.php +++ b/src/SearchBy/DirectiveTest.php @@ -70,25 +70,24 @@ public function dataProviderHandleBuilder(): array { 'valid condition' => [ true, [ - 'not' => 'yes', - 'allOf' => [ - [ - 'a' => [ - 'eq' => 1, - 'not' => 'yes', + 'not' => [ + 'allOf' => [ + [ + 'a' => [ + 'notEqual' => 1, + ], ], - ], - [ - 'anyOf' => [ - [ - 'a' => [ - 'eq' => 2, + [ + 'anyOf' => [ + [ + 'a' => [ + 'equal' => 2, + ], ], - ], - [ - 'b' => [ - 'eq' => 3, - 'not' => 'yes', + [ + 'b' => [ + 'notEqual' => 3, + ], ], ], ], diff --git a/src/SearchBy/DirectiveTest~full-expected.graphql b/src/SearchBy/DirectiveTest~full-expected.graphql index 1c36e359..0d315cc1 100644 --- a/src/SearchBy/DirectiveTest~full-expected.graphql +++ b/src/SearchBy/DirectiveTest~full-expected.graphql @@ -34,7 +34,7 @@ input SearchByConditionNested { anyOf: [SearchByConditionNested!] """Not.""" - not: SearchByFlag + not: SearchByConditionNested """Property condition.""" value: SearchByScalarStringOrNull @@ -51,7 +51,7 @@ input SearchByConditionProperties { anyOf: [SearchByConditionProperties!] """Not.""" - not: SearchByFlag + not: SearchByConditionProperties """Property condition.""" idScalar: SearchByScalarIDOrNull @@ -101,13 +101,16 @@ Available operators for enum Value (only one operator allowed at a time). """ input SearchByEnumValue { """Equal (`=`).""" - eq: Value + equal: Value + + """Not Equal (`!=`).""" + notEqual: Value """Within a set of values.""" in: [Value!] - """Not.""" - not: SearchByFlag + """Outside a set of values.""" + notIn: [Value!] } """ @@ -115,16 +118,22 @@ Available operators for enum Value (only one operator allowed at a time). """ input SearchByEnumValueOrNull { """Equal (`=`).""" - eq: Value + equal: Value + + """Not Equal (`!=`).""" + notEqual: Value """Within a set of values.""" in: [Value!] + """Outside a set of values.""" + notIn: [Value!] + """Is NULL?""" isNull: SearchByFlag - """Not.""" - not: SearchByFlag + """Is NOT NULL?""" + isNotNull: SearchByFlag } """Flag.""" @@ -134,11 +143,30 @@ enum SearchByFlag { """Relation condition for input Nested.""" input SearchByRelationNested { - """Conditions for the related objects.""" + """ + Conditions for the related objects (`has()`). + + See also: + * https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-existence + * https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-absence + """ where: SearchByConditionNested! + """ + Shortcut for `doesntHave()`, same as: + + ``` + where: [...] + lt: 1 + ``` + """ + not: Boolean! = false + """Equal (`=`).""" - eq: Int + equal: Int + + """Not Equal (`!=`).""" + notEqual: Int """Less than (`<`).""" lt: Int @@ -151,9 +179,6 @@ input SearchByRelationNested { """Greater than or equal to (`>=`).""" gte: Int - - """Not.""" - not: SearchByFlag } """ @@ -161,10 +186,7 @@ Available operators for scalar Boolean! (only one operator allowed at a time). """ input SearchByScalarBoolean { """Equal (`=`).""" - eq: Boolean - - """Not.""" - not: SearchByFlag + equal: Boolean } """ @@ -172,13 +194,13 @@ Available operators for scalar Boolean (only one operator allowed at a time). """ input SearchByScalarBooleanOrNull { """Equal (`=`).""" - eq: Boolean + equal: Boolean """Is NULL?""" isNull: SearchByFlag - """Not.""" - not: SearchByFlag + """Is NOT NULL?""" + isNotNull: SearchByFlag } """ @@ -186,7 +208,10 @@ Available operators for scalar Float! (only one operator allowed at a time). """ input SearchByScalarFloat { """Equal (`=`).""" - eq: Float + equal: Float + + """Not Equal (`!=`).""" + notEqual: Float """Less than (`<`).""" lt: Float @@ -203,11 +228,14 @@ input SearchByScalarFloat { """Within a set of values.""" in: [Float!] + """Outside a set of values.""" + notIn: [Float!] + """Within a range.""" between: SearchByScalarFloatOperatorBetween - """Not.""" - not: SearchByFlag + """Outside a range.""" + notBetween: SearchByScalarFloatOperatorNotBetween } input SearchByScalarFloatOperatorBetween { @@ -215,12 +243,20 @@ input SearchByScalarFloatOperatorBetween { max: Float! } +input SearchByScalarFloatOperatorNotBetween { + min: Float! + max: Float! +} + """ Available operators for scalar Float (only one operator allowed at a time). """ input SearchByScalarFloatOrNull { """Equal (`=`).""" - eq: Float + equal: Float + + """Not Equal (`!=`).""" + notEqual: Float """Less than (`<`).""" lt: Float @@ -237,14 +273,20 @@ input SearchByScalarFloatOrNull { """Within a set of values.""" in: [Float!] + """Outside a set of values.""" + notIn: [Float!] + """Within a range.""" between: SearchByScalarFloatOperatorBetween + """Outside a range.""" + notBetween: SearchByScalarFloatOperatorNotBetween + """Is NULL?""" isNull: SearchByFlag - """Not.""" - not: SearchByFlag + """Is NOT NULL?""" + isNotNull: SearchByFlag } """ @@ -252,13 +294,16 @@ Available operators for scalar ID! (only one operator allowed at a time). """ input SearchByScalarID { """Equal (`=`).""" - eq: ID + equal: ID + + """Not Equal (`!=`).""" + notEqual: ID """Within a set of values.""" in: [ID!] - """Not.""" - not: SearchByFlag + """Outside a set of values.""" + notIn: [ID!] } """ @@ -266,16 +311,22 @@ Available operators for scalar ID (only one operator allowed at a time). """ input SearchByScalarIDOrNull { """Equal (`=`).""" - eq: ID + equal: ID + + """Not Equal (`!=`).""" + notEqual: ID """Within a set of values.""" in: [ID!] + """Outside a set of values.""" + notIn: [ID!] + """Is NULL?""" isNull: SearchByFlag - """Not.""" - not: SearchByFlag + """Is NOT NULL?""" + isNotNull: SearchByFlag } """ @@ -283,7 +334,10 @@ Available operators for scalar Int! (only one operator allowed at a time). """ input SearchByScalarInt { """Equal (`=`).""" - eq: Int + equal: Int + + """Not Equal (`!=`).""" + notEqual: Int """Less than (`<`).""" lt: Int @@ -300,11 +354,14 @@ input SearchByScalarInt { """Within a set of values.""" in: [Int!] + """Outside a set of values.""" + notIn: [Int!] + """Within a range.""" between: SearchByScalarIntOperatorBetween - """Not.""" - not: SearchByFlag + """Outside a range.""" + notBetween: SearchByScalarIntOperatorNotBetween } input SearchByScalarIntOperatorBetween { @@ -312,12 +369,20 @@ input SearchByScalarIntOperatorBetween { max: Int! } +input SearchByScalarIntOperatorNotBetween { + min: Int! + max: Int! +} + """ Available operators for scalar Int (only one operator allowed at a time). """ input SearchByScalarIntOrNull { """Equal (`=`).""" - eq: Int + equal: Int + + """Not Equal (`!=`).""" + notEqual: Int """Less than (`<`).""" lt: Int @@ -334,14 +399,20 @@ input SearchByScalarIntOrNull { """Within a set of values.""" in: [Int!] + """Outside a set of values.""" + notIn: [Int!] + """Within a range.""" between: SearchByScalarIntOperatorBetween + """Outside a range.""" + notBetween: SearchByScalarIntOperatorNotBetween + """Is NULL?""" isNull: SearchByFlag - """Not.""" - not: SearchByFlag + """Is NOT NULL?""" + isNotNull: SearchByFlag } """ @@ -349,16 +420,22 @@ Available operators for scalar String! (only one operator allowed at a time). """ input SearchByScalarString { """Equal (`=`).""" - eq: String + equal: String + + """Not Equal (`!=`).""" + notEqual: String """Like.""" like: String + """Not like.""" + notLike: String + """Within a set of values.""" in: [String!] - """Not.""" - not: SearchByFlag + """Outside a set of values.""" + notIn: [String!] } """ @@ -366,19 +443,28 @@ Available operators for scalar String (only one operator allowed at a time). """ input SearchByScalarStringOrNull { """Equal (`=`).""" - eq: String + equal: String + + """Not Equal (`!=`).""" + notEqual: String """Like.""" like: String + """Not like.""" + notLike: String + """Within a set of values.""" in: [String!] + """Outside a set of values.""" + notIn: [String!] + """Is NULL?""" isNull: SearchByFlag - """Not.""" - not: SearchByFlag + """Is NOT NULL?""" + isNotNull: SearchByFlag } enum Value { diff --git a/src/SearchBy/DirectiveTest~usedonly-expected.graphql b/src/SearchBy/DirectiveTest~usedonly-expected.graphql index 80ccffc3..619f5cf8 100644 --- a/src/SearchBy/DirectiveTest~usedonly-expected.graphql +++ b/src/SearchBy/DirectiveTest~usedonly-expected.graphql @@ -17,7 +17,7 @@ input SearchByConditionProperties { anyOf: [SearchByConditionProperties!] """Not.""" - not: SearchByFlag + not: SearchByConditionProperties """Property condition.""" value: SearchByScalarString @@ -33,14 +33,20 @@ Available operators for scalar String! (only one operator allowed at a time). """ input SearchByScalarString { """Equal (`=`).""" - eq: String + equal: String + + """Not Equal (`!=`).""" + notEqual: String """Like.""" like: String + """Not like.""" + notLike: String + """Within a set of values.""" in: [String!] - """Not.""" - not: SearchByFlag + """Outside a set of values.""" + notIn: [String!] } diff --git a/src/SearchBy/Operators/Comparison/Between.php b/src/SearchBy/Operators/Comparison/Between.php index 8b00f522..dea6392b 100644 --- a/src/SearchBy/Operators/Comparison/Between.php +++ b/src/SearchBy/Operators/Comparison/Between.php @@ -6,10 +6,9 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\Builder as QueryBuilder; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\OperatorHasTypesForScalar; -use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\OperatorNegationable; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\BaseOperator; -class Between extends BaseOperator implements ComparisonOperator, OperatorNegationable, OperatorHasTypesForScalar { +class Between extends BaseOperator implements ComparisonOperator, OperatorHasTypesForScalar { protected const TypeRange = 'Range'; public function getName(): string { @@ -48,10 +47,7 @@ public function apply( EloquentBuilder|QueryBuilder $builder, string $property, mixed $value, - bool $not, ): EloquentBuilder|QueryBuilder { - return $not - ? $builder->whereNotBetween($property, $value) - : $builder->whereBetween($property, $value); + return $builder->whereBetween($property, $value); } } diff --git a/src/SearchBy/Operators/Comparison/BetweenTest.php b/src/SearchBy/Operators/Comparison/BetweenTest.php index 79605e3b..ed6a24f6 100644 --- a/src/SearchBy/Operators/Comparison/BetweenTest.php +++ b/src/SearchBy/Operators/Comparison/BetweenTest.php @@ -27,11 +27,10 @@ public function testApply( Closure $builder, string $property, mixed $value, - bool $not, ): void { $operator = $this->app->make(Between::class); $builder = $builder($this); - $builder = $operator->apply($builder, $property, $value, $not); + $builder = $operator->apply($builder, $property, $value); $actual = [ 'sql' => $builder->toSql(), 'bindings' => $builder->getBindings(), @@ -50,23 +49,13 @@ public function dataProviderApply(): array { return (new CompositeDataProvider( new BuilderDataProvider(), new ArrayDataProvider([ - 'between' => [ + 'between' => [ [ 'sql' => 'select * from "tmp" where "property" between ? and ?', 'bindings' => [1, 2], ], 'property', [1, 2, 3], - false, - ], - 'not between' => [ - [ - 'sql' => 'select * from "tmp" where "property" not between ? and ?', - 'bindings' => [1, 2], - ], - 'property', - [1, 2, 3], - true, ], ]), ))->getData(); diff --git a/src/SearchBy/Operators/Comparison/ComparisonOperator.php b/src/SearchBy/Operators/Comparison/ComparisonOperator.php index fee78951..b8cf8efd 100644 --- a/src/SearchBy/Operators/Comparison/ComparisonOperator.php +++ b/src/SearchBy/Operators/Comparison/ComparisonOperator.php @@ -10,6 +10,5 @@ public function apply( EloquentBuilder|QueryBuilder $builder, string $property, mixed $value, - bool $not, ): EloquentBuilder|QueryBuilder; } diff --git a/src/SearchBy/Operators/Comparison/Equal.php b/src/SearchBy/Operators/Comparison/Equal.php index bf31613e..487e6a18 100644 --- a/src/SearchBy/Operators/Comparison/Equal.php +++ b/src/SearchBy/Operators/Comparison/Equal.php @@ -4,12 +4,11 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\Builder as QueryBuilder; -use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\OperatorNegationable; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\BaseOperator; -class Equal extends BaseOperator implements ComparisonOperator, OperatorNegationable { +class Equal extends BaseOperator implements ComparisonOperator { public function getName(): string { - return 'eq'; + return 'equal'; } protected function getDescription(): string { @@ -20,10 +19,7 @@ public function apply( EloquentBuilder|QueryBuilder $builder, string $property, mixed $value, - bool $not, ): EloquentBuilder|QueryBuilder { - return $not - ? $builder->where($property, '!=', $value) - : $builder->where($property, '=', $value); + return $builder->where($property, '=', $value); } } diff --git a/src/SearchBy/Operators/Comparison/EqualTest.php b/src/SearchBy/Operators/Comparison/EqualTest.php index 6069cd9d..3aac28d9 100644 --- a/src/SearchBy/Operators/Comparison/EqualTest.php +++ b/src/SearchBy/Operators/Comparison/EqualTest.php @@ -27,11 +27,10 @@ public function testApply( Closure $builder, string $property, mixed $value, - bool $not, ): void { $operator = $this->app->make(Equal::class); $builder = $builder($this); - $builder = $operator->apply($builder, $property, $value, $not); + $builder = $operator->apply($builder, $property, $value); $actual = [ 'sql' => $builder->toSql(), 'bindings' => $builder->getBindings(), @@ -50,23 +49,13 @@ public function dataProviderApply(): array { return (new CompositeDataProvider( new BuilderDataProvider(), new ArrayDataProvider([ - 'equal' => [ + 'equal' => [ [ 'sql' => 'select * from "tmp" where "property" = ?', 'bindings' => ['abc'], ], 'property', 'abc', - false, - ], - 'not equal' => [ - [ - 'sql' => 'select * from "tmp" where "property" != ?', - 'bindings' => ['abc'], - ], - 'property', - 'abc', - true, ], ]), ))->getData(); diff --git a/src/SearchBy/Operators/Comparison/GreaterThan.php b/src/SearchBy/Operators/Comparison/GreaterThan.php index a0b32117..4adabe0c 100644 --- a/src/SearchBy/Operators/Comparison/GreaterThan.php +++ b/src/SearchBy/Operators/Comparison/GreaterThan.php @@ -19,7 +19,6 @@ public function apply( EloquentBuilder|QueryBuilder $builder, string $property, mixed $value, - bool $not, ): EloquentBuilder|QueryBuilder { return $builder->where($property, '>', $value); } diff --git a/src/SearchBy/Operators/Comparison/GreaterThanOrEqual.php b/src/SearchBy/Operators/Comparison/GreaterThanOrEqual.php index f6fcf988..720da2b0 100644 --- a/src/SearchBy/Operators/Comparison/GreaterThanOrEqual.php +++ b/src/SearchBy/Operators/Comparison/GreaterThanOrEqual.php @@ -19,7 +19,6 @@ public function apply( EloquentBuilder|QueryBuilder $builder, string $property, mixed $value, - bool $not, ): EloquentBuilder|QueryBuilder { return $builder->where($property, '>=', $value); } diff --git a/src/SearchBy/Operators/Comparison/GreaterThanOrEqualTest.php b/src/SearchBy/Operators/Comparison/GreaterThanOrEqualTest.php index fd4b04e4..f63d3a86 100644 --- a/src/SearchBy/Operators/Comparison/GreaterThanOrEqualTest.php +++ b/src/SearchBy/Operators/Comparison/GreaterThanOrEqualTest.php @@ -27,11 +27,10 @@ public function testApply( Closure $builder, string $property, mixed $value, - bool $not, ): void { $operator = $this->app->make(GreaterThanOrEqual::class); $builder = $builder($this); - $builder = $operator->apply($builder, $property, $value, $not); + $builder = $operator->apply($builder, $property, $value); $actual = [ 'sql' => $builder->toSql(), 'bindings' => $builder->getBindings(), @@ -59,15 +58,6 @@ public function dataProviderApply(): array { 123, false, ], - '"not" not supported' => [ - [ - 'sql' => 'select * from "tmp" where "property" >= ?', - 'bindings' => ['abc'], - ], - 'property', - 'abc', - true, - ], ]), ))->getData(); } diff --git a/src/SearchBy/Operators/Comparison/GreaterThanTest.php b/src/SearchBy/Operators/Comparison/GreaterThanTest.php index 011c13c7..55179d80 100644 --- a/src/SearchBy/Operators/Comparison/GreaterThanTest.php +++ b/src/SearchBy/Operators/Comparison/GreaterThanTest.php @@ -27,11 +27,10 @@ public function testApply( Closure $builder, string $property, mixed $value, - bool $not, ): void { $operator = $this->app->make(GreaterThan::class); $builder = $builder($this); - $builder = $operator->apply($builder, $property, $value, $not); + $builder = $operator->apply($builder, $property, $value); $actual = [ 'sql' => $builder->toSql(), 'bindings' => $builder->getBindings(), @@ -50,7 +49,7 @@ public function dataProviderApply(): array { return (new CompositeDataProvider( new BuilderDataProvider(), new ArrayDataProvider([ - 'greater than' => [ + 'greater than' => [ [ 'sql' => 'select * from "tmp" where "property" > ?', 'bindings' => [123], @@ -59,15 +58,6 @@ public function dataProviderApply(): array { 123, false, ], - '"not" not supported' => [ - [ - 'sql' => 'select * from "tmp" where "property" > ?', - 'bindings' => ['abc'], - ], - 'property', - 'abc', - true, - ], ]), ))->getData(); } diff --git a/src/SearchBy/Operators/Comparison/In.php b/src/SearchBy/Operators/Comparison/In.php index 05a14d4a..4bed4dc4 100644 --- a/src/SearchBy/Operators/Comparison/In.php +++ b/src/SearchBy/Operators/Comparison/In.php @@ -4,10 +4,9 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\Builder as QueryBuilder; -use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\OperatorNegationable; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\BaseOperator; -class In extends BaseOperator implements ComparisonOperator, OperatorNegationable { +class In extends BaseOperator implements ComparisonOperator { public function getName(): string { return 'in'; } @@ -27,10 +26,7 @@ public function apply( EloquentBuilder|QueryBuilder $builder, string $property, mixed $value, - bool $not, ): EloquentBuilder|QueryBuilder { - return $not - ? $builder->whereNotIn($property, $value) - : $builder->whereIn($property, $value); + return $builder->whereIn($property, $value); } } diff --git a/src/SearchBy/Operators/Comparison/InTest.php b/src/SearchBy/Operators/Comparison/InTest.php index 1d861299..ad292a1a 100644 --- a/src/SearchBy/Operators/Comparison/InTest.php +++ b/src/SearchBy/Operators/Comparison/InTest.php @@ -27,11 +27,10 @@ public function testApply( Closure $builder, string $property, mixed $value, - bool $not, ): void { $operator = $this->app->make(In::class); $builder = $builder($this); - $builder = $operator->apply($builder, $property, $value, $not); + $builder = $operator->apply($builder, $property, $value); $actual = [ 'sql' => $builder->toSql(), 'bindings' => $builder->getBindings(), @@ -50,7 +49,7 @@ public function dataProviderApply(): array { return (new CompositeDataProvider( new BuilderDataProvider(), new ArrayDataProvider([ - 'in' => [ + 'in' => [ [ 'sql' => 'select * from "tmp" where "property" in (?, ?, ?)', 'bindings' => ['abc', 2, 4], @@ -59,15 +58,6 @@ public function dataProviderApply(): array { ['abc', 2, 4], false, ], - 'not in' => [ - [ - 'sql' => 'select * from "tmp" where "property" not in (?, ?, ?)', - 'bindings' => ['abc', 2, 4], - ], - 'property', - ['abc', 2, 4], - true, - ], ]), ))->getData(); } diff --git a/src/SearchBy/Operators/Not.php b/src/SearchBy/Operators/Comparison/IsNotNull.php similarity index 50% rename from src/SearchBy/Operators/Not.php rename to src/SearchBy/Operators/Comparison/IsNotNull.php index b0fb4270..4ba24d23 100644 --- a/src/SearchBy/Operators/Not.php +++ b/src/SearchBy/Operators/Comparison/IsNotNull.php @@ -1,26 +1,22 @@ where($nested, null, null, 'and not'); + public function apply( + EloquentBuilder|QueryBuilder $builder, + string $property, + mixed $value, + ): EloquentBuilder|QueryBuilder { + return $builder->whereNotNull($property); } } diff --git a/src/SearchBy/Operators/NotTest.php b/src/SearchBy/Operators/Comparison/IsNotNullTest.php similarity index 69% rename from src/SearchBy/Operators/NotTest.php rename to src/SearchBy/Operators/Comparison/IsNotNullTest.php index d882bd06..5e3695dd 100644 --- a/src/SearchBy/Operators/NotTest.php +++ b/src/SearchBy/Operators/Comparison/IsNotNullTest.php @@ -1,10 +1,8 @@ // ========================================================================= /** @@ -24,10 +22,15 @@ class NotTest extends TestCase { * * @param array{sql: string, bindings: array} $expected */ - public function testApply(array $expected, Closure $builder, Closure $nested): void { - $operator = $this->app->make(Not::class); + public function testApply( + array $expected, + Closure $builder, + string $property, + mixed $value, + ): void { + $operator = $this->app->make(IsNotNull::class); $builder = $builder($this); - $builder = $operator->apply($builder, $nested); + $builder = $operator->apply($builder, $property, $value); $actual = [ 'sql' => $builder->toSql(), 'bindings' => $builder->getBindings(), @@ -46,14 +49,14 @@ public function dataProviderApply(): array { return (new CompositeDataProvider( new BuilderDataProvider(), new ArrayDataProvider([ - 'not' => [ + 'ok' => [ [ - 'sql' => 'select * from "tmp" where not (1 = 1)', + 'sql' => 'select * from "tmp" where "property" is not null', 'bindings' => [], ], - static function (EloquentBuilder|QueryBuilder $builder): EloquentBuilder|QueryBuilder { - return $builder->whereRaw('1 = 1'); - }, + 'property', + null, + true, ], ]), ))->getData(); diff --git a/src/SearchBy/Operators/Comparison/IsNull.php b/src/SearchBy/Operators/Comparison/IsNull.php index be962039..26c3b1b1 100644 --- a/src/SearchBy/Operators/Comparison/IsNull.php +++ b/src/SearchBy/Operators/Comparison/IsNull.php @@ -4,14 +4,13 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\Builder as QueryBuilder; -use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\OperatorNegationable; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Directive; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\BaseOperator; /** * @internal Must not be used directly. */ -class IsNull extends BaseOperator implements ComparisonOperator, OperatorNegationable { +class IsNull extends BaseOperator implements ComparisonOperator { public function getName(): string { return 'isNull'; } @@ -31,10 +30,7 @@ public function apply( EloquentBuilder|QueryBuilder $builder, string $property, mixed $value, - bool $not, ): EloquentBuilder|QueryBuilder { - return $not - ? $builder->whereNotNull($property) - : $builder->whereNull($property); + return $builder->whereNull($property); } } diff --git a/src/SearchBy/Operators/Comparison/IsNullTest.php b/src/SearchBy/Operators/Comparison/IsNullTest.php index bcea0d68..bd4c0852 100644 --- a/src/SearchBy/Operators/Comparison/IsNullTest.php +++ b/src/SearchBy/Operators/Comparison/IsNullTest.php @@ -27,11 +27,10 @@ public function testApply( Closure $builder, string $property, mixed $value, - bool $not, ): void { $operator = $this->app->make(IsNull::class); $builder = $builder($this); - $builder = $operator->apply($builder, $property, $value, $not); + $builder = $operator->apply($builder, $property, $value); $actual = [ 'sql' => $builder->toSql(), 'bindings' => $builder->getBindings(), @@ -50,23 +49,13 @@ public function dataProviderApply(): array { return (new CompositeDataProvider( new BuilderDataProvider(), new ArrayDataProvider([ - 'is null' => [ + 'is null' => [ [ 'sql' => 'select * from "tmp" where "property" is null', 'bindings' => [], ], 'property', null, - false, - ], - 'is not null' => [ - [ - 'sql' => 'select * from "tmp" where "property" is not null', - 'bindings' => [], - ], - 'property', - null, - true, ], ]), ))->getData(); diff --git a/src/SearchBy/Operators/Comparison/LessThan.php b/src/SearchBy/Operators/Comparison/LessThan.php index ed7cc58b..b70687c1 100644 --- a/src/SearchBy/Operators/Comparison/LessThan.php +++ b/src/SearchBy/Operators/Comparison/LessThan.php @@ -19,7 +19,6 @@ public function apply( EloquentBuilder|QueryBuilder $builder, string $property, mixed $value, - bool $not, ): EloquentBuilder|QueryBuilder { return $builder->where($property, '<', $value); } diff --git a/src/SearchBy/Operators/Comparison/LessThanOrEqual.php b/src/SearchBy/Operators/Comparison/LessThanOrEqual.php index a8b1b2e6..c671d8f1 100644 --- a/src/SearchBy/Operators/Comparison/LessThanOrEqual.php +++ b/src/SearchBy/Operators/Comparison/LessThanOrEqual.php @@ -19,7 +19,6 @@ public function apply( EloquentBuilder|QueryBuilder $builder, string $property, mixed $value, - bool $not, ): EloquentBuilder|QueryBuilder { return $builder->where($property, '<=', $value); } diff --git a/src/SearchBy/Operators/Comparison/LessThanOrEqualTest.php b/src/SearchBy/Operators/Comparison/LessThanOrEqualTest.php index 795d5b59..77865a02 100644 --- a/src/SearchBy/Operators/Comparison/LessThanOrEqualTest.php +++ b/src/SearchBy/Operators/Comparison/LessThanOrEqualTest.php @@ -27,11 +27,10 @@ public function testApply( Closure $builder, string $property, mixed $value, - bool $not, ): void { $operator = $this->app->make(LessThanOrEqual::class); $builder = $builder($this); - $builder = $operator->apply($builder, $property, $value, $not); + $builder = $operator->apply($builder, $property, $value); $actual = [ 'sql' => $builder->toSql(), 'bindings' => $builder->getBindings(), @@ -50,7 +49,7 @@ public function dataProviderApply(): array { return (new CompositeDataProvider( new BuilderDataProvider(), new ArrayDataProvider([ - 'less than or equal' => [ + 'less than or equal' => [ [ 'sql' => 'select * from "tmp" where "property" <= ?', 'bindings' => [123], @@ -59,15 +58,6 @@ public function dataProviderApply(): array { 123, false, ], - '"not" not supported' => [ - [ - 'sql' => 'select * from "tmp" where "property" <= ?', - 'bindings' => ['abc'], - ], - 'property', - 'abc', - true, - ], ]), ))->getData(); } diff --git a/src/SearchBy/Operators/Comparison/LessThanTest.php b/src/SearchBy/Operators/Comparison/LessThanTest.php index a8723f6c..25c53baa 100644 --- a/src/SearchBy/Operators/Comparison/LessThanTest.php +++ b/src/SearchBy/Operators/Comparison/LessThanTest.php @@ -27,11 +27,10 @@ public function testApply( Closure $builder, string $property, mixed $value, - bool $not, ): void { $operator = $this->app->make(LessThan::class); $builder = $builder($this); - $builder = $operator->apply($builder, $property, $value, $not); + $builder = $operator->apply($builder, $property, $value); $actual = [ 'sql' => $builder->toSql(), 'bindings' => $builder->getBindings(), @@ -50,7 +49,7 @@ public function dataProviderApply(): array { return (new CompositeDataProvider( new BuilderDataProvider(), new ArrayDataProvider([ - 'less than' => [ + 'less than' => [ [ 'sql' => 'select * from "tmp" where "property" < ?', 'bindings' => [123], @@ -59,15 +58,6 @@ public function dataProviderApply(): array { 123, false, ], - '"not" not supported' => [ - [ - 'sql' => 'select * from "tmp" where "property" < ?', - 'bindings' => ['abc'], - ], - 'property', - 'abc', - true, - ], ]), ))->getData(); } diff --git a/src/SearchBy/Operators/Comparison/Like.php b/src/SearchBy/Operators/Comparison/Like.php index e73ac497..9e76014c 100644 --- a/src/SearchBy/Operators/Comparison/Like.php +++ b/src/SearchBy/Operators/Comparison/Like.php @@ -4,10 +4,9 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\Builder as QueryBuilder; -use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\OperatorNegationable; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\BaseOperator; -class Like extends BaseOperator implements ComparisonOperator, OperatorNegationable { +class Like extends BaseOperator implements ComparisonOperator { public function getName(): string { return 'like'; } @@ -20,10 +19,7 @@ public function apply( EloquentBuilder|QueryBuilder $builder, string $property, mixed $value, - bool $not, ): EloquentBuilder|QueryBuilder { - return $not - ? $builder->where($property, 'not like', $value) - : $builder->where($property, 'like', $value); + return $builder->where($property, 'like', $value); } } diff --git a/src/SearchBy/Operators/Comparison/LikeTest.php b/src/SearchBy/Operators/Comparison/LikeTest.php index 769d9913..586990ff 100644 --- a/src/SearchBy/Operators/Comparison/LikeTest.php +++ b/src/SearchBy/Operators/Comparison/LikeTest.php @@ -27,11 +27,10 @@ public function testApply( Closure $builder, string $property, mixed $value, - bool $not, ): void { $operator = $this->app->make(Like::class); $builder = $builder($this); - $builder = $operator->apply($builder, $property, $value, $not); + $builder = $operator->apply($builder, $property, $value); $actual = [ 'sql' => $builder->toSql(), 'bindings' => $builder->getBindings(), @@ -50,23 +49,13 @@ public function dataProviderApply(): array { return (new CompositeDataProvider( new BuilderDataProvider(), new ArrayDataProvider([ - 'like' => [ + 'like' => [ [ 'sql' => 'select * from "tmp" where "property" like ?', 'bindings' => ['abc'], ], 'property', 'abc', - false, - ], - 'not like' => [ - [ - 'sql' => 'select * from "tmp" where "property" not like ?', - 'bindings' => ['abc'], - ], - 'property', - 'abc', - true, ], ]), ))->getData(); diff --git a/src/SearchBy/Operators/Comparison/NotBetween.php b/src/SearchBy/Operators/Comparison/NotBetween.php new file mode 100644 index 00000000..847ce71b --- /dev/null +++ b/src/SearchBy/Operators/Comparison/NotBetween.php @@ -0,0 +1,25 @@ +whereNotBetween($property, $value); + } +} diff --git a/src/SearchBy/Operators/Comparison/NotBetweenTest.php b/src/SearchBy/Operators/Comparison/NotBetweenTest.php new file mode 100644 index 00000000..655999cd --- /dev/null +++ b/src/SearchBy/Operators/Comparison/NotBetweenTest.php @@ -0,0 +1,64 @@ + + // ========================================================================= + /** + * @covers ::apply + * + * @dataProvider dataProviderApply + * + * @param array{sql: string, bindings: array} $expected + */ + public function testApply( + array $expected, + Closure $builder, + string $property, + mixed $value, + ): void { + $operator = $this->app->make(NotBetween::class); + $builder = $builder($this); + $builder = $operator->apply($builder, $property, $value); + $actual = [ + 'sql' => $builder->toSql(), + 'bindings' => $builder->getBindings(), + ]; + + $this->assertEquals($expected, $actual); + } + // + + // + // ========================================================================= + /** + * @return array + */ + public function dataProviderApply(): array { + return (new CompositeDataProvider( + new BuilderDataProvider(), + new ArrayDataProvider([ + 'ok' => [ + [ + 'sql' => 'select * from "tmp" where "property" not between ? and ?', + 'bindings' => [1, 2], + ], + 'property', + [1, 2, 3], + ], + ]), + ))->getData(); + } + // +} diff --git a/src/SearchBy/Operators/Comparison/NotEqual.php b/src/SearchBy/Operators/Comparison/NotEqual.php new file mode 100644 index 00000000..19332ba2 --- /dev/null +++ b/src/SearchBy/Operators/Comparison/NotEqual.php @@ -0,0 +1,25 @@ +where($property, '!=', $value); + } +} diff --git a/src/SearchBy/Operators/Comparison/NotEqualTest.php b/src/SearchBy/Operators/Comparison/NotEqualTest.php new file mode 100644 index 00000000..624ee73c --- /dev/null +++ b/src/SearchBy/Operators/Comparison/NotEqualTest.php @@ -0,0 +1,64 @@ + + // ========================================================================= + /** + * @covers ::apply + * + * @dataProvider dataProviderApply + * + * @param array{sql: string, bindings: array} $expected + */ + public function testApply( + array $expected, + Closure $builder, + string $property, + mixed $value, + ): void { + $operator = $this->app->make(NotEqual::class); + $builder = $builder($this); + $builder = $operator->apply($builder, $property, $value); + $actual = [ + 'sql' => $builder->toSql(), + 'bindings' => $builder->getBindings(), + ]; + + $this->assertEquals($expected, $actual); + } + // + + // + // ========================================================================= + /** + * @return array + */ + public function dataProviderApply(): array { + return (new CompositeDataProvider( + new BuilderDataProvider(), + new ArrayDataProvider([ + 'ok' => [ + [ + 'sql' => 'select * from "tmp" where "property" != ?', + 'bindings' => ['abc'], + ], + 'property', + 'abc', + ], + ]), + ))->getData(); + } + // +} diff --git a/src/SearchBy/Operators/Comparison/NotIn.php b/src/SearchBy/Operators/Comparison/NotIn.php new file mode 100644 index 00000000..cd8d6cc8 --- /dev/null +++ b/src/SearchBy/Operators/Comparison/NotIn.php @@ -0,0 +1,32 @@ +whereNotIn($property, $value); + } +} diff --git a/src/SearchBy/Operators/Comparison/NotInTest.php b/src/SearchBy/Operators/Comparison/NotInTest.php new file mode 100644 index 00000000..c7b0ef9c --- /dev/null +++ b/src/SearchBy/Operators/Comparison/NotInTest.php @@ -0,0 +1,64 @@ + + // ========================================================================= + /** + * @covers ::apply + * + * @dataProvider dataProviderApply + * + * @param array{sql: string, bindings: array} $expected + */ + public function testApply( + array $expected, + Closure $builder, + string $property, + mixed $value, + ): void { + $operator = $this->app->make(NotIn::class); + $builder = $builder($this); + $builder = $operator->apply($builder, $property, $value); + $actual = [ + 'sql' => $builder->toSql(), + 'bindings' => $builder->getBindings(), + ]; + + $this->assertEquals($expected, $actual); + } + // + + // + // ========================================================================= + /** + * @return array + */ + public function dataProviderApply(): array { + return (new CompositeDataProvider( + new BuilderDataProvider(), + new ArrayDataProvider([ + 'ok' => [ + [ + 'sql' => 'select * from "tmp" where "property" not in (?, ?, ?)', + 'bindings' => ['abc', 2, 4], + ], + 'property', + ['abc', 2, 4], + ], + ]), + ))->getData(); + } + // +} diff --git a/src/SearchBy/Operators/Comparison/NotLike.php b/src/SearchBy/Operators/Comparison/NotLike.php new file mode 100644 index 00000000..0e4f6207 --- /dev/null +++ b/src/SearchBy/Operators/Comparison/NotLike.php @@ -0,0 +1,25 @@ +where($property, 'not like', $value); + } +} diff --git a/src/SearchBy/Operators/Comparison/NotLikeTest.php b/src/SearchBy/Operators/Comparison/NotLikeTest.php new file mode 100644 index 00000000..60886c1d --- /dev/null +++ b/src/SearchBy/Operators/Comparison/NotLikeTest.php @@ -0,0 +1,64 @@ + + // ========================================================================= + /** + * @covers ::apply + * + * @dataProvider dataProviderApply + * + * @param array{sql: string, bindings: array} $expected + */ + public function testApply( + array $expected, + Closure $builder, + string $property, + mixed $value, + ): void { + $operator = $this->app->make(NotLike::class); + $builder = $builder($this); + $builder = $operator->apply($builder, $property, $value); + $actual = [ + 'sql' => $builder->toSql(), + 'bindings' => $builder->getBindings(), + ]; + + $this->assertEquals($expected, $actual); + } + // + + // + // ========================================================================= + /** + * @return array + */ + public function dataProviderApply(): array { + return (new CompositeDataProvider( + new BuilderDataProvider(), + new ArrayDataProvider([ + 'ok' => [ + [ + 'sql' => 'select * from "tmp" where "property" not like ?', + 'bindings' => ['abc'], + ], + 'property', + 'abc', + ], + ]), + ))->getData(); + } + // +} diff --git a/src/SearchBy/Operators/Complex/Relation.php b/src/SearchBy/Operators/Complex/Relation.php index b2ae8edf..bdfdc20f 100644 --- a/src/SearchBy/Operators/Complex/Relation.php +++ b/src/SearchBy/Operators/Complex/Relation.php @@ -6,8 +6,7 @@ use Illuminate\Database\Query\Builder as QueryBuilder; use LastDragon_ru\LaraASP\GraphQL\Helpers\ModelHelper; use LastDragon_ru\LaraASP\GraphQL\PackageTranslator; -use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\OperatorNegationable; -use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\BaseOperator; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\Operator; use LastDragon_ru\LaraASP\GraphQL\SearchBy\SearchBuilder; use LastDragon_ru\LaraASP\GraphQL\SearchBy\SearchLogicException; @@ -17,26 +16,41 @@ /** * @internal Must not be used directly. */ -class Relation extends BaseOperator implements ComplexOperator, OperatorNegationable { +class Relation implements Operator, ComplexOperator { public function __construct( protected PackageTranslator $translator, ) { - parent::__construct(); + // empty } public function getName(): string { return 'where'; } - protected function getDescription(): string { - return 'Conditions for the related objects.'; - } - /** * @inheritdoc */ public function getDefinition(array $map, string $scalar, bool $nullable): string { - return parent::getDefinition($map, "{$scalar}!", true); + return <<getName()}: {$scalar}! + + """ + Shortcut for `doesntHave()`, same as: + + ``` + where: [...] + lt: 1 + ``` + """ + not: Boolean! = false + DEF; } /** @@ -60,16 +74,18 @@ public function apply( } // Possible variants: - // * has + not = doesntHave - // * has + not + operator = has + !$operator + // * where = whereHas + // * where + not = doesntHave + // * has + not + operator = error // Conditions & Not $relation = (new ModelHelper($builder))->getRelation($property); $original = $conditions; $has = $conditions[$this->getName()]; - $not = (bool) $search->getNotOperator($conditions); + $not = (bool) ($conditions['not'] ?? false); unset($conditions[$this->getName()]); + unset($conditions['not']); unset($original[$this->getName()]); // Build diff --git a/src/SearchBy/Operators/Complex/RelationTest.php b/src/SearchBy/Operators/Complex/RelationTest.php index 13455470..f0cc9b68 100644 --- a/src/SearchBy/Operators/Complex/RelationTest.php +++ b/src/SearchBy/Operators/Complex/RelationTest.php @@ -10,7 +10,7 @@ use Illuminate\Database\Query\Builder as QueryBuilder; use LastDragon_ru\LaraASP\GraphQL\PackageTranslator; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Comparison\Equal; -use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Not; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Comparison\NotEqual; use LastDragon_ru\LaraASP\GraphQL\SearchBy\SearchBuilder; use LastDragon_ru\LaraASP\GraphQL\SearchBy\SearchLogicException; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\TestCase; @@ -46,8 +46,8 @@ public function testApply( $search = new SearchBuilder( $this->app->make(PackageTranslator::class), [ - $this->app->make(Not::class), $this->app->make(Equal::class), + $this->app->make(NotEqual::class), $this->app->make(Relation::class), ], ); @@ -67,7 +67,7 @@ public function testApply( */ public function dataProviderApply(): array { return [ - 'query builder not supported' => [ + 'query builder not supported' => [ new SearchLogicException(sprintf( 'Operator `%s` cannot be used with `%s`.', 'where', @@ -79,7 +79,7 @@ static function (self $test): QueryBuilder { 'test', [], ], - 'not a relation' => [ + 'not a relation' => [ new LogicException(sprintf( 'Property `%s` is not a relation.', 'delete', @@ -90,7 +90,7 @@ static function (): EloquentBuilder { 'delete', [], ], - '{has: yes}' => [ + '{has: yes}' => [ [ 'sql' => 'select * from "table_a" where exists ('. 'select * from "table_b" '. @@ -106,7 +106,7 @@ static function (): EloquentBuilder { 'where' => 'yes', ], ], - '{has: yes, not: yes}' => [ + '{has: yes, not: yes}' => [ [ 'sql' => 'select * from "table_a" where not exists ('. 'select * from "table_b" '. @@ -120,10 +120,10 @@ static function (): EloquentBuilder { 'test', [ 'where' => 'yes', - 'not' => 'yes', + 'not' => true, ], ], - '{has: {property: {eq: 1}}}' => [ + '{has: {property: {equal: 1}}}' => [ [ 'sql' => 'select * from "table_a" where exists ('. 'select * from "table_b" where '. @@ -138,12 +138,12 @@ static function (): EloquentBuilder { [ 'where' => [ 'property' => [ - 'eq' => 123, + 'equal' => 123, ], ], ], ], - '{has: yes, eq: 1}' => [ + '{has: yes, equal: 1}' => [ [ 'sql' => 'select * from "table_a" where ('. 'select count(*) from "table_b" where '. @@ -157,30 +157,26 @@ static function (): EloquentBuilder { 'test', [ 'where' => 'yes', - 'eq' => 345, + 'equal' => 345, ], ], - '{has: yes, eq: 1, not: yes}' => [ - [ - 'sql' => 'select * from "table_a" where ('. - 'select count(*) from "table_b" '. - 'where "table_a"."id" = "table_b"."table_a_id"'. - ') != 345', - 'bindings' => [/* strange */], - ], + '{has: yes, equal: 1, not: yes}' => [ + new SearchLogicException( + 'Only one comparison operator allowed, found: `not`, `equal`', + ), static function (): EloquentBuilder { return RelationTest__ModelA::query(); }, 'test', [ 'where' => 'yes', - 'not' => 'yes', - 'eq' => 345, + 'not' => true, + 'equal' => 345, ], ], - '{has: yes, eq: 1, gt: 2}' => [ + '{has: yes, equal: 1, gt: 2}' => [ new SearchLogicException( - 'Only one comparison operator allowed, found: `eq`, `gt`', + 'Only one comparison operator allowed, found: `equal`, `gt`', ), static function (): EloquentBuilder { return RelationTest__ModelA::query(); @@ -188,11 +184,11 @@ static function (): EloquentBuilder { 'test', [ 'where' => 'yes', - 'eq' => 345, + 'equal' => 345, 'gt' => 2, ], ], - '{has: {property: {eq: 1}}} (own)' => [ + '{has: {property: {equal: 1}}} (own)' => [ [ 'sql' => 'select * from "table_a" where exists ('. 'select * from "table_a" as "table_alias_0" where '. @@ -208,7 +204,7 @@ static function (): EloquentBuilder { [ 'where' => [ 'property' => [ - 'eq' => 123, + 'equal' => 123, ], ], ], diff --git a/src/SearchBy/Operators/Logical/AllOf.php b/src/SearchBy/Operators/Logical/AllOf.php index ca8f1ab1..a39faec3 100644 --- a/src/SearchBy/Operators/Logical/AllOf.php +++ b/src/SearchBy/Operators/Logical/AllOf.php @@ -2,14 +2,15 @@ namespace LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Logical; -use Closure; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\Builder as QueryBuilder; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\BaseOperator; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\SearchBuilder; /** * @internal Must not be used directly. */ -class AllOf extends Logical { +class AllOf extends BaseOperator implements LogicalOperator { public function getName(): string { return 'allOf'; } @@ -18,7 +19,37 @@ protected function getDescription(): string { return 'All of the conditions must be true.'; } - public function apply(EloquentBuilder|QueryBuilder $builder, Closure $nested): EloquentBuilder|QueryBuilder { - return $builder->where($nested, null, null, 'and'); + /** + * @inheritdoc + */ + public function getDefinition(array $map, string $scalar, bool $nullable): string { + return parent::getDefinition($map, "[{$scalar}!]", true); + } + + /** + * @inheritDoc + */ + public function apply( + SearchBuilder $search, + EloquentBuilder|QueryBuilder $builder, + array $conditions, + ?string $tableAlias, + ): EloquentBuilder|QueryBuilder { + foreach ($conditions as $condition) { + $builder = $builder->where( + static function (EloquentBuilder|QueryBuilder $builder) use ( + $search, + $condition, + $tableAlias, + ): EloquentBuilder|QueryBuilder { + return $search->process($builder, $condition, $tableAlias); + }, + null, + null, + 'and', + ); + } + + return $builder; } } diff --git a/src/SearchBy/Operators/Logical/AllOfTest.php b/src/SearchBy/Operators/Logical/AllOfTest.php index 262307ee..9cfd6894 100644 --- a/src/SearchBy/Operators/Logical/AllOfTest.php +++ b/src/SearchBy/Operators/Logical/AllOfTest.php @@ -3,12 +3,17 @@ namespace LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Logical; use Closure; -use Illuminate\Database\Eloquent\Builder as EloquentBuilder; -use Illuminate\Database\Query\Builder as QueryBuilder; +use LastDragon_ru\LaraASP\GraphQL\PackageTranslator; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Comparison\Equal; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Comparison\NotEqual; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\SearchBuilder; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\BuilderDataProvider; +use LastDragon_ru\LaraASP\GraphQL\Testing\Package\EloquentBuilderDataProvider; +use LastDragon_ru\LaraASP\GraphQL\Testing\Package\QueryBuilderDataProvider; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\TestCase; use LastDragon_ru\LaraASP\Testing\Providers\ArrayDataProvider; use LastDragon_ru\LaraASP\Testing\Providers\CompositeDataProvider; +use LastDragon_ru\LaraASP\Testing\Providers\MergeDataProvider; /** * @internal @@ -23,12 +28,19 @@ class AllOfTest extends TestCase { * @dataProvider dataProviderApply * * @param array{sql: string, bindings: array} $expected + * @param array $conditions */ - public function testApply(array $expected, Closure $builder, Closure $nested): void { + public function testApply(array $expected, Closure $builder, array $conditions, ?string $tableAlias): void { + $search = new SearchBuilder( + $this->app->make(PackageTranslator::class), + [ + $this->app->make(Equal::class), + $this->app->make(NotEqual::class), + ], + ); $operator = $this->app->make(AllOf::class); $builder = $builder($this); - $builder = $operator->apply($builder, $nested); - $builder = $operator->apply($builder, $nested); + $builder = $operator->apply($search, $builder, $conditions, $tableAlias); $actual = [ 'sql' => $builder->toSql(), 'bindings' => $builder->getBindings(), @@ -44,20 +56,65 @@ public function testApply(array $expected, Closure $builder, Closure $nested): v * @return array */ public function dataProviderApply(): array { - return (new CompositeDataProvider( - new BuilderDataProvider(), - new ArrayDataProvider([ - 'allOf' => [ - [ - 'sql' => 'select * from "tmp" where (1 = 1) and (1 = 1)', - 'bindings' => [], + return (new MergeDataProvider([ + 'Both' => (new CompositeDataProvider( + new BuilderDataProvider(), + new ArrayDataProvider([ + 'allOf with alias' => [ + [ + 'sql' => 'select * from "tmp" where ("alias"."a" = ?) and ("alias"."b" != ?)', + 'bindings' => [ + 2, + 22, + ], + ], + [ + ['a' => ['equal' => 2]], + ['b' => ['notEqual' => 22]], + ], + 'alias', ], - static function (EloquentBuilder|QueryBuilder $builder): EloquentBuilder|QueryBuilder { - return $builder->whereRaw('1 = 1'); - }, - ], - ]), - ))->getData(); + ]), + )), + 'Query' => (new CompositeDataProvider( + new QueryBuilderDataProvider(), + new ArrayDataProvider([ + 'allOf' => [ + [ + 'sql' => 'select * from "tmp" where ("a" = ?) and ("b" != ?)', + 'bindings' => [ + 2, + 22, + ], + ], + [ + ['a' => ['equal' => 2]], + ['b' => ['notEqual' => 22]], + ], + null, + ], + ]), + )), + 'Eloquent' => (new CompositeDataProvider( + new EloquentBuilderDataProvider(), + new ArrayDataProvider([ + 'allOf' => [ + [ + 'sql' => 'select * from "tmp" where ("tmp"."a" = ?) and ("tmp"."b" != ?)', + 'bindings' => [ + 2, + 22, + ], + ], + [ + ['a' => ['equal' => 2]], + ['b' => ['notEqual' => 22]], + ], + null, + ], + ]), + )), + ]))->getData(); } // } diff --git a/src/SearchBy/Operators/Logical/AnyOf.php b/src/SearchBy/Operators/Logical/AnyOf.php index 65d252ef..753fae44 100644 --- a/src/SearchBy/Operators/Logical/AnyOf.php +++ b/src/SearchBy/Operators/Logical/AnyOf.php @@ -2,14 +2,15 @@ namespace LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Logical; -use Closure; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\Builder as QueryBuilder; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\BaseOperator; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\SearchBuilder; /** * @internal Must not be used directly. */ -class AnyOf extends Logical { +class AnyOf extends BaseOperator implements LogicalOperator { public function getName(): string { return 'anyOf'; } @@ -18,7 +19,37 @@ protected function getDescription(): string { return 'Any of the conditions must be true.'; } - public function apply(EloquentBuilder|QueryBuilder $builder, Closure $nested): EloquentBuilder|QueryBuilder { - return $builder->where($nested, null, null, 'or'); + /** + * @inheritdoc + */ + public function getDefinition(array $map, string $scalar, bool $nullable): string { + return parent::getDefinition($map, "[{$scalar}!]", true); + } + + /** + * @inheritDoc + */ + public function apply( + SearchBuilder $search, + EloquentBuilder|QueryBuilder $builder, + array $conditions, + ?string $tableAlias, + ): EloquentBuilder|QueryBuilder { + foreach ($conditions as $condition) { + $builder = $builder->where( + static function (EloquentBuilder|QueryBuilder $builder) use ( + $search, + $condition, + $tableAlias, + ): EloquentBuilder|QueryBuilder { + return $search->process($builder, $condition, $tableAlias); + }, + null, + null, + 'or', + ); + } + + return $builder; } } diff --git a/src/SearchBy/Operators/Logical/AnyOfTest.php b/src/SearchBy/Operators/Logical/AnyOfTest.php index faa71166..ae61b45e 100644 --- a/src/SearchBy/Operators/Logical/AnyOfTest.php +++ b/src/SearchBy/Operators/Logical/AnyOfTest.php @@ -3,12 +3,17 @@ namespace LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Logical; use Closure; -use Illuminate\Database\Eloquent\Builder as EloquentBuilder; -use Illuminate\Database\Query\Builder as QueryBuilder; +use LastDragon_ru\LaraASP\GraphQL\PackageTranslator; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Comparison\Equal; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Comparison\NotEqual; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\SearchBuilder; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\BuilderDataProvider; +use LastDragon_ru\LaraASP\GraphQL\Testing\Package\EloquentBuilderDataProvider; +use LastDragon_ru\LaraASP\GraphQL\Testing\Package\QueryBuilderDataProvider; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\TestCase; use LastDragon_ru\LaraASP\Testing\Providers\ArrayDataProvider; use LastDragon_ru\LaraASP\Testing\Providers\CompositeDataProvider; +use LastDragon_ru\LaraASP\Testing\Providers\MergeDataProvider; /** * @internal @@ -23,12 +28,19 @@ class AnyOfTest extends TestCase { * @dataProvider dataProviderApply * * @param array{sql: string, bindings: array} $expected + * @param array $conditions */ - public function testApply(array $expected, Closure $builder, Closure $nested): void { + public function testApply(array $expected, Closure $builder, array $conditions, ?string $tableAlias): void { + $search = new SearchBuilder( + $this->app->make(PackageTranslator::class), + [ + $this->app->make(Equal::class), + $this->app->make(NotEqual::class), + ], + ); $operator = $this->app->make(AnyOf::class); $builder = $builder($this); - $builder = $operator->apply($builder, $nested); - $builder = $operator->apply($builder, $nested); + $builder = $operator->apply($search, $builder, $conditions, $tableAlias); $actual = [ 'sql' => $builder->toSql(), 'bindings' => $builder->getBindings(), @@ -44,20 +56,65 @@ public function testApply(array $expected, Closure $builder, Closure $nested): v * @return array */ public function dataProviderApply(): array { - return (new CompositeDataProvider( - new BuilderDataProvider(), - new ArrayDataProvider([ - 'anyOf' => [ - [ - 'sql' => 'select * from "tmp" where (1 = 1) or (1 = 1)', - 'bindings' => [], + return (new MergeDataProvider([ + 'Both' => (new CompositeDataProvider( + new BuilderDataProvider(), + new ArrayDataProvider([ + 'allOf with alias' => [ + [ + 'sql' => 'select * from "tmp" where ("alias"."a" = ?) or ("alias"."b" != ?)', + 'bindings' => [ + 2, + 22, + ], + ], + [ + ['a' => ['equal' => 2]], + ['b' => ['notEqual' => 22]], + ], + 'alias', ], - static function (EloquentBuilder|QueryBuilder $builder): EloquentBuilder|QueryBuilder { - return $builder->whereRaw('1 = 1'); - }, - ], - ]), - ))->getData(); + ]), + )), + 'Query' => (new CompositeDataProvider( + new QueryBuilderDataProvider(), + new ArrayDataProvider([ + 'allOf' => [ + [ + 'sql' => 'select * from "tmp" where ("a" = ?) or ("b" != ?)', + 'bindings' => [ + 2, + 22, + ], + ], + [ + ['a' => ['equal' => 2]], + ['b' => ['notEqual' => 22]], + ], + null, + ], + ]), + )), + 'Eloquent' => (new CompositeDataProvider( + new EloquentBuilderDataProvider(), + new ArrayDataProvider([ + 'allOf' => [ + [ + 'sql' => 'select * from "tmp" where ("tmp"."a" = ?) or ("tmp"."b" != ?)', + 'bindings' => [ + 2, + 22, + ], + ], + [ + ['a' => ['equal' => 2]], + ['b' => ['notEqual' => 22]], + ], + null, + ], + ]), + )), + ]))->getData(); } // } diff --git a/src/SearchBy/Operators/Logical/Logical.php b/src/SearchBy/Operators/Logical/Logical.php deleted file mode 100644 index 7c2db955..00000000 --- a/src/SearchBy/Operators/Logical/Logical.php +++ /dev/null @@ -1,18 +0,0 @@ - $conditions + */ + public function apply( + SearchBuilder $search, + EloquentBuilder|QueryBuilder $builder, + array $conditions, + ?string $tableAlias, + ): EloquentBuilder|QueryBuilder; } diff --git a/src/SearchBy/Operators/Logical/Not.php b/src/SearchBy/Operators/Logical/Not.php new file mode 100644 index 00000000..f1d81a26 --- /dev/null +++ b/src/SearchBy/Operators/Logical/Not.php @@ -0,0 +1,41 @@ +where( + static function (EloquentBuilder|QueryBuilder $builder) use ( + $search, + $conditions, + $tableAlias, + ): EloquentBuilder|QueryBuilder { + return $search->process($builder, $conditions, $tableAlias); + }, + null, + null, + 'and not', + ); + } +} diff --git a/src/SearchBy/Operators/Logical/NotTest.php b/src/SearchBy/Operators/Logical/NotTest.php new file mode 100644 index 00000000..30d2ded0 --- /dev/null +++ b/src/SearchBy/Operators/Logical/NotTest.php @@ -0,0 +1,127 @@ + + // ========================================================================= + /** + * @covers ::apply + * + * @dataProvider dataProviderApply + * + * @param array{sql: string, bindings: array} $expected + * @param array $conditions + */ + public function testApply(array $expected, Closure $builder, array $conditions, ?string $tableAlias): void { + $search = new SearchBuilder( + $this->app->make(PackageTranslator::class), + [ + $this->app->make(AllOf::class), + $this->app->make(Equal::class), + $this->app->make(NotEqual::class), + ], + ); + $operator = $this->app->make(Not::class); + $builder = $builder($this); + $builder = $operator->apply($search, $builder, $conditions, $tableAlias); + $actual = [ + 'sql' => $builder->toSql(), + 'bindings' => $builder->getBindings(), + ]; + + $this->assertEquals($expected, $actual); + } + // + + // + // ========================================================================= + /** + * @return array + */ + public function dataProviderApply(): array { + return (new MergeDataProvider([ + 'Both' => (new CompositeDataProvider( + new BuilderDataProvider(), + new ArrayDataProvider([ + 'not with alias' => [ + [ + 'sql' => 'select * from "tmp" where not ((("alias"."a" = ?) and ("alias"."b" != ?)))', + 'bindings' => [ + 2, + 22, + ], + ], + [ + 'allOf' => [ + ['a' => ['equal' => 2]], + ['b' => ['notEqual' => 22]], + ], + ], + 'alias', + ], + ]), + )), + 'Query' => (new CompositeDataProvider( + new QueryBuilderDataProvider(), + new ArrayDataProvider([ + 'not' => [ + [ + 'sql' => 'select * from "tmp" where not ((("a" = ?) and ("b" != ?)))', + 'bindings' => [ + 2, + 22, + ], + ], + [ + 'allOf' => [ + ['a' => ['equal' => 2]], + ['b' => ['notEqual' => 22]], + ], + ], + null, + ], + ]), + )), + 'Eloquent' => (new CompositeDataProvider( + new EloquentBuilderDataProvider(), + new ArrayDataProvider([ + 'not' => [ + [ + 'sql' => 'select * from "tmp" where not ((("tmp"."a" = ?) and ("tmp"."b" != ?)))', + 'bindings' => [ + 2, + 22, + ], + ], + [ + 'allOf' => [ + ['a' => ['equal' => 2]], + ['b' => ['notEqual' => 22]], + ], + ], + null, + ], + ]), + )), + ]))->getData(); + } + // +} diff --git a/src/SearchBy/SearchBuilder.php b/src/SearchBy/SearchBuilder.php index 4498f367..85238889 100644 --- a/src/SearchBy/SearchBuilder.php +++ b/src/SearchBy/SearchBuilder.php @@ -6,11 +6,9 @@ use Illuminate\Database\Query\Builder as QueryBuilder; use InvalidArgumentException; use LastDragon_ru\LaraASP\GraphQL\PackageTranslator; -use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\OperatorNegationable; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Comparison\ComparisonOperator; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Complex\ComplexOperator; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Logical\LogicalOperator; -use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Not; use function array_keys; use function count; @@ -78,13 +76,6 @@ public function process( array $input, string $tableAlias = null, ): EloquentBuilder|QueryBuilder { - // Not? - $not = $this->getNotOperator($input); - - if ($not) { - return $this->processNotOperator($builder, $not, $input, $tableAlias); - } - // More than one property? if (count($input) > 1) { throw new SearchLogicException($this->translator->get( @@ -125,34 +116,6 @@ public function process( return $builder; } - /** - * @param array $conditions - */ - public function processNotOperator( - EloquentBuilder|QueryBuilder $builder, - Not $not, - array $conditions, - string $tableAlias = null, - ): EloquentBuilder|QueryBuilder { - return $builder->where( - function (EloquentBuilder|QueryBuilder $builder) use ( - $not, - $conditions, - $tableAlias, - ): EloquentBuilder|QueryBuilder { - return $not->apply( - $builder, - function (EloquentBuilder|QueryBuilder $builder) use ( - $conditions, - $tableAlias, - ): EloquentBuilder|QueryBuilder { - return $this->process($builder, $conditions, $tableAlias); - }, - ); - }, - ); - } - /** * @param array $conditions */ @@ -190,19 +153,7 @@ function (EloquentBuilder|QueryBuilder $builder) use ( $conditions, $tableAlias, ): EloquentBuilder|QueryBuilder { - foreach ($conditions as $condition) { - $builder = $logical->apply( - $builder, - function (EloquentBuilder|QueryBuilder $builder) use ( - $condition, - $tableAlias, - ): EloquentBuilder|QueryBuilder { - return $this->process($builder, $condition, $tableAlias); - }, - ); - } - - return $builder; + return $logical->apply($this, $builder, $conditions, $tableAlias); }, ); } @@ -216,9 +167,6 @@ public function processComparison( array $conditions, string $tableAlias = null, ): EloquentBuilder|QueryBuilder { - // Not? - $not = (bool) $this->getNotOperator($conditions); - // Empty? if (count($conditions) <= 0) { throw new SearchLogicException($this->translator->get( @@ -251,17 +199,6 @@ public function processComparison( )); } - // Not allowed? - if ($not && !($operator instanceof OperatorNegationable)) { - throw new SearchLogicException($this->translator->get( - 'search_by.errors.unsupported_option', - [ - 'operator' => $name, - 'option' => Not::Name, - ], - )); - } - // Table Alias? if ($tableAlias) { $property = "{$tableAlias}.{$property}"; @@ -272,7 +209,7 @@ public function processComparison( } // Apply - return $operator->apply($builder, $property, $value, $not); + return $operator->apply($builder, $property, $value); } // @@ -301,23 +238,5 @@ public function getLogicalOperator(string $property): ?LogicalOperator { public function getComparisonOperator(string $property): ?ComparisonOperator { return $this->comparison[$property] ?? null; } - - /** - * @param array $conditions - */ - public function getNotOperator(array &$conditions): ?Not { - $not = null; - $operator = isset($conditions[Not::Name]) - ? $this->getLogicalOperator(Not::Name) - : null; - - if ($operator instanceof Not) { - $not = $operator; - - unset($conditions[Not::Name]); - } - - return $not; - } // } diff --git a/src/SearchBy/SearchBuilderTest.php b/src/SearchBy/SearchBuilderTest.php index af7a3c71..1cdf9ce2 100644 --- a/src/SearchBy/SearchBuilderTest.php +++ b/src/SearchBy/SearchBuilderTest.php @@ -11,19 +11,18 @@ use LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\Operator; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Comparison\Equal; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Comparison\GreaterThan; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Comparison\NotEqual; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Complex\ComplexOperator; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Logical\AllOf; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Logical\AnyOf; use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Logical\LogicalOperator; -use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Not; +use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Logical\Not; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\BuilderDataProvider; use LastDragon_ru\LaraASP\GraphQL\Testing\Package\TestCase; use LastDragon_ru\LaraASP\Testing\Providers\ArrayDataProvider; use LastDragon_ru\LaraASP\Testing\Providers\CompositeDataProvider; use Mockery; -use function count; - /** * @internal * @coversDefaultClass \LastDragon_ru\LaraASP\GraphQL\SearchBy\SearchBuilder @@ -53,11 +52,12 @@ public function testProcess( $search = new SearchBuilder( $this->app->make(PackageTranslator::class), [ - $this->app->make(Not::class), $this->app->make(Equal::class), + $this->app->make(NotEqual::class), $this->app->make(GreaterThan::class), $this->app->make(AllOf::class), $this->app->make(AnyOf::class), + $this->app->make(Not::class), ], ); $builder = $builder($this); @@ -70,42 +70,6 @@ public function testProcess( $this->assertEquals($expected, $actual); } - /** - * @covers ::processNotOperator - * - * @dataProvider dataProviderProcessNotOperator - * - * @param array $expected - */ - public function testProcessNotOperator(array $expected, Closure $builder): void { - $not = Mockery::mock(Not::class); - $not - ->shouldReceive('getName') - ->once() - ->andReturn('not'); - $not - ->shouldReceive('apply') - ->once() - ->andReturnUsing( - static function ( - EloquentBuilder|QueryBuilder $builder, - Closure $nested, - ): EloquentBuilder|QueryBuilder { - return $builder->whereRaw('not (1 = 1)'); - }, - ); - - $builder = $builder($this); - $search = new SearchBuilder($this->app->make(PackageTranslator::class), [$not]); - $builder = $search->processNotOperator($builder, $not, [1, 2]); - $actual = [ - 'sql' => $builder->toSql(), - 'bindings' => $builder->getBindings(), - ]; - - $this->assertEquals($expected, $actual); - } - /** * @covers ::processComparison * @@ -129,7 +93,7 @@ public function testProcessComparison( $search = new SearchBuilder( $this->app->make(PackageTranslator::class), [ - $this->app->make(Not::class), + $this->app->make(NotEqual::class), $this->app->make(Equal::class), $this->app->make(GreaterThan::class), ], @@ -164,11 +128,13 @@ public function testProcessLogicalOperator(array $expected, Closure $builder): v $logical ->shouldReceive('apply') - ->times(count($conditions)) + ->once() ->andReturnUsing( static function ( + SearchBuilder $search, EloquentBuilder|QueryBuilder $builder, - Closure $nested, + array $conditions, + ?string $tableAlias, ): EloquentBuilder|QueryBuilder { return $builder->whereRaw('(1 = 1)'); }, @@ -226,20 +192,6 @@ static function ( $this->assertEquals($expected, $actual); } - /** - * @covers ::getNotOperator - */ - public function testGetNotOperator(): void { - $search = new SearchBuilder($this->app->make(PackageTranslator::class), [$this->app->make(Not::class)]); - $with = ['not' => 'yes']; - $without = []; - - $this->assertNotNull($search->getNotOperator($with)); - $this->assertEmpty($with); - - $this->assertNull($search->getNotOperator($without)); - } - /** * @covers ::getComplexOperator */ @@ -292,13 +244,11 @@ public function dataProviderProcess(): array { 'Only one property allowed, found: `a`, `b`.', ), [ - 'not' => 'yes', - 'a' => [ - 'eq' => 2, + 'a' => [ + 'equal' => 2, ], - 'b' => [ - 'eq' => 3, - 'not' => 'yes', + 'b' => [ + 'notEqual' => 3, ], ], null, @@ -315,25 +265,24 @@ public function dataProviderProcess(): array { ], ], [ - 'not' => 'yes', - 'allOf' => [ - [ - 'a' => [ - 'eq' => 1, - 'not' => 'yes', + 'not' => [ + 'allOf' => [ + [ + 'a' => [ + 'notEqual' => 1, + ], ], - ], - [ - 'anyOf' => [ - [ - 'a' => [ - 'eq' => 2, + [ + 'anyOf' => [ + [ + 'a' => [ + 'equal' => 2, + ], ], - ], - [ - 'b' => [ - 'eq' => 3, - 'not' => 'yes', + [ + 'b' => [ + 'notEqual' => 3, + ], ], ], ], @@ -366,25 +315,24 @@ static function (TestCase $test): QueryBuilder { return $test->app->make('db')->table('tmp'); }, [ - 'not' => 'yes', - 'allOf' => [ - [ - 'a' => [ - 'eq' => 1, - 'not' => 'yes', + 'not' => [ + 'allOf' => [ + [ + 'a' => [ + 'notEqual' => 1, + ], ], - ], - [ - 'anyOf' => [ - [ - 'a' => [ - 'eq' => 2, + [ + 'anyOf' => [ + [ + 'a' => [ + 'equal' => 2, + ], ], - ], - [ - 'b' => [ - 'eq' => 3, - 'not' => 'yes', + [ + 'b' => [ + 'notEqual' => 3, + ], ], ], ], @@ -423,25 +371,24 @@ static function (TestCase $test): EloquentBuilder { })->query(); }, [ - 'not' => 'yes', - 'allOf' => [ - [ - 'a' => [ - 'eq' => 1, - 'not' => 'yes', + 'not' => [ + 'allOf' => [ + [ + 'a' => [ + 'notEqual' => 1, + ], ], - ], - [ - 'anyOf' => [ - [ - 'a' => [ - 'eq' => 2, + [ + 'anyOf' => [ + [ + 'a' => [ + 'equal' => 2, + ], ], - ], - [ - 'b' => [ - 'eq' => 3, - 'not' => 'yes', + [ + 'b' => [ + 'notEqual' => 3, + ], ], ], ], @@ -453,23 +400,6 @@ static function (TestCase $test): EloquentBuilder { ]; } - /** - * @return array - */ - public function dataProviderProcessNotOperator(): array { - return (new CompositeDataProvider( - new BuilderDataProvider(), - new ArrayDataProvider([ - 'ok' => [ - [ - 'sql' => 'select * from "tmp" where (not (1 = 1))', - 'bindings' => [], - ], - ], - ]), - ))->getData(); - } - /** * @return array */ @@ -479,7 +409,7 @@ public function dataProviderProcessLogicalOperator(): array { new ArrayDataProvider([ 'ok' => [ [ - 'sql' => 'select * from "tmp" where ((1 = 1) and (1 = 1))', + 'sql' => 'select * from "tmp" where ((1 = 1))', 'bindings' => [], ], ], @@ -519,24 +449,14 @@ public function dataProviderProcessComparison(): array { [], null, ], - 'empty (not only)' => [ - new SearchLogicException( - 'Search condition cannot be empty.', - ), - 'property', - [ - 'not' => 'yes', - ], - null, - ], 'more than one condition' => [ new SearchLogicException( - 'Only one comparison operator allowed, found: `eq`, `in`.', + 'Only one comparison operator allowed, found: `equal`, `in`.', ), 'property', [ - 'eq' => 'yes', - 'in' => [1, 2], + 'equal' => 'yes', + 'in' => [1, 2], ], null, ], @@ -550,17 +470,6 @@ public function dataProviderProcessComparison(): array { ], null, ], - 'operator cannot be used with not' => [ - new SearchLogicException( - 'Operator `gt` cannot be used with `not`.', - ), - 'property', - [ - 'gt' => 'yes', - 'not' => 'yes', - ], - null, - ], 'valid condition with table alias' => [ [ 'sql' => 'select * from "tmp" where "alias"."property" = ?', @@ -570,7 +479,7 @@ public function dataProviderProcessComparison(): array { ], 'property', [ - 'eq' => 123, + 'equal' => 123, ], 'alias', ], @@ -595,7 +504,7 @@ static function (TestCase $test): QueryBuilder { }, 'property', [ - 'eq' => 123, + 'equal' => 123, ], null, ], @@ -626,7 +535,7 @@ static function (TestCase $test): EloquentBuilder { }, 'property', [ - 'eq' => 123, + 'equal' => 123, ], null, ], diff --git a/src/Testing/Package/BuilderDataProvider.php b/src/Testing/Package/BuilderDataProvider.php index 71a83e10..078d934b 100644 --- a/src/Testing/Package/BuilderDataProvider.php +++ b/src/Testing/Package/BuilderDataProvider.php @@ -2,34 +2,13 @@ namespace LastDragon_ru\LaraASP\GraphQL\Testing\Package; -use Illuminate\Database\Eloquent\Builder as EloquentBuilder; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Query\Builder as QueryBuilder; -use LastDragon_ru\LaraASP\Testing\Providers\ArrayDataProvider; -use LastDragon_ru\LaraASP\Testing\Providers\Unknown; +use LastDragon_ru\LaraASP\Testing\Providers\MergeDataProvider; -class BuilderDataProvider extends ArrayDataProvider { +class BuilderDataProvider extends MergeDataProvider { public function __construct() { parent::__construct([ - 'Query Builder' => [ - new Unknown(), - static function (TestCase $test): QueryBuilder { - return $test->getApplication()->make('db')->table('tmp'); - }, - ], - 'Eloquent Builder' => [ - new Unknown(), - static function (TestCase $test): EloquentBuilder { - return (new class() extends Model { - /** - * @phpcsSuppress SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint - * - * @var string - */ - public $table = 'tmp'; - })->query(); - }, - ], + 'Query' => new QueryBuilderDataProvider(), + 'Eloquent' => new EloquentBuilderDataProvider(), ]); } } diff --git a/src/Testing/Package/EloquentBuilderDataProvider.php b/src/Testing/Package/EloquentBuilderDataProvider.php new file mode 100644 index 00000000..d05376a0 --- /dev/null +++ b/src/Testing/Package/EloquentBuilderDataProvider.php @@ -0,0 +1,28 @@ + [ + new Unknown(), + static function (TestCase $test): EloquentBuilder { + return (new class() extends Model { + /** + * @phpcsSuppress SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint + * + * @var string + */ + public $table = 'tmp'; + })->query(); + }, + ], + ]); + } +} diff --git a/src/Testing/Package/QueryBuilderDataProvider.php b/src/Testing/Package/QueryBuilderDataProvider.php new file mode 100644 index 00000000..5d6c99c6 --- /dev/null +++ b/src/Testing/Package/QueryBuilderDataProvider.php @@ -0,0 +1,20 @@ + [ + new Unknown(), + static function (TestCase $test): QueryBuilder { + return $test->getApplication()->make('db')->table('tmp'); + }, + ], + ]); + } +}