Skip to content

Commit df0fe4f

Browse files
jasperteytpetry
andauthored
Variable length arithmetic (#25)
Co-authored-by: tpetry <[email protected]>
1 parent e4d1576 commit df0fe4f

File tree

14 files changed

+150
-33
lines changed

14 files changed

+150
-33
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,12 @@ use Tpetry\QueryExpressions\Operator\Arithmetic\{
140140
};
141141
use Tpetry\QueryExpressions\Operator\Value\Value;
142142

143-
new Add(string|Expression $value1, string|Expression $value2);
144-
new Divide(string|Expression $value1, string|Expression $value2);
145-
new Modulo(string|Expression $value1, string|Expression $value2);
146-
new Multiply(string|Expression $value1, string|Expression $value2);
143+
new Add(string|Expression $value1, string|Expression $value2, string|Expression ...$values);
144+
new Divide(string|Expression $value1, string|Expression $value2, string|Expression ...$values);
145+
new Modulo(string|Expression $value1, string|Expression $value2, string|Expression ...$values);
146+
new Multiply(string|Expression $value1, string|Expression $value2, string|Expression ...$values);
147147
new Power(string|Expression $value1, string|Expression $value2);
148-
new Subtract(string|Expression $value1, string|Expression $value2);
148+
new Subtract(string|Expression $value1, string|Expression $value2, string|Expression ...$values);
149149

150150
// UPDATE user_quotas SET credits = credits - 15 WHERE id = 1985
151151
$quota->update([

src/Operator/Arithmetic/Add.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44

55
namespace Tpetry\QueryExpressions\Operator\Arithmetic;
66

7-
use Tpetry\QueryExpressions\Operator\OperatorExpression;
8-
9-
class Add extends OperatorExpression
7+
class Add extends ArithmeticExpression
108
{
119
protected function operator(): string
1210
{
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tpetry\QueryExpressions\Operator\Arithmetic;
6+
7+
use Illuminate\Contracts\Database\Query\Expression;
8+
use Illuminate\Database\Grammar;
9+
use Tpetry\QueryExpressions\Concerns\StringizeExpression;
10+
11+
/**
12+
* @internal
13+
*/
14+
abstract class ArithmeticExpression implements Expression
15+
{
16+
use StringizeExpression;
17+
18+
/** @var string[]|Expression[] */
19+
private readonly array $values;
20+
21+
public function __construct(
22+
private readonly string|Expression $value1,
23+
private readonly string|Expression $value2,
24+
string|Expression ...$values,
25+
) {
26+
$this->values = $values;
27+
}
28+
29+
public function getValue(Grammar $grammar): string
30+
{
31+
$expression = implode(" {$this->operator()} ", $this->expressions($grammar));
32+
33+
return "({$expression})";
34+
}
35+
36+
/**
37+
* @return array<int, float|int|string>
38+
*/
39+
protected function expressions(Grammar $grammar): array
40+
{
41+
return array_map(
42+
fn (string|Expression $value) => $this->stringize($grammar, $value),
43+
[$this->value1, $this->value2, ...$this->values],
44+
);
45+
}
46+
47+
abstract protected function operator(): string;
48+
}

src/Operator/Arithmetic/Divide.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44

55
namespace Tpetry\QueryExpressions\Operator\Arithmetic;
66

7-
use Tpetry\QueryExpressions\Operator\OperatorExpression;
8-
9-
class Divide extends OperatorExpression
7+
class Divide extends ArithmeticExpression
108
{
119
protected function operator(): string
1210
{

src/Operator/Arithmetic/Modulo.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44

55
namespace Tpetry\QueryExpressions\Operator\Arithmetic;
66

7-
use Tpetry\QueryExpressions\Operator\OperatorExpression;
8-
9-
class Modulo extends OperatorExpression
7+
class Modulo extends ArithmeticExpression
108
{
119
protected function operator(): string
1210
{

src/Operator/Arithmetic/Multiply.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44

55
namespace Tpetry\QueryExpressions\Operator\Arithmetic;
66

7-
use Tpetry\QueryExpressions\Operator\OperatorExpression;
8-
9-
class Multiply extends OperatorExpression
7+
class Multiply extends ArithmeticExpression
108
{
119
protected function operator(): string
1210
{

src/Operator/Arithmetic/Power.php

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,43 @@
44

55
namespace Tpetry\QueryExpressions\Operator\Arithmetic;
66

7-
use Illuminate\Contracts\Database\Query\Expression;
87
use Illuminate\Database\Grammar;
98
use Tpetry\QueryExpressions\Concerns\IdentifiesDriver;
109
use Tpetry\QueryExpressions\Concerns\StringizeExpression;
1110

12-
class Power implements Expression
11+
class Power extends ArithmeticExpression
1312
{
1413
use IdentifiesDriver;
1514
use StringizeExpression;
1615

17-
public function __construct(
18-
private readonly string|Expression $value1,
19-
private readonly string|Expression $value2,
20-
) {
16+
public function getValue(Grammar $grammar): string
17+
{
18+
return match ($this->identify($grammar)) {
19+
'mysql', 'sqlite', 'sqlsrv' => $this->buildPowerFunctionChain($grammar),
20+
'pgsql' => parent::getValue($grammar),
21+
};
2122
}
2223

23-
public function getValue(Grammar $grammar)
24+
protected function buildPowerFunctionChain(Grammar $grammar): string
2425
{
25-
$value1 = $this->stringize($grammar, $this->value1);
26-
$value2 = $this->stringize($grammar, $this->value2);
26+
$expressions = $this->expressions($grammar);
2727

28-
return match ($this->identify($grammar)) {
29-
'mysql', 'sqlite', 'sqlsrv' => "power({$value1}, {$value2})",
30-
'pgsql' => "({$value1} ^ {$value2})",
31-
};
28+
// Build the initial expressions by using the two required parameters of the object.
29+
$value0 = array_shift($expressions);
30+
$value1 = array_shift($expressions);
31+
$expression = "power({$value0}, {$value1})";
32+
33+
// For each remaining value call the power function again with the last result and the new value.
34+
while (count($expressions) > 0) {
35+
$value = array_shift($expressions);
36+
$expression = "power({$expression}, $value)";
37+
}
38+
39+
return $expression;
40+
}
41+
42+
protected function operator(): string
43+
{
44+
return '^';
3245
}
3346
}

src/Operator/Arithmetic/Subtract.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44

55
namespace Tpetry\QueryExpressions\Operator\Arithmetic;
66

7-
use Tpetry\QueryExpressions\Operator\OperatorExpression;
8-
9-
class Subtract extends OperatorExpression
7+
class Subtract extends ArithmeticExpression
108
{
119
protected function operator(): string
1210
{

tests/Operator/Arithmetic/AddTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,14 @@
4444
->toBePgsql('(0 + "val")')
4545
->toBeSqlite('(0 + "val")')
4646
->toBeSqlsrv('(0 + [val])');
47+
48+
it('can add variadic values')
49+
->expect(new Add(new Expression(0), 'val1', 'val2', new Expression(1)))
50+
->toBeExecutable(function (Blueprint $table) {
51+
$table->integer('val1');
52+
$table->integer('val2');
53+
})
54+
->toBeMysql('(0 + `val1` + `val2` + 1)')
55+
->toBePgsql('(0 + "val1" + "val2" + 1)')
56+
->toBeSqlite('(0 + "val1" + "val2" + 1)')
57+
->toBeSqlsrv('(0 + [val1] + [val2] + 1)');

tests/Operator/Arithmetic/DivideTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,14 @@
4444
->toBePgsql('(0 / "val")')
4545
->toBeSqlite('(0 / "val")')
4646
->toBeSqlsrv('(0 / [val])');
47+
48+
it('can divide variadic values')
49+
->expect(new Divide(new Expression(0), 'val1', 'val2', new Expression(1)))
50+
->toBeExecutable(function (Blueprint $table) {
51+
$table->integer('val1');
52+
$table->integer('val2');
53+
})
54+
->toBeMysql('(0 / `val1` / `val2` / 1)')
55+
->toBePgsql('(0 / "val1" / "val2" / 1)')
56+
->toBeSqlite('(0 / "val1" / "val2" / 1)')
57+
->toBeSqlsrv('(0 / [val1] / [val2] / 1)');

0 commit comments

Comments
 (0)