Skip to content

Commit 930744b

Browse files
committed
Add index to version keywords to maintain the order of the keywords from the source material
1 parent 32ac948 commit 930744b

File tree

5 files changed

+195
-21
lines changed

5 files changed

+195
-21
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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 Version20260323085127 extends AbstractMigration
11+
{
12+
public function getDescription(): string
13+
{
14+
return 'Add index to version keyword association';
15+
}
16+
17+
public function up(Schema $schema): void
18+
{
19+
$this->addSql(<<<'SQL'
20+
ALTER TABLE version_keyword DROP CONSTRAINT fk_a65a946f115d4552
21+
SQL);
22+
$this->addSql(<<<'SQL'
23+
ALTER TABLE version_keyword DROP CONSTRAINT fk_a65a946f4bbc2705
24+
SQL);
25+
$this->addSql(<<<'SQL'
26+
ALTER TABLE version_keyword DROP CONSTRAINT version_keyword_pkey
27+
SQL);
28+
$this->addSql(<<<'SQL'
29+
ALTER TABLE version_keyword ADD id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL
30+
SQL);
31+
$this->addSql(<<<'SQL'
32+
ALTER TABLE version_keyword ADD index INT DEFAULT NULL
33+
SQL);
34+
$this->addSql(<<<'SQL'
35+
UPDATE version_keyword
36+
SET index = sub.row_num - 1
37+
FROM (
38+
SELECT version_id, keyword_id, ROW_NUMBER() OVER (PARTITION BY version_id ORDER BY keyword_id) AS row_num
39+
FROM version_keyword
40+
) sub
41+
WHERE version_keyword.version_id = sub.version_id AND version_keyword.keyword_id = sub.keyword_id
42+
SQL);
43+
$this->addSql(<<<'SQL'
44+
ALTER TABLE version_keyword ALTER COLUMN index SET NOT NULL
45+
SQL);
46+
$this->addSql(<<<'SQL'
47+
ALTER TABLE
48+
version_keyword
49+
ADD
50+
CONSTRAINT FK_A65A946F115D4552 FOREIGN KEY (keyword_id) REFERENCES keyword (id) NOT DEFERRABLE
51+
SQL);
52+
$this->addSql(<<<'SQL'
53+
ALTER TABLE
54+
version_keyword
55+
ADD
56+
CONSTRAINT FK_A65A946F4BBC2705 FOREIGN KEY (version_id) REFERENCES version (id) NOT DEFERRABLE
57+
SQL);
58+
$this->addSql(<<<'SQL'
59+
ALTER TABLE version_keyword ADD PRIMARY KEY (id)
60+
SQL);
61+
}
62+
63+
public function down(Schema $schema): void
64+
{
65+
$this->addSql(<<<'SQL'
66+
ALTER TABLE version_keyword DROP CONSTRAINT FK_A65A946F4BBC2705
67+
SQL);
68+
$this->addSql(<<<'SQL'
69+
ALTER TABLE version_keyword DROP CONSTRAINT FK_A65A946F115D4552
70+
SQL);
71+
$this->addSql(<<<'SQL'
72+
ALTER TABLE version_keyword DROP CONSTRAINT version_keyword_pkey
73+
SQL);
74+
$this->addSql(<<<'SQL'
75+
ALTER TABLE version_keyword DROP id
76+
SQL);
77+
$this->addSql(<<<'SQL'
78+
ALTER TABLE version_keyword DROP index
79+
SQL);
80+
$this->addSql(<<<'SQL'
81+
ALTER TABLE
82+
version_keyword
83+
ADD
84+
CONSTRAINT fk_a65a946f4bbc2705 FOREIGN KEY (version_id) REFERENCES version (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
85+
SQL);
86+
$this->addSql(<<<'SQL'
87+
ALTER TABLE
88+
version_keyword
89+
ADD
90+
CONSTRAINT fk_a65a946f115d4552 FOREIGN KEY (keyword_id) REFERENCES keyword (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
91+
SQL);
92+
$this->addSql(<<<'SQL'
93+
ALTER TABLE version_keyword ADD PRIMARY KEY (version_id, keyword_id)
94+
SQL);
95+
}
96+
}

src/Doctrine/Entity/Keyword.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
namespace CodedMonkey\Dirigent\Doctrine\Entity;
44

55
use CodedMonkey\Dirigent\Doctrine\Repository\KeywordRepository;
6-
use Doctrine\Common\Collections\ArrayCollection;
7-
use Doctrine\Common\Collections\Collection;
86
use Doctrine\ORM\Mapping as ORM;
97

108
#[ORM\Entity(repositoryClass: KeywordRepository::class)]
@@ -18,13 +16,9 @@ class Keyword
1816
#[ORM\Column(length: 191)]
1917
private string $name;
2018

21-
#[ORM\ManyToMany(targetEntity: Version::class, mappedBy: 'keywords')]
22-
protected Collection $versions;
23-
2419
public function __construct(string $name)
2520
{
2621
$this->name = $name;
27-
$this->versions = new ArrayCollection();
2822
}
2923

3024
public function getId(): ?int

src/Doctrine/Entity/Version.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ class Version extends TrackedEntity implements \Stringable
7979
#[ORM\OrderBy(['index' => 'ASC'])]
8080
private Collection $suggest;
8181

82-
#[ORM\ManyToMany(targetEntity: Keyword::class, inversedBy: 'versions', cascade: ['persist', 'detach', 'remove'])]
82+
#[ORM\OneToMany(mappedBy: 'version', targetEntity: VersionKeyword::class, cascade: ['persist', 'detach', 'remove'])]
83+
#[ORM\OrderBy(['index' => 'ASC'])]
8384
private Collection $keywords;
8485

8586
#[ORM\Column]
@@ -354,16 +355,16 @@ public function addSuggestLink(VersionSuggestLink $suggest): void
354355
}
355356

356357
/**
357-
* @return Collection<int, Keyword>
358+
* @return Collection<int, VersionKeyword>
358359
*/
359360
public function getKeywords(): Collection
360361
{
361362
return $this->keywords;
362363
}
363364

364-
public function addKeyword(Keyword $keyword): void
365+
public function addKeyword(VersionKeyword $versionKeyword): void
365366
{
366-
$this->keywords[] = $keyword;
367+
$this->keywords[] = $versionKeyword;
367368
}
368369

369370
public function getAutoload(): array
@@ -642,8 +643,8 @@ public function getBrowsableRepositoryUrl(): ?string
642643
public function toComposerArray(): array
643644
{
644645
$keywords = [];
645-
foreach ($this->getKeywords() as $keyword) {
646-
$keywords[] = $keyword->getName();
646+
foreach ($this->getKeywords() as $versionKeyword) {
647+
$keywords[] = $versionKeyword->getKeyword()->getName();
647648
}
648649

649650
$authors = $this->getAuthors();
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
namespace CodedMonkey\Dirigent\Doctrine\Entity;
4+
5+
use Doctrine\ORM\Mapping as ORM;
6+
7+
#[ORM\Entity]
8+
class VersionKeyword
9+
{
10+
#[ORM\Id]
11+
#[ORM\Column]
12+
#[ORM\GeneratedValue]
13+
private ?int $id = null;
14+
15+
#[ORM\ManyToOne(targetEntity: Version::class, inversedBy: 'keywords')]
16+
#[ORM\JoinColumn(nullable: false)]
17+
private Version $version;
18+
19+
#[ORM\ManyToOne(targetEntity: Keyword::class)]
20+
#[ORM\JoinColumn(nullable: false)]
21+
private Keyword $keyword;
22+
23+
#[ORM\Column]
24+
private int $index;
25+
26+
public function getId(): ?int
27+
{
28+
return $this->id;
29+
}
30+
31+
public function getVersion(): Version
32+
{
33+
return $this->version;
34+
}
35+
36+
public function setVersion(Version $version): void
37+
{
38+
$this->version = $version;
39+
}
40+
41+
public function getKeyword(): Keyword
42+
{
43+
return $this->keyword;
44+
}
45+
46+
public function setKeyword(Keyword $keyword): void
47+
{
48+
$this->keyword = $keyword;
49+
}
50+
51+
public function getIndex(): int
52+
{
53+
return $this->index;
54+
}
55+
56+
public function setIndex(int $index): void
57+
{
58+
$this->index = $index;
59+
}
60+
61+
public function getName(): string
62+
{
63+
return $this->keyword->getName();
64+
}
65+
}

src/Package/PackageMetadataResolver.php

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use CodedMonkey\Dirigent\Doctrine\Entity\Version;
1313
use CodedMonkey\Dirigent\Doctrine\Entity\VersionConflictLink;
1414
use CodedMonkey\Dirigent\Doctrine\Entity\VersionDevRequireLink;
15+
use CodedMonkey\Dirigent\Doctrine\Entity\VersionKeyword;
1516
use CodedMonkey\Dirigent\Doctrine\Entity\VersionProvideLink;
1617
use CodedMonkey\Dirigent\Doctrine\Entity\VersionReplaceLink;
1718
use CodedMonkey\Dirigent\Doctrine\Entity\VersionRequireLink;
@@ -406,25 +407,42 @@ private function updateVersion(Package $package, Version $version, CompletePacka
406407

407408
// Handle keywords
408409
if ($keywordsData = $data->getKeywords()) {
409-
foreach ($version->getKeywords() as $keyword) {
410-
$keywordName = $keyword->getName();
410+
$keywords = [];
411+
$keywordIndex = 0;
412+
foreach ($keywordsData as $keywordName) {
413+
$keywords[$keywordName] = $keywordIndex++;
414+
}
415+
416+
foreach ($version->getKeywords() as $versionKeyword) {
417+
$keywordName = $versionKeyword->getKeyword()->getName();
411418
// Clear keywords that have disappeared (for updates)
412-
if (!in_array($keywordName, $keywordsData, true)) {
413-
$version->getKeywords()->removeElement($keyword);
414-
$em->remove($keyword);
419+
if (!isset($keywords[$keywordName])) {
420+
$version->getKeywords()->removeElement($versionKeyword);
421+
$em->remove($versionKeyword);
415422
} else {
423+
// Update index if it changed
424+
if ($versionKeyword->getIndex() !== $keywords[$keywordName]) {
425+
$versionKeyword->setIndex($keywords[$keywordName]);
426+
}
416427
// Clear those that are already set
417-
$index = array_search($keywordName, $keywordsData, true);
418-
unset($keywordsData[$index]);
428+
unset($keywords[$keywordName]);
419429
}
420430
}
421431

422-
foreach ($keywordsData as $keywordName) {
432+
foreach ($keywords as $keywordName => $keywordIndex) {
423433
$keyword = $this->keywordRepository->getByName($keywordName);
424-
$version->addKeyword($keyword);
434+
$versionKeyword = new VersionKeyword();
435+
$versionKeyword->setKeyword($keyword);
436+
$versionKeyword->setIndex($keywordIndex);
437+
$version->addKeyword($versionKeyword);
438+
$versionKeyword->setVersion($version);
439+
$em->persist($versionKeyword);
425440
}
426441
} elseif (count($version->getKeywords())) {
427442
// Clear existing keywords if present
443+
foreach ($version->getKeywords() as $versionKeyword) {
444+
$em->remove($versionKeyword);
445+
}
428446
$version->getKeywords()->clear();
429447
}
430448

0 commit comments

Comments
 (0)