Skip to content

Commit 292e03d

Browse files
committed
Create generalized package link entities
1 parent 243e389 commit 292e03d

7 files changed

Lines changed: 247 additions & 1 deletion

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DoctrineMigrations;
6+
7+
use Doctrine\DBAL\Schema\Schema;
8+
use Doctrine\Migrations\AbstractMigration;
9+
10+
final class Version20250606221557 extends AbstractMigration
11+
{
12+
public function getDescription(): string
13+
{
14+
return 'Add generalized package link tables';
15+
}
16+
17+
public function up(Schema $schema): void
18+
{
19+
$this->addSql(<<<'SQL'
20+
CREATE TABLE package_provide_link (linked_package_name VARCHAR(191) NOT NULL, implementation BOOLEAN NOT NULL, package_id INT NOT NULL, PRIMARY KEY(linked_package_name, package_id, implementation))
21+
SQL);
22+
$this->addSql(<<<'SQL'
23+
CREATE INDEX IDX_BB570504F44CABFF ON package_provide_link (package_id)
24+
SQL);
25+
$this->addSql(<<<'SQL'
26+
CREATE TABLE package_require_link (linked_package_name VARCHAR(191) NOT NULL, dev_dependency BOOLEAN NOT NULL, package_id INT NOT NULL, PRIMARY KEY(linked_package_name, package_id, dev_dependency))
27+
SQL);
28+
$this->addSql(<<<'SQL'
29+
CREATE INDEX IDX_D4456EB9F44CABFF ON package_require_link (package_id)
30+
SQL);
31+
$this->addSql(<<<'SQL'
32+
CREATE TABLE package_suggest_link (linked_package_name VARCHAR(191) NOT NULL, package_id INT NOT NULL, PRIMARY KEY(linked_package_name, package_id))
33+
SQL);
34+
$this->addSql(<<<'SQL'
35+
CREATE INDEX IDX_70C2B280F44CABFF ON package_suggest_link (package_id)
36+
SQL);
37+
$this->addSql(<<<'SQL'
38+
ALTER TABLE package_provide_link ADD CONSTRAINT FK_BB570504F44CABFF FOREIGN KEY (package_id) REFERENCES package (id) NOT DEFERRABLE INITIALLY IMMEDIATE
39+
SQL);
40+
$this->addSql(<<<'SQL'
41+
ALTER TABLE package_require_link ADD CONSTRAINT FK_D4456EB9F44CABFF FOREIGN KEY (package_id) REFERENCES package (id) NOT DEFERRABLE INITIALLY IMMEDIATE
42+
SQL);
43+
$this->addSql(<<<'SQL'
44+
ALTER TABLE package_suggest_link ADD CONSTRAINT FK_70C2B280F44CABFF FOREIGN KEY (package_id) REFERENCES package (id) NOT DEFERRABLE INITIALLY IMMEDIATE
45+
SQL);
46+
}
47+
48+
public function down(Schema $schema): void
49+
{
50+
$this->addSql(<<<'SQL'
51+
ALTER TABLE package_provide_link DROP CONSTRAINT FK_BB570504F44CABFF
52+
SQL);
53+
$this->addSql(<<<'SQL'
54+
ALTER TABLE package_require_link DROP CONSTRAINT FK_D4456EB9F44CABFF
55+
SQL);
56+
$this->addSql(<<<'SQL'
57+
ALTER TABLE package_suggest_link DROP CONSTRAINT FK_70C2B280F44CABFF
58+
SQL);
59+
$this->addSql(<<<'SQL'
60+
DROP TABLE package_provide_link
61+
SQL);
62+
$this->addSql(<<<'SQL'
63+
DROP TABLE package_require_link
64+
SQL);
65+
$this->addSql(<<<'SQL'
66+
DROP TABLE package_suggest_link
67+
SQL);
68+
}
69+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace CodedMonkey\Dirigent\Doctrine\Entity;
4+
5+
use Doctrine\ORM\Mapping as ORM;
6+
7+
#[ORM\MappedSuperclass]
8+
abstract class AbstractPackageLink
9+
{
10+
protected Package $package;
11+
12+
#[ORM\Id, ORM\Column(length: 191)]
13+
private string $linkedPackageName;
14+
15+
public function getPackage(): Package
16+
{
17+
return $this->package;
18+
}
19+
20+
public function setPackage(Package $package): void
21+
{
22+
$this->package = $package;
23+
}
24+
25+
public function getLinkedPackageName(): string
26+
{
27+
return $this->linkedPackageName;
28+
}
29+
30+
public function setLinkedPackageName(string $packageName): void
31+
{
32+
$this->linkedPackageName = $packageName;
33+
}
34+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace CodedMonkey\Dirigent\Doctrine\Entity;
4+
5+
use Doctrine\ORM\Mapping as ORM;
6+
7+
#[ORM\Entity]
8+
class PackageProvideLink extends AbstractPackageLink
9+
{
10+
#[ORM\Id, ORM\ManyToOne(targetEntity: Package::class), ORM\JoinColumn(nullable: false)]
11+
protected Package $package;
12+
13+
#[ORM\Id, ORM\Column]
14+
private bool $implementation;
15+
16+
public function isImplementation(): bool
17+
{
18+
return $this->implementation;
19+
}
20+
21+
public function setImplementation(bool $implementation): static
22+
{
23+
$this->implementation = $implementation;
24+
25+
return $this;
26+
}
27+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace CodedMonkey\Dirigent\Doctrine\Entity;
4+
5+
use Doctrine\ORM\Mapping as ORM;
6+
7+
#[ORM\Entity]
8+
class PackageRequireLink extends AbstractPackageLink
9+
{
10+
#[ORM\Id, ORM\ManyToOne(targetEntity: Package::class), ORM\JoinColumn(nullable: false)]
11+
protected Package $package;
12+
13+
#[ORM\Id, ORM\Column]
14+
private bool $devDependency;
15+
16+
public function isDevDependency(): bool
17+
{
18+
return $this->devDependency;
19+
}
20+
21+
public function setDevDependency(bool $devDependency): static
22+
{
23+
$this->devDependency = $devDependency;
24+
25+
return $this;
26+
}
27+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace CodedMonkey\Dirigent\Doctrine\Entity;
4+
5+
use Doctrine\ORM\Mapping as ORM;
6+
7+
#[ORM\Entity]
8+
class PackageSuggestLink extends AbstractPackageLink
9+
{
10+
#[ORM\Id, ORM\ManyToOne(targetEntity: Package::class), ORM\JoinColumn(nullable: false)]
11+
protected Package $package;
12+
}

src/Doctrine/Repository/PackageRepository.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public function save(Package $entity, bool $flush = false): void
4040

4141
public function remove(Package $entity, bool $flush = false): void
4242
{
43+
$this->deletePackageLinks($entity->getId());
44+
4345
$this->getEntityManager()->remove($entity);
4446

4547
if ($flush) {
@@ -82,4 +84,56 @@ public function getAllPackageIds(): array
8284

8385
return $connection->fetchAllAssociative('SELECT id FROM package ORDER BY id');
8486
}
87+
88+
public function updatePackageLinks(int $packageId, int $versionId): void
89+
{
90+
$connection = $this->getEntityManager()->getConnection();
91+
$connection->beginTransaction();
92+
93+
try {
94+
$this->deletePackageLinks($packageId);
95+
96+
$connection->executeStatement(<<<'SQL'
97+
INSERT INTO package_provide_link (linked_package_name, implementation, package_id)
98+
SELECT linked_package_name, FALSE, :id
99+
FROM version_provide_link
100+
WHERE version_id = :version AND linked_package_name NOT LIKE '%-implementation'
101+
SQL,
102+
['id' => $packageId, 'version' => $versionId],
103+
);
104+
$connection->executeStatement(<<<'SQL'
105+
INSERT INTO package_provide_link (linked_package_name, implementation, package_id)
106+
SELECT SUBSTRING(linked_package_name, 1, LENGTH(linked_package_name) - 15), TRUE, :id
107+
FROM version_provide_link
108+
WHERE version_id = :version AND linked_package_name LIKE '%-implementation'
109+
SQL,
110+
['id' => $packageId, 'version' => $versionId],
111+
);
112+
$connection->executeStatement(
113+
'INSERT INTO package_require_link (linked_package_name, dev_dependency, package_id) SELECT linked_package_name, FALSE, :id FROM version_require_link WHERE version_id = :version',
114+
['id' => $packageId, 'version' => $versionId],
115+
);
116+
$connection->executeStatement(
117+
'INSERT INTO package_require_link (linked_package_name, dev_dependency, package_id) SELECT linked_package_name, TRUE, :id FROM version_dev_require_link WHERE version_id = :version',
118+
['id' => $packageId, 'version' => $versionId],
119+
);
120+
$connection->executeStatement(
121+
'INSERT INTO package_suggest_link (linked_package_name, package_id) SELECT linked_package_name, :id FROM version_suggest_link WHERE version_id = :version',
122+
['id' => $packageId, 'version' => $versionId],
123+
);
124+
} catch (\Throwable $exception) {
125+
$connection->rollBack();
126+
throw $exception;
127+
}
128+
129+
$connection->commit();
130+
}
131+
132+
public function deletePackageLinks(int $packageId): void
133+
{
134+
$connection = $this->getEntityManager()->getConnection();
135+
$connection->executeStatement('DELETE FROM package_provide_link WHERE package_id = :id', ['id' => $packageId]);
136+
$connection->executeStatement('DELETE FROM package_require_link WHERE package_id = :id', ['id' => $packageId]);
137+
$connection->executeStatement('DELETE FROM package_suggest_link WHERE package_id = :id', ['id' => $packageId]);
138+
}
85139
}

src/Package/PackageMetadataResolver.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use CodedMonkey\Dirigent\Doctrine\Entity\VersionReplaceLink;
1717
use CodedMonkey\Dirigent\Doctrine\Entity\VersionRequireLink;
1818
use CodedMonkey\Dirigent\Doctrine\Entity\VersionSuggestLink;
19+
use CodedMonkey\Dirigent\Doctrine\Repository\PackageRepository;
1920
use CodedMonkey\Dirigent\Doctrine\Repository\RegistryRepository;
2021
use CodedMonkey\Dirigent\Doctrine\Repository\VersionRepository;
2122
use CodedMonkey\Dirigent\Message\DumpPackageProvider;
@@ -56,6 +57,7 @@ public function __construct(
5657
private MessageBusInterface $messenger,
5758
private EntityManagerInterface $entityManager,
5859
private RegistryRepository $registryRepository,
60+
private PackageRepository $packageRepository,
5961
private VersionRepository $versionRepository,
6062
) {
6163
}
@@ -162,6 +164,9 @@ private function resolveVcsRepository(Package $package): void
162164
private function updatePackage(Package $package, array $composerPackages, ?VcsDriverInterface $driver = null): void
163165
{
164166
$existingVersions = $this->versionRepository->getVersionMetadataForUpdate($package);
167+
$processedVersions = [];
168+
/** @var ?string $primaryVersionName Version name to use as package link source */
169+
$primaryVersionName = null;
165170

166171
foreach ($composerPackages as $composerPackage) {
167172
if ($composerPackage instanceof AliasPackage) {
@@ -175,10 +180,28 @@ private function updatePackage(Package $package, array $composerPackages, ?VcsDr
175180
}
176181

177182
$this->updateVersion($package, $version, $composerPackage, $driver);
183+
$versionName = $version->getNormalizedVersion();
178184

179-
unset($existingVersions[$version->getNormalizedVersion()]);
185+
// Use the first version which should be the highest stable version by default
186+
if (null === $primaryVersionName) {
187+
$primaryVersionName = $versionName;
188+
}
189+
// If default branch is present however we prefer that as the canonical package link source
190+
if ($version->isDefaultBranch()) {
191+
$primaryVersionName = $versionName;
192+
}
193+
194+
$processedVersions[$versionName] = $version;
195+
unset($existingVersions[$versionName]);
196+
}
197+
198+
if ($primaryVersionName) {
199+
$primaryVersion = $processedVersions[$primaryVersionName];
200+
201+
$this->packageRepository->updatePackageLinks($package->getId(), $primaryVersion->getId());
180202
}
181203

204+
// Remove outdated versions
182205
foreach ($existingVersions as $version) {
183206
$versionEntity = $this->versionRepository->find($version['id']);
184207

0 commit comments

Comments
 (0)