Skip to content

Commit 18ec906

Browse files
feat: add support for POINT and POINT[] data types (#348)
Co-authored-by: Martin Georgiev <martin-georgiev@users.noreply.github.com>
1 parent ef688dc commit 18ec906

13 files changed

+693
-0
lines changed

docs/AVAILABLE-TYPES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@
1717
| cidr[] | _cidr | `MartinGeorgiev\Doctrine\DBAL\Types\CidrArray` |
1818
| macaddr | macaddr | `MartinGeorgiev\Doctrine\DBAL\Types\Macaddr` |
1919
| macaddr[] | _macaddr | `MartinGeorgiev\Doctrine\DBAL\Types\MacaddrArray` |
20+
| point | point | `MartinGeorgiev\Doctrine\DBAL\Types\Point` |
21+
| point[] | _point | `MartinGeorgiev\Doctrine\DBAL\Types\PointArray` |

docs/INTEGRATING-WITH-DOCTRINE.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ Type::addType('inet', "MartinGeorgiev\\Doctrine\\DBAL\\Types\\Inet");
2828
Type::addType('inet[]', "MartinGeorgiev\\Doctrine\\DBAL\\Types\\InetArray");
2929
Type::addType('macaddr', "MartinGeorgiev\\Doctrine\\DBAL\\Types\\Macaddr");
3030
Type::addType('macaddr[]', "MartinGeorgiev\\Doctrine\\DBAL\\Types\\MacaddrArray");
31+
32+
Type::addType('point', "MartinGeorgiev\\Doctrine\\DBAL\\Types\\Point");
33+
Type::addType('point[]', "MartinGeorgiev\\Doctrine\\DBAL\\Types\\PointArray");
3134
```
3235

3336

@@ -204,6 +207,10 @@ $platform->registerDoctrineTypeMapping('inet[]','inet[]');
204207
$platform->registerDoctrineTypeMapping('_inet','inet[]');
205208
$platform->registerDoctrineTypeMapping('macaddr[]','macaddr[]');
206209
$platform->registerDoctrineTypeMapping('_macaddr','macaddr[]');
210+
211+
$platform->registerDoctrineTypeMapping('point','point');
212+
$platform->registerDoctrineTypeMapping('point[]','point[]');
213+
$platform->registerDoctrineTypeMapping('_point','point[]');
207214
...
208215

209216
```

docs/INTEGRATING-WITH-LARAVEL.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ return [
4646
'macaddr' => 'macaddr',
4747
'macaddr[]' => 'macaddr[]',
4848
'_macaddr' => 'macaddr[]',
49+
50+
'point' => 'point',
51+
'point[]' => 'point[]',
52+
'_point' => 'point[]',
4953
],
5054
],
5155
],
@@ -80,6 +84,9 @@ return [
8084
'inet[]' => MartinGeorgiev\Doctrine\DBAL\Types\InetArray::class,
8185
'macaddr' => MartinGeorgiev\Doctrine\DBAL\Types\Macaddr::class,
8286
'macaddr[]' => MartinGeorgiev\Doctrine\DBAL\Types\MacaddrArray::class,
87+
88+
'point' => MartinGeorgiev\Doctrine\DBAL\Types\Point::class,
89+
'point[]' => MartinGeorgiev\Doctrine\DBAL\Types\PointArray::class,
8390
],
8491
];
8592
```
@@ -301,6 +308,12 @@ class DoctrineEventSubscriber implements Subscriber
301308
if (!Type::hasType('macaddr[]')) {
302309
Type::addType('macaddr[]', \MartinGeorgiev\Doctrine\DBAL\Types\MacaddrArray::class);
303310
}
311+
if (!Type::hasType('point')) {
312+
Type::addType('point', \MartinGeorgiev\Doctrine\DBAL\Types\Point::class);
313+
}
314+
if (!Type::hasType('point[]')) {
315+
Type::addType('point[]', \MartinGeorgiev\Doctrine\DBAL\Types\PointArray::class);
316+
}
304317
}
305318
}
306319
```

docs/INTEGRATING-WITH-SYMFONY.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ doctrine:
2828
inet[]: MartinGeorgiev\Doctrine\DBAL\Types\InetArray
2929
macaddr: MartinGeorgiev\Doctrine\DBAL\Types\Macaddr
3030
macaddr[]: MartinGeorgiev\Doctrine\DBAL\Types\MacaddrArray
31+
32+
point: MartinGeorgiev\Doctrine\DBAL\Types\Point
33+
point[]: MartinGeorgiev\Doctrine\DBAL\Types\PointArray
3134
```
3235
3336
@@ -73,6 +76,10 @@ doctrine:
7376
macaddr: macaddr
7477
macaddr[]: macaddr[]
7578
_macaddr: macaddr[]
79+
80+
point: point
81+
point[]: point[]
82+
_point: point[]
7683
```
7784

7885

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MartinGeorgiev\Doctrine\DBAL\Types\Exceptions;
6+
7+
use Doctrine\DBAL\Types\ConversionException;
8+
9+
/**
10+
* @since 3.1
11+
*
12+
* @author Sébastien Jean <sebastien.jean76@gmail.com>
13+
*/
14+
class InvalidPointArrayItemForDatabaseException extends ConversionException
15+
{
16+
private static function create(string $message, mixed $value): self
17+
{
18+
return new self(\sprintf($message, \var_export($value, true)));
19+
}
20+
21+
public static function isNotAPoint(mixed $value): self
22+
{
23+
return self::create('Given value of %s is not a point.', $value);
24+
}
25+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MartinGeorgiev\Doctrine\DBAL\Types\Exceptions;
6+
7+
use Doctrine\DBAL\Types\ConversionException;
8+
9+
/**
10+
* @since 3.1
11+
*
12+
* @author Sébastien Jean <sebastien.jean76@gmail.com>
13+
*/
14+
class InvalidPointArrayItemForPHPException extends ConversionException
15+
{
16+
private static function create(string $message, mixed $value): self
17+
{
18+
return new self(\sprintf($message, \var_export($value, true)));
19+
}
20+
21+
public static function forInvalidType(mixed $value): self
22+
{
23+
return self::create('Array values must be strings, %s given', $value);
24+
}
25+
26+
public static function forInvalidFormat(mixed $value): self
27+
{
28+
return self::create('Invalid point format in array: %s', $value);
29+
}
30+
31+
public static function forInvalidArrayType(mixed $value): self
32+
{
33+
return self::create('Value must be an array, %s given', $value);
34+
}
35+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MartinGeorgiev\Doctrine\DBAL\Types\Exceptions;
6+
7+
use Doctrine\DBAL\Types\ConversionException;
8+
9+
/**
10+
* @since 3.1
11+
*
12+
* @author Sébastien Jean <sebastien.jean76@gmail.com>
13+
*/
14+
class InvalidPointForDatabaseException extends ConversionException
15+
{
16+
private static function create(string $message, mixed $value): self
17+
{
18+
return new self(\sprintf($message, \var_export($value, true)));
19+
}
20+
21+
public static function forInvalidType(mixed $value): self
22+
{
23+
return self::create('Database value must be a string, %s given', $value);
24+
}
25+
26+
public static function forInvalidFormat(mixed $value): self
27+
{
28+
return self::create('Invalid point format in database: %s', $value);
29+
}
30+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MartinGeorgiev\Doctrine\DBAL\Types\Exceptions;
6+
7+
use Doctrine\DBAL\Types\ConversionException;
8+
9+
/**
10+
* @since 3.1
11+
*
12+
* @author Sébastien Jean <sebastien.jean76@gmail.com>
13+
*/
14+
class InvalidPointForPHPException extends ConversionException
15+
{
16+
private static function create(string $message, mixed $value): self
17+
{
18+
return new self(\sprintf($message, \var_export($value, true)));
19+
}
20+
21+
public static function forInvalidType(mixed $value): self
22+
{
23+
return self::create('Value must be a point, %s given', $value);
24+
}
25+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MartinGeorgiev\Doctrine\DBAL\Types;
6+
7+
use Doctrine\DBAL\Platforms\AbstractPlatform;
8+
use MartinGeorgiev\Doctrine\DBAL\Types\Exceptions\InvalidPointForDatabaseException;
9+
use MartinGeorgiev\Doctrine\DBAL\Types\Exceptions\InvalidPointForPHPException;
10+
use MartinGeorgiev\Doctrine\DBAL\Types\ValueObject\Point as PointValueObject;
11+
12+
/**
13+
* Implementation of PostgreSQL POINT data type.
14+
*
15+
* @see https://www.postgresql.org/docs/17/datatype-geometric.html#DATATYPE-GEOMETRIC-POINTS
16+
* @since 3.1
17+
*
18+
* @author Sébastien Jean <sebastien.jean76@gmail.com>
19+
*/
20+
class Point extends BaseType
21+
{
22+
protected const TYPE_NAME = 'point';
23+
24+
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
25+
{
26+
if ($value === null) {
27+
return null;
28+
}
29+
30+
if (!$value instanceof PointValueObject) {
31+
throw InvalidPointForPHPException::forInvalidType($value);
32+
}
33+
34+
return (string) $value;
35+
}
36+
37+
public function convertToPHPValue($value, AbstractPlatform $platform): ?PointValueObject
38+
{
39+
if ($value === null) {
40+
return null;
41+
}
42+
43+
if (!\is_string($value)) {
44+
throw InvalidPointForDatabaseException::forInvalidType($value);
45+
}
46+
47+
try {
48+
return PointValueObject::fromString($value);
49+
} catch (\InvalidArgumentException) {
50+
throw InvalidPointForDatabaseException::forInvalidFormat($value);
51+
}
52+
}
53+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MartinGeorgiev\Doctrine\DBAL\Types;
6+
7+
use MartinGeorgiev\Doctrine\DBAL\Types\Exceptions\InvalidPointArrayItemForDatabaseException;
8+
use MartinGeorgiev\Doctrine\DBAL\Types\Exceptions\InvalidPointArrayItemForPHPException;
9+
use MartinGeorgiev\Doctrine\DBAL\Types\ValueObject\Point as PointValueObject;
10+
11+
/**
12+
* Implementation of PostgreSQL POINT[] data type.
13+
*
14+
* @see https://www.postgresql.org/docs/17/datatype-geometric.html#DATATYPE-GEOMETRIC-POINTS
15+
* @since 3.1
16+
*
17+
* @author Sébastien Jean <sebastien.jean76@gmail.com>
18+
*/
19+
class PointArray extends BaseArray
20+
{
21+
protected const TYPE_NAME = 'point[]';
22+
23+
protected function transformArrayItemForPostgres(mixed $item): string
24+
{
25+
if (!$item instanceof PointValueObject) {
26+
throw InvalidPointArrayItemForDatabaseException::isNotAPoint($item);
27+
}
28+
29+
return '"'.$item.'"';
30+
}
31+
32+
protected function transformPostgresArrayToPHPArray(string $postgresArray): array
33+
{
34+
if (!\str_starts_with($postgresArray, '{"') || !\str_ends_with($postgresArray, '"}')) {
35+
return [];
36+
}
37+
38+
$trimmedPostgresArray = \mb_substr($postgresArray, 2, -2);
39+
if ($trimmedPostgresArray === '') {
40+
return [];
41+
}
42+
43+
return \explode('","', $trimmedPostgresArray);
44+
}
45+
46+
public function transformArrayItemForPHP(mixed $item): ?PointValueObject
47+
{
48+
if ($item === null) {
49+
return null;
50+
}
51+
52+
if (!\is_string($item)) {
53+
$this->throwInvalidTypeException($item);
54+
}
55+
56+
try {
57+
return PointValueObject::fromString($item);
58+
} catch (\InvalidArgumentException) {
59+
$this->throwInvalidFormatException($item);
60+
}
61+
}
62+
63+
public function isValidArrayItemForDatabase(mixed $item): bool
64+
{
65+
return $item instanceof PointValueObject;
66+
}
67+
68+
protected function throwInvalidTypeException(mixed $value): never
69+
{
70+
throw InvalidPointArrayItemForPHPException::forInvalidType($value);
71+
}
72+
73+
protected function throwInvalidFormatException(mixed $value): never
74+
{
75+
throw InvalidPointArrayItemForPHPException::forInvalidFormat($value);
76+
}
77+
78+
protected function throwInvalidItemException(): never
79+
{
80+
throw InvalidPointArrayItemForPHPException::forInvalidFormat('Array contains invalid point items');
81+
}
82+
}

0 commit comments

Comments
 (0)