diff --git a/README.md b/README.md index 3ce2fcf..39692ea 100644 --- a/README.md +++ b/README.md @@ -140,12 +140,12 @@ use Tpetry\QueryExpressions\Operator\Arithmetic\{ }; use Tpetry\QueryExpressions\Operator\Value\Value; -new Add(string|Expression $value1, string|Expression $value2); -new Divide(string|Expression $value1, string|Expression $value2); -new Modulo(string|Expression $value1, string|Expression $value2); -new Multiply(string|Expression $value1, string|Expression $value2); +new Add(string|Expression $value1, string|Expression $value2, string|Expression ...$values); +new Divide(string|Expression $value1, string|Expression $value2, string|Expression ...$values); +new Modulo(string|Expression $value1, string|Expression $value2, string|Expression ...$values); +new Multiply(string|Expression $value1, string|Expression $value2, string|Expression ...$values); new Power(string|Expression $value1, string|Expression $value2); -new Subtract(string|Expression $value1, string|Expression $value2); +new Subtract(string|Expression $value1, string|Expression $value2, string|Expression ...$values); // UPDATE user_quotas SET credits = credits - 15 WHERE id = 1985 $quota->update([ diff --git a/src/Operator/Arithmetic/Add.php b/src/Operator/Arithmetic/Add.php index 610e38d..fc218c1 100644 --- a/src/Operator/Arithmetic/Add.php +++ b/src/Operator/Arithmetic/Add.php @@ -4,9 +4,7 @@ namespace Tpetry\QueryExpressions\Operator\Arithmetic; -use Tpetry\QueryExpressions\Operator\OperatorExpression; - -class Add extends OperatorExpression +class Add extends ArithmeticExpression { protected function operator(): string { diff --git a/src/Operator/Arithmetic/ArithmeticExpression.php b/src/Operator/Arithmetic/ArithmeticExpression.php new file mode 100644 index 0000000..e1d1f59 --- /dev/null +++ b/src/Operator/Arithmetic/ArithmeticExpression.php @@ -0,0 +1,48 @@ +values = $values; + } + + public function getValue(Grammar $grammar): string + { + $expression = implode(" {$this->operator()} ", $this->expressions($grammar)); + + return "({$expression})"; + } + + /** + * @return array + */ + protected function expressions(Grammar $grammar): array + { + return array_map( + fn (string|Expression $value) => $this->stringize($grammar, $value), + [$this->value1, $this->value2, ...$this->values], + ); + } + + abstract protected function operator(): string; +} diff --git a/src/Operator/Arithmetic/Divide.php b/src/Operator/Arithmetic/Divide.php index 772f037..962202a 100644 --- a/src/Operator/Arithmetic/Divide.php +++ b/src/Operator/Arithmetic/Divide.php @@ -4,9 +4,7 @@ namespace Tpetry\QueryExpressions\Operator\Arithmetic; -use Tpetry\QueryExpressions\Operator\OperatorExpression; - -class Divide extends OperatorExpression +class Divide extends ArithmeticExpression { protected function operator(): string { diff --git a/src/Operator/Arithmetic/Modulo.php b/src/Operator/Arithmetic/Modulo.php index 7c8b576..24dc94f 100644 --- a/src/Operator/Arithmetic/Modulo.php +++ b/src/Operator/Arithmetic/Modulo.php @@ -4,9 +4,7 @@ namespace Tpetry\QueryExpressions\Operator\Arithmetic; -use Tpetry\QueryExpressions\Operator\OperatorExpression; - -class Modulo extends OperatorExpression +class Modulo extends ArithmeticExpression { protected function operator(): string { diff --git a/src/Operator/Arithmetic/Multiply.php b/src/Operator/Arithmetic/Multiply.php index ebb9fd0..3087816 100644 --- a/src/Operator/Arithmetic/Multiply.php +++ b/src/Operator/Arithmetic/Multiply.php @@ -4,9 +4,7 @@ namespace Tpetry\QueryExpressions\Operator\Arithmetic; -use Tpetry\QueryExpressions\Operator\OperatorExpression; - -class Multiply extends OperatorExpression +class Multiply extends ArithmeticExpression { protected function operator(): string { diff --git a/src/Operator/Arithmetic/Power.php b/src/Operator/Arithmetic/Power.php index 0fde741..a4751c3 100644 --- a/src/Operator/Arithmetic/Power.php +++ b/src/Operator/Arithmetic/Power.php @@ -4,30 +4,43 @@ namespace Tpetry\QueryExpressions\Operator\Arithmetic; -use Illuminate\Contracts\Database\Query\Expression; use Illuminate\Database\Grammar; use Tpetry\QueryExpressions\Concerns\IdentifiesDriver; use Tpetry\QueryExpressions\Concerns\StringizeExpression; -class Power implements Expression +class Power extends ArithmeticExpression { use IdentifiesDriver; use StringizeExpression; - public function __construct( - private readonly string|Expression $value1, - private readonly string|Expression $value2, - ) { + public function getValue(Grammar $grammar): string + { + return match ($this->identify($grammar)) { + 'mysql', 'sqlite', 'sqlsrv' => $this->buildPowerFunctionChain($grammar), + 'pgsql' => parent::getValue($grammar), + }; } - public function getValue(Grammar $grammar) + protected function buildPowerFunctionChain(Grammar $grammar): string { - $value1 = $this->stringize($grammar, $this->value1); - $value2 = $this->stringize($grammar, $this->value2); + $expressions = $this->expressions($grammar); - return match ($this->identify($grammar)) { - 'mysql', 'sqlite', 'sqlsrv' => "power({$value1}, {$value2})", - 'pgsql' => "({$value1} ^ {$value2})", - }; + // Build the initial expressions by using the two required parameters of the object. + $value0 = array_shift($expressions); + $value1 = array_shift($expressions); + $expression = "power({$value0}, {$value1})"; + + // For each remaining value call the power function again with the last result and the new value. + while (count($expressions) > 0) { + $value = array_shift($expressions); + $expression = "power({$expression}, $value)"; + } + + return $expression; + } + + protected function operator(): string + { + return '^'; } } diff --git a/src/Operator/Arithmetic/Subtract.php b/src/Operator/Arithmetic/Subtract.php index 02fb2ad..fcaf903 100644 --- a/src/Operator/Arithmetic/Subtract.php +++ b/src/Operator/Arithmetic/Subtract.php @@ -4,9 +4,7 @@ namespace Tpetry\QueryExpressions\Operator\Arithmetic; -use Tpetry\QueryExpressions\Operator\OperatorExpression; - -class Subtract extends OperatorExpression +class Subtract extends ArithmeticExpression { protected function operator(): string { diff --git a/tests/Operator/Arithmetic/AddTest.php b/tests/Operator/Arithmetic/AddTest.php index e49a740..cb96789 100644 --- a/tests/Operator/Arithmetic/AddTest.php +++ b/tests/Operator/Arithmetic/AddTest.php @@ -44,3 +44,14 @@ ->toBePgsql('(0 + "val")') ->toBeSqlite('(0 + "val")') ->toBeSqlsrv('(0 + [val])'); + +it('can add variadic values') + ->expect(new Add(new Expression(0), 'val1', 'val2', new Expression(1))) + ->toBeExecutable(function (Blueprint $table) { + $table->integer('val1'); + $table->integer('val2'); + }) + ->toBeMysql('(0 + `val1` + `val2` + 1)') + ->toBePgsql('(0 + "val1" + "val2" + 1)') + ->toBeSqlite('(0 + "val1" + "val2" + 1)') + ->toBeSqlsrv('(0 + [val1] + [val2] + 1)'); diff --git a/tests/Operator/Arithmetic/DivideTest.php b/tests/Operator/Arithmetic/DivideTest.php index ae46393..37a3378 100644 --- a/tests/Operator/Arithmetic/DivideTest.php +++ b/tests/Operator/Arithmetic/DivideTest.php @@ -44,3 +44,14 @@ ->toBePgsql('(0 / "val")') ->toBeSqlite('(0 / "val")') ->toBeSqlsrv('(0 / [val])'); + +it('can divide variadic values') + ->expect(new Divide(new Expression(0), 'val1', 'val2', new Expression(1))) + ->toBeExecutable(function (Blueprint $table) { + $table->integer('val1'); + $table->integer('val2'); + }) + ->toBeMysql('(0 / `val1` / `val2` / 1)') + ->toBePgsql('(0 / "val1" / "val2" / 1)') + ->toBeSqlite('(0 / "val1" / "val2" / 1)') + ->toBeSqlsrv('(0 / [val1] / [val2] / 1)'); diff --git a/tests/Operator/Arithmetic/ModuloTest.php b/tests/Operator/Arithmetic/ModuloTest.php index b60d573..4f78e07 100644 --- a/tests/Operator/Arithmetic/ModuloTest.php +++ b/tests/Operator/Arithmetic/ModuloTest.php @@ -44,3 +44,14 @@ ->toBePgsql('(0 % "val")') ->toBeSqlite('(0 % "val")') ->toBeSqlsrv('(0 % [val])'); + +it('can modulo variadic values') + ->expect(new Modulo(new Expression(0), 'val1', 'val2', new Expression(1))) + ->toBeExecutable(function (Blueprint $table) { + $table->integer('val1'); + $table->integer('val2'); + }) + ->toBeMysql('(0 % `val1` % `val2` % 1)') + ->toBePgsql('(0 % "val1" % "val2" % 1)') + ->toBeSqlite('(0 % "val1" % "val2" % 1)') + ->toBeSqlsrv('(0 % [val1] % [val2] % 1)'); diff --git a/tests/Operator/Arithmetic/MultiplyTest.php b/tests/Operator/Arithmetic/MultiplyTest.php index bba93bd..bc1e013 100644 --- a/tests/Operator/Arithmetic/MultiplyTest.php +++ b/tests/Operator/Arithmetic/MultiplyTest.php @@ -44,3 +44,14 @@ ->toBePgsql('(0 * "val")') ->toBeSqlite('(0 * "val")') ->toBeSqlsrv('(0 * [val])'); + +it('can multiply variadic values') + ->expect(new Multiply(new Expression(0), 'val1', 'val2', new Expression(1))) + ->toBeExecutable(function (Blueprint $table) { + $table->integer('val1'); + $table->integer('val2'); + }) + ->toBeMysql('(0 * `val1` * `val2` * 1)') + ->toBePgsql('(0 * "val1" * "val2" * 1)') + ->toBeSqlite('(0 * "val1" * "val2" * 1)') + ->toBeSqlsrv('(0 * [val1] * [val2] * 1)'); diff --git a/tests/Operator/Arithmetic/PowerTest.php b/tests/Operator/Arithmetic/PowerTest.php index 838ebad..66c5b87 100644 --- a/tests/Operator/Arithmetic/PowerTest.php +++ b/tests/Operator/Arithmetic/PowerTest.php @@ -44,3 +44,14 @@ ->toBePgsql('(0 ^ "val")') ->toBeSqlite('power(0, "val")') ->toBeSqlsrv('power(0, [val])'); + +it('can power variadic values') + ->expect(new Power(new Expression(0), 'val1', 'val2', new Expression(1))) + ->toBeExecutable(function (Blueprint $table) { + $table->integer('val1'); + $table->integer('val2'); + }) + ->toBeMysql('power(power(power(0, `val1`), `val2`), 1)') + ->toBePgsql('(0 ^ "val1" ^ "val2" ^ 1)') + ->toBeSqlite('power(power(power(0, "val1"), "val2"), 1)') + ->toBeSqlsrv('power(power(power(0, [val1]), [val2]), 1)'); diff --git a/tests/Operator/Arithmetic/SubtractTest.php b/tests/Operator/Arithmetic/SubtractTest.php index 05d5844..e8ab26c 100644 --- a/tests/Operator/Arithmetic/SubtractTest.php +++ b/tests/Operator/Arithmetic/SubtractTest.php @@ -44,3 +44,14 @@ ->toBePgsql('(0 - "val")') ->toBeSqlite('(0 - "val")') ->toBeSqlsrv('(0 - [val])'); + +it('can subtract variadic values') + ->expect(new Subtract(new Expression(0), 'val1', 'val2', new Expression(1))) + ->toBeExecutable(function (Blueprint $table) { + $table->integer('val1'); + $table->integer('val2'); + }) + ->toBeMysql('(0 - `val1` - `val2` - 1)') + ->toBePgsql('(0 - "val1" - "val2" - 1)') + ->toBeSqlite('(0 - "val1" - "val2" - 1)') + ->toBeSqlsrv('(0 - [val1] - [val2] - 1)');