Skip to content

Commit 0a00da4

Browse files
authored
Merge pull request #43 from wmde/doctrine-update-2024
Update Doctrine ORM and DBAL
2 parents c73e003 + 9290c31 commit 0a00da4

22 files changed

+245
-126
lines changed

bin/doctrine

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
use Doctrine\DBAL\DriverManager;
5+
use Doctrine\DBAL\Tools\DsnParser;
6+
use Doctrine\ORM\EntityManager;
7+
use Doctrine\ORM\ORMSetup;
8+
use Doctrine\ORM\Tools\Console\ConsoleRunner;
9+
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
10+
use Symfony\Component\Dotenv\Dotenv;
11+
use WMDE\Fundraising\AddressChangeContext\AddressChangeContextFactory;
12+
13+
require __DIR__.'/../vendor/autoload.php';
14+
15+
$dotenv = new Dotenv();
16+
$dotenv->load( __DIR__ . '/../.env' );
17+
18+
function createEntityManager(): EntityManager {
19+
if (empty( $_ENV['DB_DSN'] ) ) {
20+
echo "You must set the database connection string in 'DB_DSN'\n";
21+
exit(1);
22+
}
23+
$dsnParser = new DsnParser(['mysql' => 'pdo_mysql']);
24+
$connectionParams = $dsnParser
25+
->parse( $_ENV['DB_DSN'] );
26+
$connection = DriverManager::getConnection( $connectionParams );
27+
28+
$contextFactory = new AddressChangeContextFactory();
29+
$contextFactory->registerCustomTypes( $connection );
30+
$doctrineConfig = ORMSetup::createXMLMetadataConfiguration(
31+
$contextFactory->getDoctrineMappingPaths(),
32+
true
33+
);
34+
35+
return new EntityManager( $connection, $doctrineConfig );
36+
}
37+
38+
39+
ConsoleRunner::run(
40+
new SingleManagerProvider(createEntityManager()),
41+
[]
42+
);

composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
"license": "GPL-2.0-or-later",
55
"require": {
66
"php": ">=8.1",
7-
"doctrine/orm": "^2.11",
8-
"doctrine/dbal": "^3.3",
7+
"doctrine/orm": "~2.18 | ~3.0",
8+
"doctrine/dbal": "~3.8 | ~4.0",
99
"ramsey/uuid": "^4.0",
1010
"wmde/freezable-value-object": "~2.0"
1111
},
1212
"require-dev": {
1313
"phpunit/phpunit": "~9.2",
1414
"symfony/cache": "^5.3",
15+
"symfony/dotenv": "~6.4",
1516
"wmde/fundraising-phpcs": "~10.0",
1617
"phpmd/phpmd": "~2.6",
1718
"phpstan/phpstan": "^1.2",

config/DoctrineClassMapping/WMDE.Fundraising.AddressChangeContext.Domain.Model.AddressChange.dcm.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
<embedded name="identifier" class="WMDE\Fundraising\AddressChangeContext\Domain\Model\AddressChangeId" column-prefix="current_" />
1616
<embedded name="previousIdentifier" class="AddressChangeId" column-prefix="previous_" />
1717

18-
<field name="addressType" type="string" column="address_type" length="10" nullable="false" />
18+
<field name="addressType" type="AddressType" column="address_type" length="10" nullable="false" />
1919
<field name="externalId" type="integer" column="external_id" nullable="false" />
2020
<field name="externalIdType" type="string" column="external_id_type" length="10" nullable="false" />
2121
<field name="exportDate" type="datetime" column="export_date" nullable="true" />
2222
<field name="createdAt" type="datetime" column="created_at" nullable="false" />
2323
<field name="modifiedAt" type="datetime" column="modified_at" nullable="false" />
2424
<field name="donationReceipt" type="boolean" column="donation_receipt" nullable="false" />
2525

26-
<many-to-one field="address" target-entity="Address" fetch="LAZY">
26+
<many-to-one field="address" target-entity="Address" fetch="EAGER">
2727
<join-columns>
2828
<join-column name="address_id" referenced-column-name="id"/>
2929
</join-columns>

src/AddressChangeContextFactory.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
namespace WMDE\Fundraising\AddressChangeContext;
66

77
use Doctrine\Common\EventSubscriber;
8+
use Doctrine\DBAL\Connection;
9+
use Doctrine\DBAL\Types\Type;
810

911
/**
1012
* @license GPL-2.0-or-later
@@ -34,4 +36,17 @@ public function newEventSubscribers(): array {
3436
return [];
3537
}
3638

39+
public function registerCustomTypes( Connection $connection ): void {
40+
$this->registerDoctrinePaymentIntervalType( $connection );
41+
}
42+
43+
public function registerDoctrinePaymentIntervalType( Connection $connection ): void {
44+
static $isRegistered = false;
45+
if ( $isRegistered ) {
46+
return;
47+
}
48+
Type::addType( 'AddressType', 'WMDE\Fundraising\AddressChangeContext\DataAccess\DoctrineTypes\AddressType' );
49+
$connection->getDatabasePlatform()->registerDoctrineTypeMapping( 'AddressType', 'AddressType' );
50+
$isRegistered = true;
51+
}
3752
}

src/DataAccess/DoctrineAddressChangeRepository.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,8 @@ public function getAddressChangeByUuids( string $currentIdentifier, string $prev
2424
return $this->entityManager->getRepository( AddressChange::class )->createQueryBuilder( 'ac' )
2525
->where( 'ac.identifier.identifier = :currentIdentifier' )
2626
->orWhere( 'ac.previousIdentifier.identifier = :previousIdentifier' )
27-
->setParameters( [
28-
'currentIdentifier' => $currentIdentifier,
29-
'previousIdentifier' => $previousIdentifier
30-
] )
27+
->setParameter( 'currentIdentifier', $currentIdentifier )
28+
->setParameter( 'previousIdentifier', $previousIdentifier )
3129
->getQuery()
3230
->getOneOrNullResult();
3331
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace WMDE\Fundraising\AddressChangeContext\DataAccess\DoctrineTypes;
4+
5+
use Doctrine\DBAL\Platforms\AbstractPlatform;
6+
use Doctrine\DBAL\Types\Type;
7+
use WMDE\Fundraising\AddressChangeContext\Domain\Model\AddressType as DomainAddressType;
8+
9+
class AddressType extends Type {
10+
public function getSQLDeclaration( array $column, AbstractPlatform $platform ): string {
11+
return 'VARCHAR(10)';
12+
}
13+
14+
public function convertToPHPValue( mixed $value, AbstractPlatform $platform ): DomainAddressType {
15+
return match ( $value ) {
16+
'person' => DomainAddressType::Person,
17+
'company' => DomainAddressType::Company,
18+
default => throw new \InvalidArgumentException(
19+
"Could not convert address type string ({$value}) to enum"
20+
),
21+
};
22+
}
23+
24+
public function convertToDatabaseValue( mixed $value, AbstractPlatform $platform ): string {
25+
return match ( $value ) {
26+
DomainAddressType::Person => 'person',
27+
DomainAddressType::Company => 'company',
28+
default => throw new \InvalidArgumentException(
29+
"Could not convert address type enum ({$value}) to string"
30+
),
31+
};
32+
}
33+
34+
/**
35+
* @codeCoverageIgnore
36+
* @return string
37+
*/
38+
public function getName(): string {
39+
return 'AddressType';
40+
}
41+
42+
}

src/Domain/Model/Address.php

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@
88

99
class Address {
1010

11-
private const TYPE_PERSONAL = 'personal';
12-
13-
private const TYPE_COMPANY = 'company';
14-
1511
/**
1612
* @var int|null
1713
* @phpstan-ignore-next-line
@@ -36,8 +32,6 @@ class Address {
3632

3733
private string $country = '';
3834

39-
private string $addressType;
40-
4135
private function __construct(
4236
string $salutation,
4337
string $company,
@@ -47,8 +41,8 @@ private function __construct(
4741
string $address,
4842
string $postcode,
4943
string $city,
50-
string $country,
51-
string $addressType ) {
44+
string $country
45+
) {
5246
$this->salutation = $salutation;
5347
$this->company = $company;
5448
$this->title = $title;
@@ -58,7 +52,6 @@ private function __construct(
5852
$this->postcode = $postcode;
5953
$this->city = $city;
6054
$this->country = $country;
61-
$this->addressType = $addressType;
6255
}
6356

6457
public static function newPersonalAddress(
@@ -78,7 +71,7 @@ public static function newPersonalAddress(
7871
self::assertNotEmpty( 'City', $city );
7972
self::assertNotEmpty( 'Country', $country );
8073

81-
return new self( $salutation, '', $title, $firstName, $lastName, $address, $postcode, $city, $country, self::TYPE_PERSONAL );
74+
return new self( $salutation, '', $title, $firstName, $lastName, $address, $postcode, $city, $country );
8275
}
8376

8477
public static function newCompanyAddress(
@@ -92,7 +85,7 @@ public static function newCompanyAddress(
9285
self::assertNotEmpty( 'Post Code', $postcode );
9386
self::assertNotEmpty( 'City', $city );
9487
self::assertNotEmpty( 'Country', $country );
95-
return new self( '', $company, '', '', '', $address, $postcode, $city, $country, self::TYPE_COMPANY );
88+
return new self( '', $company, '', '', '', $address, $postcode, $city, $country );
9689
}
9790

9891
private static function assertNotEmpty( string $field, string $value ): void {
@@ -101,14 +94,6 @@ private static function assertNotEmpty( string $field, string $value ): void {
10194
}
10295
}
10396

104-
public function isPersonalAddress(): bool {
105-
return $this->addressType === self::TYPE_PERSONAL;
106-
}
107-
108-
public function isCompanyAddress(): bool {
109-
return $this->addressType === self::TYPE_COMPANY;
110-
}
111-
11297
public function getSalutation(): string {
11398
return $this->salutation;
11499
}

src/Domain/Model/AddressChange.php

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@
1313
* The recommended way to construct this class is through the AddressChangeBuilder
1414
*/
1515
class AddressChange {
16-
17-
public const ADDRESS_TYPE_PERSON = 'person';
18-
public const ADDRESS_TYPE_COMPANY = 'company';
19-
2016
public const EXTERNAL_ID_TYPE_DONATION = 'donation';
2117
public const EXTERNAL_ID_TYPE_MEMBERSHIP = 'membership';
2218

@@ -30,44 +26,32 @@ class AddressChange {
3026
*/
3127
private ?int $id;
3228

33-
private AddressChangeId $identifier;
34-
3529
private AddressChangeId $previousIdentifier;
3630

37-
private ?Address $address;
38-
39-
private string $addressType;
40-
4131
private bool $donationReceipt;
4232

43-
private int $externalId;
44-
45-
private string $externalIdType;
46-
4733
private ?\DateTimeInterface $exportDate;
4834

4935
private \DateTimeInterface $createdAt;
5036

5137
private \DateTimeInterface $modifiedAt;
5238

53-
public function __construct( string $addressType, string $externalIdType, int $externalId, AddressChangeId $identifier,
54-
?Address $address = null, ?\DateTime $createdAt = null ) {
55-
$this->addressType = $addressType;
56-
$this->identifier = $identifier;
39+
public function __construct(
40+
private readonly AddressType $addressType,
41+
private readonly string $externalIdType,
42+
private readonly int $externalId,
43+
private AddressChangeId $identifier,
44+
private ?Address $address = null,
45+
?\DateTime $createdAt = null
46+
) {
5747
$this->previousIdentifier = $identifier;
58-
$this->address = $address;
59-
if ( $addressType !== self::ADDRESS_TYPE_PERSON && $addressType !== self::ADDRESS_TYPE_COMPANY ) {
60-
throw new \InvalidArgumentException( 'Invalid address type' );
61-
}
6248
if ( $externalIdType !== self::EXTERNAL_ID_TYPE_DONATION && $externalIdType !== self::EXTERNAL_ID_TYPE_MEMBERSHIP ) {
6349
throw new \InvalidArgumentException( 'Invalid external reference type' );
6450
}
6551
$this->exportDate = null;
6652
$this->createdAt = $createdAt ?? new \DateTime();
6753
$this->modifiedAt = clone $this->createdAt;
6854
$this->donationReceipt = true;
69-
$this->externalId = $externalId;
70-
$this->externalIdType = $externalIdType;
7155
}
7256

7357
public function performAddressChange( Address $address, AddressChangeId $newIdentifier ): void {
@@ -96,11 +80,11 @@ public function getAddress(): ?Address {
9680
}
9781

9882
public function isPersonalAddress(): bool {
99-
return $this->addressType === self::ADDRESS_TYPE_PERSON;
83+
return $this->addressType === AddressType::Person;
10084
}
10185

10286
public function isCompanyAddress(): bool {
103-
return $this->addressType === self::ADDRESS_TYPE_COMPANY;
87+
return $this->addressType === AddressType::Company;
10488
}
10589

10690
public function isOptedIntoDonationReceipt(): bool {

src/Domain/Model/AddressChangeBuilder.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class AddressChangeBuilder {
1010

1111
private static ?UuidGenerator $uuidGenerator = null;
1212

13-
private ?string $addressType;
13+
private ?AddressType $addressType;
1414
private ?string $referenceType;
1515
private ?int $referenceId;
1616
private AddressChangeId $identifier;
@@ -34,14 +34,14 @@ public static function create( ?AddressChangeId $identifier = null, ?Address $ad
3434
}
3535

3636
public function forPerson(): self {
37-
return $this->setAddressType( AddressChange::ADDRESS_TYPE_PERSON );
37+
return $this->setAddressType( AddressType::Person );
3838
}
3939

4040
public function forCompany(): self {
41-
return $this->setAddressType( AddressChange::ADDRESS_TYPE_COMPANY );
41+
return $this->setAddressType( AddressType::Company );
4242
}
4343

44-
private function setAddressType( string $addressType ): self {
44+
public function setAddressType( AddressType $addressType ): self {
4545
if ( $this->addressType !== null ) {
4646
throw new \RuntimeException( 'You can only specify address type once' );
4747
}

src/Domain/Model/AddressType.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
declare( strict_types=1 );
3+
4+
namespace WMDE\Fundraising\AddressChangeContext\Domain\Model;
5+
6+
enum AddressType {
7+
case Person;
8+
case Company;
9+
}

0 commit comments

Comments
 (0)