Skip to content

Commit

Permalink
chore(seeder): add advanced (sub)decision seeders
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tomudding committed Nov 13, 2024
1 parent 8a134af commit 6fcbcca
Show file tree
Hide file tree
Showing 8 changed files with 386 additions and 16 deletions.
7 changes: 7 additions & 0 deletions config/autoload/doctrine.local.development.php.dist
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

declare(strict_types=1);

use Decision\Extensions\Doctrine\MeetingTypesType;
use Doctrine\DBAL\Driver\PDO\MySQL\Driver;

return [
Expand All @@ -41,6 +42,9 @@ return [
PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
] : [],
],
'doctrineTypeMappings' => [
MeetingTypesType::NAME => MeetingTypesType::NAME,
],
],
],
// Configuration details for the ORM.
Expand Down Expand Up @@ -73,6 +77,9 @@ return [
],
// Second level cache configuration (see doc to learn about configuration)
'second_level_cache' => [],
'types' => [
MeetingTypesType::NAME => MeetingTypesType::class,
],
],
],
'migrations_configuration' => [
Expand Down
72 changes: 72 additions & 0 deletions module/Application/src/Extensions/Doctrine/BackedEnumType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace Application\Extensions\Doctrine;

use BackedEnum;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;

use function call_user_func;

/**
* Custom mapping type for Doctrine DBAL to directly support enums in a database without having to use a native type.
*
* It is necessary to use this custom mapping type due to an apparent bug in the value conversion layer in DBAL when
* using trying to construct our (Sub)Decisions in specific scenarios (e.g. seeding the database).
*
* Due to the `final` marking of the constructor we cannot initialise {@link BackedEnumType::$enumClass} and
* {@link BackedEnumType::$name}. As such, we need to override these when creating specific mapping types.
*
* @template T of BackedEnum
*/
abstract class BackedEnumType extends Type
{
/**
* @var class-string<T> $enumClass
* @required
*/
public string $enumClass;

/**
* @required
*/
public const string NAME = '';

/**
* {@inheritDoc}
*
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingTraversableTypeHintSpecification
*/
public function getSQLDeclaration(
array $column,
AbstractPlatform $platform,
): string {
return $platform->getStringTypeDeclarationSQL($column);
}

/**
* @return T|null
*/
public function convertToPHPValue(
mixed $value,
AbstractPlatform $platform,
) {
if (empty($value)) {
return null;
}

return call_user_func([$this->enumClass, 'from'], $value);
}

/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingAnyTypeHint
*/
public function convertToDatabaseValue(
mixed $value,
AbstractPlatform $platform,
) {
return $value instanceof $this->enumClass ? $value->value : $value;
}
}
23 changes: 23 additions & 0 deletions module/Decision/src/Extensions/Doctrine/MeetingTypesType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Decision\Extensions\Doctrine;

use Application\Extensions\Doctrine\BackedEnumType;
use Decision\Model\Enums\MeetingTypes;

/**
* @extends BackedEnumType<MeetingTypes>
*/
class MeetingTypesType extends BackedEnumType
{
public string $enumClass = MeetingTypes::class;

public const string NAME = 'meeting_types';

public function getName(): string
{
return self::NAME;
}
}
6 changes: 2 additions & 4 deletions module/Decision/src/Model/Decision.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Decision\Model;

use Decision\Extensions\Doctrine\MeetingTypesType;
use Decision\Model\Enums\MeetingTypes;
use Decision\Model\SubDecision\Annulment;
use Doctrine\Common\Collections\ArrayCollection;
Expand Down Expand Up @@ -48,10 +49,7 @@ class Decision
* NOTE: This is a hack to make the meeting a primary key here.
*/
#[Id]
#[Column(
type: 'string',
enumType: MeetingTypes::class,
)]
#[Column(type: MeetingTypesType::NAME)]
protected MeetingTypes $meeting_type;

/**
Expand Down
6 changes: 2 additions & 4 deletions module/Decision/src/Model/Meeting.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Decision\Model;

use DateTime;
use Decision\Extensions\Doctrine\MeetingTypesType;
use Decision\Model\Enums\MeetingTypes;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
Expand All @@ -25,10 +26,7 @@ class Meeting
* Meeting type.
*/
#[Id]
#[Column(
type: 'string',
enumType: MeetingTypes::class,
)]
#[Column(type: MeetingTypesType::NAME)]
protected MeetingTypes $type;

/**
Expand Down
6 changes: 2 additions & 4 deletions module/Decision/src/Model/MeetingMinutes.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Decision\Model;

use Application\Model\Traits\TimestampableTrait;
use Decision\Extensions\Doctrine\MeetingTypesType;
use Decision\Model\Enums\MeetingTypes;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
Expand All @@ -27,10 +28,7 @@ class MeetingMinutes implements ResourceInterface
* Meeting type.
*/
#[Id]
#[Column(
type: 'string',
enumType: MeetingTypes::class,
)]
#[Column(type: MeetingTypesType::NAME)]
protected MeetingTypes $meeting_type;

/**
Expand Down
6 changes: 2 additions & 4 deletions module/Decision/src/Model/SubDecision.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Decision\Model;

use Decision\Extensions\Doctrine\MeetingTypesType;
use Decision\Model\Enums\MeetingTypes;
use Decision\Model\SubDecision\Abrogation;
use Decision\Model\SubDecision\Annulment;
Expand Down Expand Up @@ -99,10 +100,7 @@ abstract class SubDecision
* NOTE: This is a hack to make the decision a primary key here.
*/
#[Id]
#[Column(
type: 'string',
enumType: MeetingTypes::class,
)]
#[Column(type: MeetingTypesType::NAME)]
protected MeetingTypes $meeting_type;

/**
Expand Down
Loading

0 comments on commit 6fcbcca

Please sign in to comment.