Skip to content

Commit

Permalink
Rework the tag system (#654)
Browse files Browse the repository at this point in the history
Co-authored-by: debounced <[email protected]>
Co-authored-by: Melroy van den Berg <[email protected]>
  • Loading branch information
3 people authored May 14, 2024
1 parent 2bddbbe commit 24ed952
Show file tree
Hide file tree
Showing 124 changed files with 1,669 additions and 660 deletions.
1 change: 1 addition & 0 deletions assets/styles/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,4 @@
@import 'themes/default';
@import 'themes/solarized';
@import 'themes/tokyo-night';
@import 'components/tag';
21 changes: 21 additions & 0 deletions assets/styles/components/_tag.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.section.tag {
header {
text-align: center;

h4 {
font-size: 1.2rem;
margin-bottom: 0;
margin-top: .5rem;
}
}

.tag__actions {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
margin-top: 1rem;
margin-bottom: 2.5rem;
gap: .25rem;
}
}
10 changes: 10 additions & 0 deletions config/kbin_routes/tag.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,13 @@ tag_people:
path: tag/{name}/people
methods: [GET]
requirements: { sortBy: "%front_sort_options%" }

tag_ban:
path: /tag/{name}/ban
methods: [POST]
controller: App\Controller\Tag\TagBanController::ban

tag_unban:
path: /tag/{name}/unban
methods: [POST]
controller: App\Controller\Tag\TagBanController::unban
3 changes: 3 additions & 0 deletions config/packages/doctrine.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
types:
citext: App\DoctrineExtensions\DBAL\Types\Citext
mapping_types:
user_type: string
citext: citext

# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
Expand Down
217 changes: 217 additions & 0 deletions migrations/Version20240330101300.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20240330101300 extends AbstractMigration
{
public function getDescription(): string
{
return 'This migration moves hashtags from the entry, entry_comment, post and post_comment table to its own table, while keeping the hashtag links alive';
}

public function up(Schema $schema): void
{
$this->addSql('CREATE EXTENSION IF NOT EXISTS citext');
$this->addSql('CREATE SEQUENCE hashtag_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE SEQUENCE hashtag_link_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE TABLE hashtag (id INT NOT NULL, tag citext NOT NULL, banned BOOLEAN DEFAULT false NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE UNIQUE INDEX UNIQ_5AB52A61389B783 ON hashtag (tag)');
$this->addSql('CREATE TABLE hashtag_link (id INT NOT NULL, hashtag_id INT NOT NULL, entry_id INT DEFAULT NULL, entry_comment_id INT DEFAULT NULL, post_id INT DEFAULT NULL, post_comment_id INT DEFAULT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE INDEX IDX_83957168FB34EF56 ON hashtag_link (hashtag_id)');
$this->addSql('CREATE INDEX IDX_83957168BA364942 ON hashtag_link (entry_id)');
$this->addSql('CREATE INDEX IDX_8395716860C33421 ON hashtag_link (entry_comment_id)');
$this->addSql('CREATE INDEX IDX_839571684B89032C ON hashtag_link (post_id)');
$this->addSql('CREATE INDEX IDX_83957168DB1174D2 ON hashtag_link (post_comment_id)');
$this->addSql('ALTER TABLE hashtag_link ADD CONSTRAINT FK_83957168FB34EF56 FOREIGN KEY (hashtag_id) REFERENCES hashtag (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE hashtag_link ADD CONSTRAINT FK_83957168BA364942 FOREIGN KEY (entry_id) REFERENCES entry (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE hashtag_link ADD CONSTRAINT FK_8395716860C33421 FOREIGN KEY (entry_comment_id) REFERENCES entry_comment (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE hashtag_link ADD CONSTRAINT FK_839571684B89032C FOREIGN KEY (post_id) REFERENCES post (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE hashtag_link ADD CONSTRAINT FK_83957168DB1174D2 FOREIGN KEY (post_comment_id) REFERENCES post_comment (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');

// migrate entry tags
$select = "SELECT e.id, e.tags, keys.value::CITEXT as hashtag, e.created_at FROM entry e
JOIN LATERAL (SELECT * FROM jsonb_array_elements_text(e.tags)) as keys ON TRUE
WHERE e.tags IS NOT NULL AND jsonb_typeof(e.tags) = 'array'
UNION ALL
SELECT e.id, e.tags, keys.value::CITEXT as hashtag, e.created_at FROM entry e
JOIN LATERAL (SELECT * FROM jsonb_each_text(e.tags)) as keys ON TRUE
WHERE e.tags IS NOT NULL AND jsonb_typeof(e.tags) = 'object'
ORDER BY created_at DESC";
$foreachStatement = "IF NOT EXISTS (SELECT id FROM hashtag WHERE hashtag.tag = temprow.hashtag) THEN
INSERT INTO hashtag(id, tag) VALUES(NEXTVAL('hashtag_id_seq'), temprow.hashtag);
END IF;
IF NOT EXISTS (SELECT l.id FROM hashtag_link l
INNER JOIN hashtag def ON def.id=l.hashtag_id
WHERE l.entry_id = temprow.id AND def.tag = temprow.hashtag)
THEN
INSERT INTO hashtag_link (id, entry_id, hashtag_id) VALUES (NEXTVAL('hashtag_link_id_seq'), temprow.id, (SELECT id FROM hashtag WHERE tag = temprow.hashtag));
END IF;";

$this->addSql('DO
$do$
declare temprow record;
BEGIN
FOR temprow IN
'.$select.'
LOOP
'.$foreachStatement.'
END LOOP;
END
$do$;');

// migrate entry comments tags
$select = "SELECT e.id, e.tags, keys.value::CITEXT as hashtag, e.created_at FROM entry_comment e
JOIN LATERAL (SELECT * FROM jsonb_array_elements_text(e.tags)) as keys ON TRUE
WHERE e.tags IS NOT NULL AND jsonb_typeof(e.tags) = 'array'
UNION ALL
SELECT e.id, e.tags, keys.value::CITEXT as hashtag, e.created_at FROM entry_comment e
JOIN LATERAL (SELECT * FROM jsonb_each_text(e.tags)) as keys ON TRUE
WHERE e.tags IS NOT NULL AND jsonb_typeof(e.tags) = 'object'
ORDER BY created_at DESC";
$foreachStatement = "IF NOT EXISTS (SELECT id FROM hashtag WHERE hashtag.tag = temprow.hashtag) THEN
INSERT INTO hashtag(id, tag) VALUES(NEXTVAL('hashtag_id_seq'), temprow.hashtag);
END IF;
IF NOT EXISTS (SELECT l.id FROM hashtag_link l
INNER JOIN hashtag def ON def.id=l.hashtag_id
WHERE l.entry_comment_id = temprow.id AND def.tag = temprow.hashtag)
THEN
INSERT INTO hashtag_link (id, entry_comment_id, hashtag_id) VALUES (NEXTVAL('hashtag_link_id_seq'), temprow.id, (SELECT id FROM hashtag WHERE tag=temprow.hashtag));
END IF;";

$this->addSql('DO
$do$
declare temprow record;
BEGIN
FOR temprow IN
'.$select.'
LOOP
'.$foreachStatement.'
END LOOP;
END
$do$;');

// migrate post tags
$select = "SELECT e.id, e.tags, keys.value::CITEXT as hashtag, e.created_at FROM post e
JOIN LATERAL (SELECT * FROM jsonb_array_elements_text(e.tags)) as keys ON TRUE
WHERE e.tags IS NOT NULL AND jsonb_typeof(e.tags) = 'array'
UNION ALL
SELECT e.id, e.tags, keys.value::CITEXT as hashtag, e.created_at FROM post e
JOIN LATERAL (SELECT * FROM jsonb_each_text(e.tags)) as keys ON TRUE
WHERE e.tags IS NOT NULL AND jsonb_typeof(e.tags) = 'object'
ORDER BY created_at DESC";
$foreachStatement = "IF NOT EXISTS (SELECT id FROM hashtag WHERE hashtag.tag = temprow.hashtag) THEN
INSERT INTO hashtag(id, tag) VALUES(NEXTVAL('hashtag_id_seq'), temprow.hashtag);
END IF;
IF NOT EXISTS (SELECT l.id FROM hashtag_link l
INNER JOIN hashtag def ON def.id=l.hashtag_id
WHERE l.post_id = temprow.id AND def.tag = temprow.hashtag)
THEN
INSERT INTO hashtag_link (id, post_id, hashtag_id) VALUES (NEXTVAL('hashtag_link_id_seq'), temprow.id, (SELECT id FROM hashtag WHERE tag=temprow.hashtag));
END IF;";

$this->addSql('DO
$do$
declare temprow record;
BEGIN
FOR temprow IN
'.$select.'
LOOP
'.$foreachStatement.'
END LOOP;
END
$do$;');
// migrate post comment tags
$select = "SELECT e.id, e.tags, keys.value::CITEXT as hashtag, e.created_at FROM post_comment e
JOIN LATERAL (SELECT * FROM jsonb_array_elements_text(e.tags)) as keys ON TRUE
WHERE e.tags IS NOT NULL AND jsonb_typeof(e.tags) = 'array'
UNION ALL
SELECT e.id, e.tags, keys.value::CITEXT as hashtag, e.created_at FROM post_comment e
JOIN LATERAL (SELECT * FROM jsonb_each_text(e.tags)) as keys ON TRUE
WHERE e.tags IS NOT NULL AND jsonb_typeof(e.tags) = 'object'
ORDER BY created_at DESC";
$foreachStatement = "IF NOT EXISTS (SELECT id FROM hashtag WHERE hashtag.tag = temprow.hashtag) THEN
INSERT INTO hashtag(id, tag) VALUES(NEXTVAL('hashtag_id_seq'), temprow.hashtag);
END IF;
IF NOT EXISTS (SELECT l.id FROM hashtag_link l
INNER JOIN hashtag def ON def.id=l.hashtag_id
WHERE l.post_comment_id = temprow.id AND def.tag = temprow.hashtag)
THEN
INSERT INTO hashtag_link (id, post_comment_id, hashtag_id) VALUES (NEXTVAL('hashtag_link_id_seq'), temprow.id, (SELECT id FROM hashtag WHERE tag=temprow.hashtag));
END IF;";

$this->addSql('DO
$do$
declare temprow record;
BEGIN
FOR temprow IN
'.$select.'
LOOP
'.$foreachStatement.'
END LOOP;
END
$do$;');

$this->addSql('ALTER TABLE entry DROP COLUMN tags');
$this->addSql('ALTER TABLE entry_comment DROP COLUMN tags');
$this->addSql('ALTER TABLE post DROP COLUMN tags');
$this->addSql('ALTER TABLE post_comment DROP COLUMN tags');
}

public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE entry_comment ADD tags JSONB DEFAULT NULL');
$this->addSql('ALTER TABLE post_comment ADD tags JSONB DEFAULT NULL');
$this->addSql('ALTER TABLE post ADD tags JSONB DEFAULT NULL');
$this->addSql('ALTER TABLE entry ADD tags JSONB DEFAULT NULL');

$this->addSql('DO
$do$
declare temprow record;
BEGIN
FOR temprow IN
SELECT hl.entry_id, hl.entry_comment_id, hl.post_id, hl.post_comment_id, h.tag FROM hashtag_link hl INNER JOIN hashtag h ON h.id = hl.hashtag_id
LOOP
IF temprow.entry_id IS NOT NULL THEN
IF NOT EXISTS (SELECT id FROM entry e WHERE e.id = temprow.entry_id AND e.tags IS NOT NULL) THEN
UPDATE entry SET tags = \'[]\'::jsonb WHERE entry.id = temprow.entry_id;
END IF;
UPDATE entry SET tags = tags || to_jsonb(temprow.tag) WHERE entry.id = temprow.entry_id;
END IF;
IF temprow.entry_comment_id IS NOT NULL THEN
IF NOT EXISTS (SELECT id FROM entry_comment ec WHERE ec.id = temprow.entry_comment_id AND ec.tags IS NOT NULL) THEN
UPDATE entry_comment SET tags = \'[]\'::jsonb WHERE entry_comment.id = temprow.entry_comment_id;
END IF;
UPDATE entry_comment SET tags = tags || to_jsonb(temprow.tag) WHERE entry_comment.id = temprow.entry_comment_id;
END IF;
IF temprow.post_id IS NOT NULL THEN
IF NOT EXISTS (SELECT id FROM post p WHERE p.id = temprow.post_id AND p.tags IS NOT NULL) THEN
UPDATE post SET tags = \'[]\'::jsonb WHERE post.id = temprow.post_id;
END IF;
UPDATE post SET tags = tags || to_jsonb(temprow.tag) WHERE post.id = temprow.post_id;
END IF;
IF temprow.post_comment_id IS NOT NULL THEN
IF NOT EXISTS (SELECT id FROM post_comment pc WHERE pc.id = temprow.post_comment_id AND pc.tags IS NOT NULL) THEN
UPDATE post_comment SET tags = \'[]\'::jsonb WHERE post_comment.id = temprow.post_comment_id;
END IF;
UPDATE post_comment SET tags = tags || to_jsonb(temprow.tag) WHERE post_comment.id = temprow.post_comment_id;
END IF;
END LOOP;
END
$do$;');

$this->addSql('DROP SEQUENCE hashtag_id_seq CASCADE');
$this->addSql('DROP SEQUENCE hashtag_link_id_seq CASCADE');
$this->addSql('ALTER TABLE hashtag_link DROP CONSTRAINT FK_83957168FB34EF56');
$this->addSql('ALTER TABLE hashtag_link DROP CONSTRAINT FK_83957168BA364942');
$this->addSql('ALTER TABLE hashtag_link DROP CONSTRAINT FK_8395716860C33421');
$this->addSql('ALTER TABLE hashtag_link DROP CONSTRAINT FK_839571684B89032C');
$this->addSql('ALTER TABLE hashtag_link DROP CONSTRAINT FK_83957168DB1174D2');
$this->addSql('DROP TABLE hashtag');
$this->addSql('DROP TABLE hashtag_link');
}
}
12 changes: 7 additions & 5 deletions src/Command/MoveEntriesByTagCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return Command::FAILURE;
}

$qb = $this->entryRepository->createQueryBuilder('e');

$qb->andWhere("JSONB_CONTAINS(e.tags, '\"".$tag."\"') = true");

$entries = $qb->getQuery()->getResult();
$entries = $this->entryRepository->createQueryBuilder('e')
->where('t.tag = :tag')
->join('e.hashtags', 'h')
->join('h.hashtag', 't')
->setParameter('tag', $tag)
->getQuery()
->getResult();

foreach ($entries as $entry) {
/*
Expand Down
5 changes: 4 additions & 1 deletion src/Command/MovePostsByTagCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$qb = $this->postRepository->createQueryBuilder('p');

$qb->andWhere("JSONB_CONTAINS(p.tags, '\"".$tag."\"') = true");
$qb->andWhere('t.tag = :tag')
->join('p.hashtags', 'h')
->join('h.hashtag', 't')
->setParameter('tag', $tag);

$posts = $qb->getQuery()->getResult();

Expand Down
8 changes: 4 additions & 4 deletions src/Command/PostMagazinesUpdateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Entity\Post;
use App\Repository\MagazineRepository;
use App\Repository\PostRepository;
use App\Repository\TagLinkRepository;
use App\Service\PostManager;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
Expand All @@ -22,6 +23,7 @@ class PostMagazinesUpdateCommand extends Command
public function __construct(
private readonly PostRepository $postRepository,
private readonly PostManager $postManager,
private readonly TagLinkRepository $tagLinkRepository,
private readonly MagazineRepository $magazineRepository
) {
parent::__construct();
Expand All @@ -39,12 +41,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int

private function handleMagazine(Post $post, OutputInterface $output): void
{
if (!$post->tags) {
return;
}
$tags = $this->tagLinkRepository->getTagsOfPost($post);

$output->writeln((string) $post->getId());
foreach ($post->tags as $tag) {
foreach ($tags as $tag) {
if ($magazine = $this->magazineRepository->findOneByName($tag)) {
$output->writeln($magazine->name);
$this->postManager->changeMagazine($post, $magazine);
Expand Down
52 changes: 0 additions & 52 deletions src/Command/Update/TagsToJsonbUpdateCommand.php

This file was deleted.

Loading

0 comments on commit 24ed952

Please sign in to comment.