mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
Enhanced list tags endpoint so that it can also return stats foir every tag
This commit is contained in:
@@ -28,7 +28,7 @@ return [
|
||||
Service\ShortUrlService::class => ConfigAbstractFactory::class,
|
||||
Visit\VisitLocator::class => ConfigAbstractFactory::class,
|
||||
Visit\VisitsStatsHelper::class => ConfigAbstractFactory::class,
|
||||
Service\Tag\TagService::class => ConfigAbstractFactory::class,
|
||||
Tag\TagService::class => ConfigAbstractFactory::class,
|
||||
Service\ShortUrl\DeleteShortUrlService::class => ConfigAbstractFactory::class,
|
||||
Service\ShortUrl\ShortUrlResolver::class => ConfigAbstractFactory::class,
|
||||
|
||||
@@ -58,7 +58,7 @@ return [
|
||||
Service\ShortUrlService::class => ['em', Service\ShortUrl\ShortUrlResolver::class, Util\UrlValidator::class],
|
||||
Visit\VisitLocator::class => ['em'],
|
||||
Visit\VisitsStatsHelper::class => ['em'],
|
||||
Service\Tag\TagService::class => ['em'],
|
||||
Tag\TagService::class => ['em'],
|
||||
Service\ShortUrl\DeleteShortUrlService::class => [
|
||||
'em',
|
||||
Options\DeleteShortUrlsOptions::class,
|
||||
|
||||
@@ -24,4 +24,10 @@ return static function (ClassMetadata $metadata, array $emConfig): void {
|
||||
$builder->createField('name', Types::STRING)
|
||||
->unique()
|
||||
->build();
|
||||
|
||||
$builder->createManyToMany('shortUrls', Entity\ShortUrl::class)
|
||||
->setJoinTable(determineTableName('short_urls_in_tags', $emConfig))
|
||||
->addInverseJoinColumn('short_url_id', 'id', true, false, 'CASCADE')
|
||||
->addJoinColumn('tag_id', 'id', true, false, 'CASCADE')
|
||||
->build();
|
||||
};
|
||||
|
||||
@@ -4,16 +4,19 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Entity;
|
||||
|
||||
use Doctrine\Common\Collections;
|
||||
use JsonSerializable;
|
||||
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
|
||||
|
||||
class Tag extends AbstractEntity implements JsonSerializable
|
||||
{
|
||||
private string $name;
|
||||
private Collections\Collection $shortUrls;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->shortUrls = new Collections\ArrayCollection();
|
||||
}
|
||||
|
||||
public function rename(string $name): void
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Shlinkio\Shlink\Core\Repository;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Shlinkio\Shlink\Core\Entity\Tag;
|
||||
use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
|
||||
use function Functional\map;
|
||||
|
||||
class TagRepository extends EntityRepository implements TagRepositoryInterface
|
||||
{
|
||||
@@ -21,4 +23,25 @@ class TagRepository extends EntityRepository implements TagRepositoryInterface
|
||||
|
||||
return $qb->getQuery()->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TagInfo[]
|
||||
*/
|
||||
public function findTagsWithInfo(): array
|
||||
{
|
||||
$dql = <<<DQL
|
||||
SELECT t AS tag, COUNT(DISTINCT s.id) AS shortUrlsCount, COUNT(DISTINCT v.id) AS visitsCount
|
||||
FROM Shlinkio\Shlink\Core\Entity\Tag t
|
||||
LEFT JOIN t.shortUrls s
|
||||
LEFT JOIN s.visits v
|
||||
GROUP BY tag
|
||||
ORDER BY t.name ASC
|
||||
DQL;
|
||||
$query = $this->getEntityManager()->createQuery($dql);
|
||||
|
||||
return map(
|
||||
$query->getResult(),
|
||||
fn (array $row) => new TagInfo($row['tag'], (int) $row['shortUrlsCount'], (int) $row['visitsCount']),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,14 @@ declare(strict_types=1);
|
||||
namespace Shlinkio\Shlink\Core\Repository;
|
||||
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
|
||||
|
||||
interface TagRepositoryInterface extends ObjectRepository
|
||||
{
|
||||
public function deleteByName(array $names): int;
|
||||
|
||||
/**
|
||||
* @return TagInfo[]
|
||||
*/
|
||||
public function findTagsWithInfo(): array;
|
||||
}
|
||||
|
||||
46
module/Core/src/Tag/Model/TagInfo.php
Normal file
46
module/Core/src/Tag/Model/TagInfo.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Tag\Model;
|
||||
|
||||
use JsonSerializable;
|
||||
use Shlinkio\Shlink\Core\Entity\Tag;
|
||||
|
||||
final class TagInfo implements JsonSerializable
|
||||
{
|
||||
private Tag $tag;
|
||||
private int $shortUrlsCount;
|
||||
private int $visitsCount;
|
||||
|
||||
public function __construct(Tag $tag, int $shortUrlsCount, int $visitsCount)
|
||||
{
|
||||
$this->tag = $tag;
|
||||
$this->shortUrlsCount = $shortUrlsCount;
|
||||
$this->visitsCount = $visitsCount;
|
||||
}
|
||||
|
||||
public function tag(): Tag
|
||||
{
|
||||
return $this->tag;
|
||||
}
|
||||
|
||||
public function shortUrlsCount(): int
|
||||
{
|
||||
return $this->shortUrlsCount;
|
||||
}
|
||||
|
||||
public function visitsCount(): int
|
||||
{
|
||||
return $this->visitsCount;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'tag' => $this->tag,
|
||||
'shortUrlsCount' => $this->shortUrlsCount,
|
||||
'visitsCount' => $this->visitsCount,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Service\Tag;
|
||||
namespace Shlinkio\Shlink\Core\Tag;
|
||||
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM;
|
||||
@@ -10,6 +10,8 @@ use Shlinkio\Shlink\Core\Entity\Tag;
|
||||
use Shlinkio\Shlink\Core\Exception\TagConflictException;
|
||||
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
|
||||
use Shlinkio\Shlink\Core\Repository\TagRepository;
|
||||
use Shlinkio\Shlink\Core\Repository\TagRepositoryInterface;
|
||||
use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
|
||||
use Shlinkio\Shlink\Core\Util\TagManagerTrait;
|
||||
|
||||
class TagService implements TagServiceInterface
|
||||
@@ -25,7 +27,6 @@ class TagService implements TagServiceInterface
|
||||
|
||||
/**
|
||||
* @return Tag[]
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function listTags(): array
|
||||
{
|
||||
@@ -34,6 +35,16 @@ class TagService implements TagServiceInterface
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TagInfo[]
|
||||
*/
|
||||
public function tagsInfo(): array
|
||||
{
|
||||
/** @var TagRepositoryInterface $repo */
|
||||
$repo = $this->em->getRepository(Tag::class);
|
||||
return $repo->findTagsWithInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $tagNames
|
||||
*/
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Service\Tag;
|
||||
namespace Shlinkio\Shlink\Core\Tag;
|
||||
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Shlinkio\Shlink\Core\Entity\Tag;
|
||||
use Shlinkio\Shlink\Core\Exception\TagConflictException;
|
||||
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
|
||||
use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
|
||||
|
||||
interface TagServiceInterface
|
||||
{
|
||||
@@ -16,6 +17,11 @@ interface TagServiceInterface
|
||||
*/
|
||||
public function listTags(): array;
|
||||
|
||||
/**
|
||||
* @return TagInfo[]
|
||||
*/
|
||||
public function tagsInfo(): array;
|
||||
|
||||
/**
|
||||
* @param string[] $tagNames
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ use Shlinkio\Shlink\Core\Entity\Tag;
|
||||
use Shlinkio\Shlink\Core\Exception\TagConflictException;
|
||||
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
|
||||
use Shlinkio\Shlink\Core\Repository\TagRepository;
|
||||
use Shlinkio\Shlink\Core\Service\Tag\TagService;
|
||||
use Shlinkio\Shlink\Core\Tag\TagService;
|
||||
|
||||
class TagServiceTest extends TestCase
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user