Skip to content

Commit a1dc059

Browse files
feat: add support for ARRAY_POSITION() and ARRAY_POSITIONS() (#366)
Co-authored-by: Martin Georgiev <martin-georgiev@users.noreply.github.com>
1 parent f92f480 commit a1dc059

File tree

8 files changed

+170
-1
lines changed

8 files changed

+170
-1
lines changed

docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
| array_dims | ARRAY_DIMENSIONS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayDimensions` |
3838
| array_length | ARRAY_LENGTH | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayLength` |
3939
| array_ndims | ARRAY_NUMBER_OF_DIMENSIONS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayNumberOfDimensions` |
40+
| array_position | ARRAY_POSITION | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPosition` |
41+
| array_positions | ARRAY_POSITIONS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPositions` |
4042
| array_prepend | ARRAY_PREPEND | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPrepend` |
4143
| array_remove | ARRAY_REMOVE | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayRemove` |
4244
| array_replace | ARRAY_REPLACE | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayReplace` |

docs/INTEGRATING-WITH-DOCTRINE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ $configuration->addCustomStringFunction('ARRAY_CAT', MartinGeorgiev\Doctrine\ORM
7272
$configuration->addCustomStringFunction('ARRAY_DIMENSIONS', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayDimensions::class);
7373
$configuration->addCustomStringFunction('ARRAY_LENGTH', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayLength::class);
7474
$configuration->addCustomStringFunction('ARRAY_NUMBER_OF_DIMENSIONS', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayNumberOfDimensions::class);
75+
$configuration->addCustomStringFunction('ARRAY_POSITION', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPosition::class);
76+
$configuration->addCustomStringFunction('ARRAY_POSITIONS', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPositions::class);
7577
$configuration->addCustomStringFunction('ARRAY_PREPEND', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPrepend::class);
7678
$configuration->addCustomStringFunction('ARRAY_REMOVE', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayRemove::class);
7779
$configuration->addCustomStringFunction('ARRAY_REPLACE', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayReplace::class);

docs/INTEGRATING-WITH-LARAVEL.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ return [
128128
'ARRAY_DIMENSIONS' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayDimensions::class,
129129
'ARRAY_LENGTH' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayLength::class,
130130
'ARRAY_NUMBER_OF_DIMENSIONS' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayNumberOfDimensions::class,
131+
'ARRAY_POSITION' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPosition::class,
132+
'ARRAY_POSITIONS' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPositions::class,
131133
'ARRAY_PREPEND' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPrepend::class,
132134
'ARRAY_REMOVE' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayRemove::class,
133135
'ARRAY_REPLACE' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayReplace::class,
@@ -354,4 +356,4 @@ class DoctrineServiceProvider extends LaravelDoctrineServiceProvider
354356
}
355357
}
356358
}
357-
```
359+
```

docs/INTEGRATING-WITH-SYMFONY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ doctrine:
121121
ARRAY_DIMENSIONS: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayDimensions
122122
ARRAY_LENGTH: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayLength
123123
ARRAY_NUMBER_OF_DIMENSIONS: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayNumberOfDimensions
124+
ARRAY_POSITION: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPosition
125+
ARRAY_POSITIONS: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPositions
124126
ARRAY_PREPEND: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPrepend
125127
ARRAY_REMOVE: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayRemove
126128
ARRAY_REPLACE: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayReplace
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MartinGeorgiev\Doctrine\ORM\Query\AST\Functions;
6+
7+
/**
8+
* Implementation of PostgreSQL ARRAY_POSITION().
9+
*
10+
* @see https://www.postgresql.org/docs/9.5/static/functions-array.html
11+
* @since 3.1
12+
*
13+
* @author Daniel Gorgan <danut007ro@gmail.com>
14+
*/
15+
class ArrayPosition extends BaseVariadicFunction
16+
{
17+
protected function getNodeMappingPattern(): array
18+
{
19+
return [
20+
'StringPrimary,NewValue,SimpleArithmeticExpression',
21+
'StringPrimary,NewValue',
22+
];
23+
}
24+
25+
protected function getFunctionName(): string
26+
{
27+
return 'array_position';
28+
}
29+
30+
protected function getMinArgumentCount(): int
31+
{
32+
return 2;
33+
}
34+
35+
protected function getMaxArgumentCount(): int
36+
{
37+
return 3;
38+
}
39+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MartinGeorgiev\Doctrine\ORM\Query\AST\Functions;
6+
7+
/**
8+
* Implementation of PostgreSQL ARRAY_POSITIONS().
9+
*
10+
* @see https://www.postgresql.org/docs/9.5/static/functions-array.html
11+
* @since 3.1
12+
*
13+
* @author Daniel Gorgan <danut007ro@gmail.com>
14+
*/
15+
class ArrayPositions extends BaseFunction
16+
{
17+
protected function customizeFunction(): void
18+
{
19+
$this->setFunctionPrototype('array_positions(%s, %s)');
20+
$this->addNodeMapping('StringPrimary');
21+
$this->addNodeMapping('NewValue');
22+
}
23+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions;
6+
7+
use Fixtures\MartinGeorgiev\Doctrine\Entity\ContainsArrays;
8+
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPosition;
9+
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\BaseVariadicFunction;
10+
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Exception\InvalidArgumentForVariadicFunctionException;
11+
12+
class ArrayPositionTest extends BaseVariadicFunctionTestCase
13+
{
14+
protected function createFixture(): BaseVariadicFunction
15+
{
16+
return new ArrayPosition('ARRAY_POSITION');
17+
}
18+
19+
protected function getStringFunctions(): array
20+
{
21+
return [
22+
'ARRAY_POSITION' => ArrayPosition::class,
23+
];
24+
}
25+
26+
protected function getExpectedSqlStatements(): array
27+
{
28+
return [
29+
'finds string element' => "SELECT array_position(c0_.array1, 'new-value') AS sclr_0 FROM ContainsArrays c0_",
30+
'finds numeric element' => 'SELECT array_position(c0_.array1, 42) AS sclr_0 FROM ContainsArrays c0_',
31+
'finds element using parameter' => 'SELECT array_position(c0_.array1, ?) AS sclr_0 FROM ContainsArrays c0_',
32+
'with start position' => "SELECT array_position(c0_.array1, 'new-value', 2) AS sclr_0 FROM ContainsArrays c0_",
33+
];
34+
}
35+
36+
protected function getDqlStatements(): array
37+
{
38+
return [
39+
'finds string element' => \sprintf("SELECT ARRAY_POSITION(e.array1, 'new-value') FROM %s e", ContainsArrays::class),
40+
'finds numeric element' => \sprintf('SELECT ARRAY_POSITION(e.array1, 42) FROM %s e', ContainsArrays::class),
41+
'finds element using parameter' => \sprintf('SELECT ARRAY_POSITION(e.array1, :dql_parameter) FROM %s e', ContainsArrays::class),
42+
'with start position' => \sprintf("SELECT ARRAY_POSITION(e.array1, 'new-value', 2) FROM %s e", ContainsArrays::class),
43+
];
44+
}
45+
46+
public function test_too_few_arguments_throws_exception(): void
47+
{
48+
$this->expectException(InvalidArgumentForVariadicFunctionException::class);
49+
$this->expectExceptionMessage('array_position() requires at least 2 arguments');
50+
51+
$dql = \sprintf('SELECT ARRAY_POSITION(e.array1) FROM %s e', ContainsArrays::class);
52+
$this->buildEntityManager()->createQuery($dql)->getSQL();
53+
}
54+
55+
public function test_too_many_arguments_throws_exception(): void
56+
{
57+
$this->expectException(InvalidArgumentForVariadicFunctionException::class);
58+
$this->expectExceptionMessage('array_position() requires between 2 and 3 arguments');
59+
60+
$dql = \sprintf("SELECT ARRAY_POSITION(e.array1, 0, 1, 'extra_arg') FROM %s e", ContainsArrays::class);
61+
$this->buildEntityManager()->createQuery($dql)->getSQL();
62+
}
63+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions;
6+
7+
use Fixtures\MartinGeorgiev\Doctrine\Entity\ContainsArrays;
8+
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPositions;
9+
10+
class ArrayPositionsTest extends TestCase
11+
{
12+
protected function getStringFunctions(): array
13+
{
14+
return [
15+
'ARRAY_POSITIONS' => ArrayPositions::class,
16+
];
17+
}
18+
19+
protected function getExpectedSqlStatements(): array
20+
{
21+
return [
22+
'finds string element' => "SELECT array_positions(c0_.array1, 'new-value') AS sclr_0 FROM ContainsArrays c0_",
23+
'finds numeric element' => 'SELECT array_positions(c0_.array1, 42) AS sclr_0 FROM ContainsArrays c0_',
24+
'finds element using parameter' => 'SELECT array_positions(c0_.array1, ?) AS sclr_0 FROM ContainsArrays c0_',
25+
];
26+
}
27+
28+
protected function getDqlStatements(): array
29+
{
30+
return [
31+
'finds string element' => \sprintf("SELECT ARRAY_POSITIONS(e.array1, 'new-value') FROM %s e", ContainsArrays::class),
32+
'finds numeric element' => \sprintf('SELECT ARRAY_POSITIONS(e.array1, 42) FROM %s e', ContainsArrays::class),
33+
'finds element using parameter' => \sprintf('SELECT ARRAY_POSITIONS(e.array1, :dql_parameter) FROM %s e', ContainsArrays::class),
34+
];
35+
}
36+
}

0 commit comments

Comments
 (0)