Skip to content

Commit ca01eeb

Browse files
committed
chore(seeder): add advanced (sub)decision seeders
Utilises a custom mapping type to try to circumvent a potential ORM bug. However, this still requires a patch to ORM to fix the reverse of that issue.
1 parent c6b0c4c commit ca01eeb

File tree

8 files changed

+386
-16
lines changed

8 files changed

+386
-16
lines changed

config/autoload/doctrine.local.development.php.dist

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
declare(strict_types=1);
2222

23+
use Decision\Extensions\Doctrine\MeetingTypesType;
2324
use Doctrine\DBAL\Driver\PDO\MySQL\Driver;
2425

2526
return [
@@ -41,6 +42,9 @@ return [
4142
PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
4243
] : [],
4344
],
45+
'doctrineTypeMappings' => [
46+
MeetingTypesType::NAME => MeetingTypesType::NAME,
47+
],
4448
],
4549
],
4650
// Configuration details for the ORM.
@@ -73,6 +77,9 @@ return [
7377
],
7478
// Second level cache configuration (see doc to learn about configuration)
7579
'second_level_cache' => [],
80+
'types' => [
81+
MeetingTypesType::NAME => MeetingTypesType::class,
82+
],
7683
],
7784
],
7885
'migrations_configuration' => [
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Application\Extensions\Doctrine;
6+
7+
use BackedEnum;
8+
use Doctrine\DBAL\Platforms\AbstractPlatform;
9+
use Doctrine\DBAL\Types\Type;
10+
11+
use function call_user_func;
12+
13+
/**
14+
* Custom mapping type for Doctrine DBAL to directly support enums in a database without having to use a native type.
15+
*
16+
* It is necessary to use this custom mapping type due to an apparent bug in the value conversion layer in DBAL when
17+
* using trying to construct our (Sub)Decisions in specific scenarios (e.g. seeding the database).
18+
*
19+
* Due to the `final` marking of the constructor we cannot initialise {@link BackedEnumType::$enumClass} and
20+
* {@link BackedEnumType::$name}. As such, we need to override these when creating specific mapping types.
21+
*
22+
* @template T of BackedEnum
23+
*/
24+
abstract class BackedEnumType extends Type
25+
{
26+
/**
27+
* @var class-string<T> $enumClass
28+
* @required
29+
*/
30+
public string $enumClass;
31+
32+
/**
33+
* @required
34+
*/
35+
public const string NAME = '';
36+
37+
/**
38+
* {@inheritDoc}
39+
*
40+
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingTraversableTypeHintSpecification
41+
*/
42+
public function getSQLDeclaration(
43+
array $column,
44+
AbstractPlatform $platform,
45+
): string {
46+
return $platform->getStringTypeDeclarationSQL($column);
47+
}
48+
49+
/**
50+
* @return T|null
51+
*/
52+
public function convertToPHPValue(
53+
mixed $value,
54+
AbstractPlatform $platform,
55+
) {
56+
if (empty($value)) {
57+
return null;
58+
}
59+
60+
return call_user_func([$this->enumClass, 'from'], $value);
61+
}
62+
63+
/**
64+
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingAnyTypeHint
65+
*/
66+
public function convertToDatabaseValue(
67+
mixed $value,
68+
AbstractPlatform $platform,
69+
) {
70+
return $value instanceof $this->enumClass ? $value->value : $value;
71+
}
72+
}
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 Decision\Extensions\Doctrine;
6+
7+
use Application\Extensions\Doctrine\BackedEnumType;
8+
use Decision\Model\Enums\MeetingTypes;
9+
10+
/**
11+
* @extends BackedEnumType<MeetingTypes>
12+
*/
13+
class MeetingTypesType extends BackedEnumType
14+
{
15+
public string $enumClass = MeetingTypes::class;
16+
17+
public const string NAME = 'meeting_types';
18+
19+
public function getName(): string
20+
{
21+
return self::NAME;
22+
}
23+
}

module/Decision/src/Model/Decision.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Decision\Model;
66

7+
use Decision\Extensions\Doctrine\MeetingTypesType;
78
use Decision\Model\Enums\MeetingTypes;
89
use Decision\Model\SubDecision\Annulment;
910
use Doctrine\Common\Collections\ArrayCollection;
@@ -48,10 +49,7 @@ class Decision
4849
* NOTE: This is a hack to make the meeting a primary key here.
4950
*/
5051
#[Id]
51-
#[Column(
52-
type: 'string',
53-
enumType: MeetingTypes::class,
54-
)]
52+
#[Column(type: MeetingTypesType::NAME)]
5553
protected MeetingTypes $meeting_type;
5654

5755
/**

module/Decision/src/Model/Meeting.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Decision\Model;
66

77
use DateTime;
8+
use Decision\Extensions\Doctrine\MeetingTypesType;
89
use Decision\Model\Enums\MeetingTypes;
910
use Doctrine\Common\Collections\ArrayCollection;
1011
use Doctrine\Common\Collections\Collection;
@@ -25,10 +26,7 @@ class Meeting
2526
* Meeting type.
2627
*/
2728
#[Id]
28-
#[Column(
29-
type: 'string',
30-
enumType: MeetingTypes::class,
31-
)]
29+
#[Column(type: MeetingTypesType::NAME)]
3230
protected MeetingTypes $type;
3331

3432
/**

module/Decision/src/Model/MeetingMinutes.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Decision\Model;
66

77
use Application\Model\Traits\TimestampableTrait;
8+
use Decision\Extensions\Doctrine\MeetingTypesType;
89
use Decision\Model\Enums\MeetingTypes;
910
use Doctrine\ORM\Mapping\Column;
1011
use Doctrine\ORM\Mapping\Entity;
@@ -27,10 +28,7 @@ class MeetingMinutes implements ResourceInterface
2728
* Meeting type.
2829
*/
2930
#[Id]
30-
#[Column(
31-
type: 'string',
32-
enumType: MeetingTypes::class,
33-
)]
31+
#[Column(type: MeetingTypesType::NAME)]
3432
protected MeetingTypes $meeting_type;
3533

3634
/**

module/Decision/src/Model/SubDecision.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Decision\Model;
66

7+
use Decision\Extensions\Doctrine\MeetingTypesType;
78
use Decision\Model\Enums\MeetingTypes;
89
use Decision\Model\SubDecision\Abrogation;
910
use Decision\Model\SubDecision\Annulment;
@@ -99,10 +100,7 @@ abstract class SubDecision
99100
* NOTE: This is a hack to make the decision a primary key here.
100101
*/
101102
#[Id]
102-
#[Column(
103-
type: 'string',
104-
enumType: MeetingTypes::class,
105-
)]
103+
#[Column(type: MeetingTypesType::NAME)]
106104
protected MeetingTypes $meeting_type;
107105

108106
/**

0 commit comments

Comments
 (0)