Added tag relations database interface
This commit is contained in:
parent
850e496215
commit
0bd0589e32
6 changed files with 184 additions and 11 deletions
21
TODO
21
TODO
|
@ -28,16 +28,17 @@ everything related to tags:
|
||||||
- tag editing
|
- tag editing
|
||||||
- category (from config.ini)
|
- category (from config.ini)
|
||||||
- description
|
- description
|
||||||
- implications - when entering child tag, add parent tag
|
- relations
|
||||||
- take care of recursion
|
- handle tag relations editing in frontend
|
||||||
- listing
|
- privileges
|
||||||
- sort alphabetically
|
- template
|
||||||
- sort by time of addition
|
- ajax
|
||||||
- adding
|
- handle tag relations editing in backend
|
||||||
- removing
|
- tagservice::update
|
||||||
- editing
|
- tageditformdata
|
||||||
- source tag
|
- tagcontroller
|
||||||
- target tags (allow multiple)
|
- privileges
|
||||||
|
- handle relations in autocomplete
|
||||||
|
|
||||||
refactors:
|
refactors:
|
||||||
- add enum validation in IValidatables (needs refactors of enums and
|
- add enum validation in IValidatables (needs refactors of enums and
|
||||||
|
|
|
@ -3,9 +3,14 @@ namespace Szurubooru\Dao;
|
||||||
use Szurubooru\Dao\EntityConverters\PostEntityConverter;
|
use Szurubooru\Dao\EntityConverters\PostEntityConverter;
|
||||||
use Szurubooru\Dao\EntityConverters\TagEntityConverter;
|
use Szurubooru\Dao\EntityConverters\TagEntityConverter;
|
||||||
use Szurubooru\DatabaseConnection;
|
use Szurubooru\DatabaseConnection;
|
||||||
|
use Szurubooru\Entities\Entity;
|
||||||
|
use Szurubooru\Entities\Tag;
|
||||||
|
|
||||||
class TagDao extends AbstractDao implements ICrudDao
|
class TagDao extends AbstractDao implements ICrudDao
|
||||||
{
|
{
|
||||||
|
const TAG_RELATION_IMPLICATION = 1;
|
||||||
|
const TAG_RELATION_SUGGESTION = 2;
|
||||||
|
|
||||||
public function __construct(DatabaseConnection $databaseConnection)
|
public function __construct(DatabaseConnection $databaseConnection)
|
||||||
{
|
{
|
||||||
parent::__construct(
|
parent::__construct(
|
||||||
|
@ -49,7 +54,7 @@ class TagDao extends AbstractDao implements ICrudDao
|
||||||
->disableSmartJoin()
|
->disableSmartJoin()
|
||||||
->innerJoin('postTags pt1 ON pt1.tagId = tags.id')
|
->innerJoin('postTags pt1 ON pt1.tagId = tags.id')
|
||||||
->innerJoin('postTags pt2 ON pt2.postId = pt1.postId')
|
->innerJoin('postTags pt2 ON pt2.postId = pt1.postId')
|
||||||
->where('pt2.tagId = ?', $tagId)
|
->where('pt2.tagId', $tagId)
|
||||||
->groupBy('tags.id')
|
->groupBy('tags.id')
|
||||||
->orderBy('tags.usages DESC, name ASC');
|
->orderBy('tags.usages DESC, name ASC');
|
||||||
$arrayEntities = iterator_to_array($query);
|
$arrayEntities = iterator_to_array($query);
|
||||||
|
@ -60,4 +65,87 @@ class TagDao extends AbstractDao implements ICrudDao
|
||||||
{
|
{
|
||||||
$this->deleteBy('usages', 0);
|
$this->deleteBy('usages', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function afterLoad(Entity $tag)
|
||||||
|
{
|
||||||
|
$tag->setLazyLoader(
|
||||||
|
Tag::LAZY_LOADER_IMPLIED_TAGS,
|
||||||
|
function (Tag $tag)
|
||||||
|
{
|
||||||
|
return $this->findImpliedTags($tag);
|
||||||
|
});
|
||||||
|
|
||||||
|
$tag->setLazyLoader(
|
||||||
|
Tag::LAZY_LOADER_SUGGESTED_TAGS,
|
||||||
|
function (Tag $tag)
|
||||||
|
{
|
||||||
|
return $this->findSuggested($tag);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function afterSave(Entity $tag)
|
||||||
|
{
|
||||||
|
$this->syncImpliedTags($tag);
|
||||||
|
$this->syncSuggestedTags($tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findImpliedTags(Tag $tag)
|
||||||
|
{
|
||||||
|
return $this->findRelatedTagsByType($tag, self::TAG_RELATION_IMPLICATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findSuggested(Tag $tag)
|
||||||
|
{
|
||||||
|
return $this->findRelatedTagsByType($tag, self::TAG_RELATION_SUGGESTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function syncImpliedTags($tag)
|
||||||
|
{
|
||||||
|
$this->syncRelatedTagsByType($tag, $tag->getImpliedTags(), self::TAG_RELATION_IMPLICATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function syncSuggestedTags($tag)
|
||||||
|
{
|
||||||
|
$this->syncRelatedTagsByType($tag, $tag->getSuggestedTags(), self::TAG_RELATION_SUGGESTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function syncRelatedTagsByType(Tag $tag, array $relatedTags, $type)
|
||||||
|
{
|
||||||
|
$this->fpdo->deleteFrom('tagRelations')
|
||||||
|
->where('tag1id', $tag->getId())
|
||||||
|
->where('type', $type)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$relatedTagIds = array_filter(array_unique(array_map(
|
||||||
|
function ($tag)
|
||||||
|
{
|
||||||
|
if (!$tag->getId())
|
||||||
|
throw new \RuntimeException('Unsaved entities found');
|
||||||
|
return $tag->getId();
|
||||||
|
},
|
||||||
|
$relatedTags)));
|
||||||
|
|
||||||
|
foreach ($relatedTagIds as $tagId)
|
||||||
|
{
|
||||||
|
$this->fpdo
|
||||||
|
->insertInto('tagRelations')
|
||||||
|
->values([
|
||||||
|
'tag1id' => $tag->getId(),
|
||||||
|
'tag2id' => $tagId,
|
||||||
|
'type' => $type])
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findRelatedTagsByType(Tag $tag, $type)
|
||||||
|
{
|
||||||
|
$tagId = $tag->getId();
|
||||||
|
$query = $this->fpdo->from($this->tableName)
|
||||||
|
->disableSmartJoin()
|
||||||
|
->innerJoin('tagRelations tr ON tags.id = tr.tag2id')
|
||||||
|
->where('tr.type', $type)
|
||||||
|
->where('tr.tag1id', $tagId);
|
||||||
|
$arrayEntities = iterator_to_array($query);
|
||||||
|
return $this->arrayToEntities($arrayEntities);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,9 @@ final class Tag extends Entity
|
||||||
private $creationTime;
|
private $creationTime;
|
||||||
private $banned = false;
|
private $banned = false;
|
||||||
|
|
||||||
|
const LAZY_LOADER_IMPLIED_TAGS = 'implications';
|
||||||
|
const LAZY_LOADER_SUGGESTED_TAGS = 'suggestions';
|
||||||
|
|
||||||
const META_USAGES = 'usages';
|
const META_USAGES = 'usages';
|
||||||
|
|
||||||
public function getName()
|
public function getName()
|
||||||
|
@ -44,4 +47,23 @@ final class Tag extends Entity
|
||||||
return $this->getMeta(self::META_USAGES);
|
return $this->getMeta(self::META_USAGES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getImpliedTags()
|
||||||
|
{
|
||||||
|
return $this->lazyLoad(self::LAZY_LOADER_IMPLIED_TAGS, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setImpliedTags(array $impliedTags)
|
||||||
|
{
|
||||||
|
$this->lazySave(self::LAZY_LOADER_IMPLIED_TAGS, $impliedTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSuggestedTags()
|
||||||
|
{
|
||||||
|
return $this->lazyLoad(self::LAZY_LOADER_SUGGESTED_TAGS, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSuggestedTags(array $suggestedTags)
|
||||||
|
{
|
||||||
|
$this->lazySave(self::LAZY_LOADER_SUGGESTED_TAGS, $suggestedTags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
22
src/Upgrades/Upgrade24.php
Normal file
22
src/Upgrades/Upgrade24.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\Upgrades;
|
||||||
|
use Szurubooru\DatabaseConnection;
|
||||||
|
use Szurubooru\Services\TagService;
|
||||||
|
|
||||||
|
class Upgrade24 implements IUpgrade
|
||||||
|
{
|
||||||
|
public function run(DatabaseConnection $databaseConnection)
|
||||||
|
{
|
||||||
|
$pdo = $databaseConnection->getPDO();
|
||||||
|
$driver = $databaseConnection->getDriver();
|
||||||
|
|
||||||
|
$pdo->exec(
|
||||||
|
'CREATE TABLE tagRelations (
|
||||||
|
id INTEGER PRIMARY KEY ' . ($driver === 'mysql' ? 'AUTO_INCREMENT' : 'AUTOINCREMENT') . ',
|
||||||
|
tag1id INTEGER NOT NULL,
|
||||||
|
tag2id INTEGER NOT NULL,
|
||||||
|
type INTEGER(2) NOT NULL,
|
||||||
|
UNIQUE (tag1id, tag2id, type)
|
||||||
|
)');
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,6 +39,7 @@ return [
|
||||||
$container->get(\Szurubooru\Upgrades\Upgrade21::class),
|
$container->get(\Szurubooru\Upgrades\Upgrade21::class),
|
||||||
$container->get(\Szurubooru\Upgrades\Upgrade22::class),
|
$container->get(\Szurubooru\Upgrades\Upgrade22::class),
|
||||||
$container->get(\Szurubooru\Upgrades\Upgrade23::class),
|
$container->get(\Szurubooru\Upgrades\Upgrade23::class),
|
||||||
|
$container->get(\Szurubooru\Upgrades\Upgrade24::class),
|
||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,45 @@ final class TagDaoTest extends AbstractDatabaseTestCase
|
||||||
$this->assertEntitiesEqual($tag, $actualTag);
|
$this->assertEntitiesEqual($tag, $actualTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSavingRelations()
|
||||||
|
{
|
||||||
|
$tag1 = new Tag();
|
||||||
|
$tag1->setName('test 1');
|
||||||
|
$tag1->setCreationTime(date('c'));
|
||||||
|
$tag2 = new Tag();
|
||||||
|
$tag2->setName('test 2');
|
||||||
|
$tag2->setCreationTime(date('c'));
|
||||||
|
$tag3 = new Tag();
|
||||||
|
$tag3->setName('test 3');
|
||||||
|
$tag3->setCreationTime(date('c'));
|
||||||
|
$tag4 = new Tag();
|
||||||
|
$tag4->setName('test 4');
|
||||||
|
$tag4->setCreationTime(date('c'));
|
||||||
|
$tagDao = $this->getTagDao();
|
||||||
|
$tagDao->save($tag1);
|
||||||
|
$tagDao->save($tag2);
|
||||||
|
$tagDao->save($tag3);
|
||||||
|
$tagDao->save($tag4);
|
||||||
|
|
||||||
|
$tag = new Tag();
|
||||||
|
$tag->setName('test1');
|
||||||
|
$tag->setCreationTime(date('c'));
|
||||||
|
$tag->setImpliedTags([$tag1, $tag3]);
|
||||||
|
$tag->setSuggestedTags([$tag2, $tag4]);
|
||||||
|
|
||||||
|
$this->assertGreaterThan(0, count($tag->getImpliedTags()));
|
||||||
|
$this->assertGreaterThan(0, count($tag->getSuggestedTags()));
|
||||||
|
|
||||||
|
$tagDao->save($tag);
|
||||||
|
$actualTag = $tagDao->findById($tag->getId());
|
||||||
|
|
||||||
|
$this->assertEntitiesEqual($tag, $actualTag);
|
||||||
|
$this->assertEntitiesEqual(array_values($tag->getImpliedTags()), array_values($actualTag->getImpliedTags()));
|
||||||
|
$this->assertEntitiesEqual(array_values($tag->getSuggestedTags()), array_values($actualTag->getSuggestedTags()));
|
||||||
|
$this->assertGreaterThan(0, count($actualTag->getImpliedTags()));
|
||||||
|
$this->assertGreaterThan(0, count($actualTag->getSuggestedTags()));
|
||||||
|
}
|
||||||
|
|
||||||
public function testFindByPostIds()
|
public function testFindByPostIds()
|
||||||
{
|
{
|
||||||
$pdo = $this->databaseConnection->getPDO();
|
$pdo = $this->databaseConnection->getPDO();
|
||||||
|
|
Loading…
Reference in a new issue