[COMPONENT][Subscription] Start component

This commit is contained in:
Diogo Peralta Cordeiro 2022-01-02 20:00:17 +00:00
parent 5fa8056899
commit 6cfb69d64b
Signed by: diogo
GPG Key ID: 18D2D35001FBFAB0
7 changed files with 77 additions and 46 deletions

View File

@ -39,6 +39,7 @@ use Component\Collection\Util\ActorControllerTrait;
use Component\Collection\Util\Controller\FeedController;
use Component\Group\Entity\GroupMember;
use Component\Group\Entity\LocalGroup;
use Component\Subscription\Entity\Subscription;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
@ -91,7 +92,7 @@ class Group extends FeedController
'group_id' => $group->getId(),
'nickname' => $nickname,
]));
DB::persist(E\Subscription::create([
DB::persist(Subscription::create([
'subscriber' => $group->getId(),
'subscribed' => $group->getId(),
]));
@ -115,7 +116,7 @@ class Group extends FeedController
} else {
if (!\is_null($actor)
&& \is_null(Cache::get(
E\Subscription::cacheKeys($actor, $group)['subscribed'],
Subscription::cacheKeys($actor, $group)['subscribed'],
fn () => DB::findOneBy('subscription', [
'subscriber' => $actor->getId(),
'subscribed' => $group->getId(),
@ -125,14 +126,14 @@ class Group extends FeedController
$subscribe_form = Form::create([['subscribe', SubmitType::class, ['label' => _m('Subscribe to this group')]]]);
$subscribe_form->handleRequest($request);
if ($subscribe_form->isSubmitted() && $subscribe_form->isValid()) {
DB::persist(E\Subscription::create([
DB::persist(Subscription::create([
'subscriber' => $actor->getId(),
'subscribed' => $group->getId(),
]));
DB::flush();
Cache::delete(E\Actor::cacheKeys($group->getId())['subscriber']);
Cache::delete(E\Actor::cacheKeys($actor->getId())['subscribed']);
Cache::delete(E\Subscription::cacheKeys($actor, $group)['subscribed']);
Cache::delete(Subscription::cacheKeys($actor, $group)['subscribed']);
}
}
}

View File

@ -21,23 +21,25 @@ declare(strict_types = 1);
// }}}
namespace App\Controller;
namespace Component\Subscription\Controller;
use App\Core\Controller\ActorController;
use function App\Core\I18n\_m;
use Component\Collection\Util\ActorControllerTrait;
use Component\Collection\Util\Controller\CircleController;
use Symfony\Component\HttpFoundation\Request;
/**
* Collection of an actor's subscribers
*/
class Subscribers extends ActorController
class Subscribers extends CircleController
{
use ActorControllerTrait;
public function subscribersByActorId(Request $request, int $id)
{
return $this->handleActorById(
$id,
fn ($actor) => [
'_template' => 'subscribers/view.html.twig',
'actor' => $actor,
'actor' => $actor,
],
);
}
@ -47,8 +49,12 @@ class Subscribers extends ActorController
return $this->handleActorByNickname(
$nickname,
fn ($actor) => [
'_template' => 'subscribers/view.html.twig',
'actor' => $actor,
'_template' => 'collection/actors.html.twig',
'title' => _m('Subscribers'),
'empty_message' => _m('No subscribers'),
'sort_options' => [],
'page' => $this->int('page') ?? 1,
'actors' => $actor->getSubscribers(),
],
);
}

View File

@ -21,23 +21,25 @@ declare(strict_types = 1);
// }}}
namespace App\Controller;
namespace Component\Subscription\Controller;
use App\Core\Controller\ActorController;
use function App\Core\I18n\_m;
use Component\Collection\Util\ActorControllerTrait;
use Component\Collection\Util\Controller\CircleController;
use Symfony\Component\HttpFoundation\Request;
/**
* Collection of an actor's subscriptions
*/
class Subscriptions extends ActorController
class Subscriptions extends CircleController
{
use ActorControllerTrait;
public function subscriptionsByActorId(Request $request, int $id)
{
return $this->handleActorById(
$id,
fn ($actor) => [
'_template' => 'subscriptions/view.html.twig',
'actor' => $actor,
'actor' => $actor,
],
);
}
@ -47,8 +49,12 @@ class Subscriptions extends ActorController
return $this->handleActorByNickname(
$nickname,
fn ($actor) => [
'_template' => 'subscriptions/view.html.twig',
'actor' => $actor,
'_template' => 'collection/actors.html.twig',
'title' => _m('Subscribers'),
'empty_message' => _m('No subscribers'),
'sort_options' => [],
'page' => $this->int('page') ?? 1,
'actors' => $actor->getSubscribers(),
],
);
}

View File

@ -9,10 +9,10 @@ use App\Core\VisibilityScope;
use App\Entity\Actor;
use App\Entity\LocalUser;
use App\Entity\Note;
use App\Entity\Subscription;
use Component\Group\Entity\GroupInbox;
use Component\Group\Entity\GroupMember;
use Component\Group\Entity\LocalGroup;
use Component\Subscription\Entity\Subscription;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;

View File

@ -37,6 +37,7 @@ use App\Util\Nickname;
use Component\Avatar\Avatar;
use Component\Language\Entity\ActorLanguage;
use Component\Language\Entity\Language;
use Component\Subscription\Entity\Subscription;
use DateTimeInterface;
use Functional as F;
@ -242,26 +243,26 @@ class Actor extends Entity
// @codeCoverageIgnoreEnd
// }}} Autocode
public const PERSON = 1;
public const GROUP = 2;
public const PERSON = 1;
public const GROUP = 2;
public const ORGANIZATION = 3;
public const BUSINESS = 4;
public const BOT = 5;
public const BUSINESS = 4;
public const BOT = 5;
public static function cacheKeys(int|self $actor_id, mixed $other = null): array
{
$actor_id = \is_int($actor_id) ? $actor_id : $actor_id->getId();
return [
'id' => "actor-id-{$actor_id}",
'nickname' => "actor-nickname-id-{$actor_id}",
'fullname' => "actor-fullname-id-{$actor_id}",
'tags' => \is_null($other) ? "actor-tags-{$actor_id}" : "actor-tags-{$actor_id}-by-{$other}", // $other is $context_id
'circles' => "actor-circles-{$actor_id}",
'subscriber' => "subscriber-{$actor_id}",
'subscribed' => "subscribed-{$actor_id}",
'id' => "actor-id-{$actor_id}",
'nickname' => "actor-nickname-id-{$actor_id}",
'fullname' => "actor-fullname-id-{$actor_id}",
'tags' => \is_null($other) ? "actor-tags-{$actor_id}" : "actor-tags-{$actor_id}-by-{$other}", // $other is $context_id
'circles' => "actor-circles-{$actor_id}",
'subscriber' => "subscriber-{$actor_id}",
'subscribed' => "subscribed-{$actor_id}",
'relative-nickname' => "actor-{$actor_id}-relative-nickname-{$other}", // $other is $nickname
'can-admin' => "actor-{$actor_id}-can-admin-{$other}", // $other is an actor id
'can-admin' => "actor-{$actor_id}-can-admin-{$other}", // $other is an actor id
];
}
@ -286,17 +287,17 @@ class Actor extends Entity
public static function getById(int $id): ?self
{
return Cache::get(self::cacheKeys($id)['id'], fn () => DB::find('actor', ['id' => $id]));
return Cache::get(self::cacheKeys($id)['id'], fn() => DB::find('actor', ['id' => $id]));
}
public static function getNicknameById(int $id): string
{
return Cache::get(self::cacheKeys($id)['nickname'], fn () => self::getById($id)->getNickname());
return Cache::get(self::cacheKeys($id)['nickname'], fn() => self::getById($id)->getNickname());
}
public static function getFullnameById(int $id): ?string
{
return Cache::get(self::cacheKeys($id)['fullname'], fn () => self::getById($id)->getFullname());
return Cache::get(self::cacheKeys($id)['fullname'], fn() => self::getById($id)->getFullname());
}
/**
@ -324,8 +325,8 @@ class Actor extends Entity
* - If null = All tags attributed to self by other actors (excludes self tags)
* - If self = Same as getSelfTags
* - otherwise = Tags that $context attributed to $this
* @param null|int $offset Offset from latest
* @param null|int $limit Max number to get
* @param null|int $offset Offset from latest
* @param null|int $limit Max number to get
*
* @return ActorTag[] resulting lists
*/
@ -334,7 +335,7 @@ class Actor extends Entity
if (\is_null($context)) {
return Cache::getList(
self::cacheKeys($this->getId())['tags'],
fn () => DB::dql(
fn() => DB::dql(
<<< 'EOQ'
SELECT tag
FROM actor_tag tag
@ -349,7 +350,7 @@ class Actor extends Entity
$context_id = \is_int($context) ? $context : $context->getId();
return Cache::getList(
self::cacheKeys($this->getId(), $context_id)['tags'],
fn () => DB::dql(
fn() => DB::dql(
<<< 'EOQ'
SELECT tag
FROM actor_tag tag
@ -367,7 +368,7 @@ class Actor extends Entity
{
return Cache::getList(
self::cacheKeys($this->getId())['circles'],
fn () => DB::findBy('actor_circle', ['tagger' => $this->getId()]),
fn() => DB::findBy('actor_circle', ['tagger' => $this->getId()]),
);
}
@ -375,10 +376,7 @@ class Actor extends Entity
{
return Cache::get(
self::cacheKeys($this->getId())[$which],
fn () => DB::dql(
"select count(s) from subscription s where s.{$column} = :{$column}", // Not injecting the parameter value
[$column => $this->getId()],
)[0][1] - ($this->getIsLocal() ? 1 : 0), // Remove self subscription if local
fn() => DB::count(Subscription::class, [$column => $this->getId()]) - ($this->getIsLocal() ? 1 : 0)
);
}
@ -387,11 +385,31 @@ class Actor extends Entity
return $this->getSubCount(which: 'subscriber', column: 'subscribed_id');
}
public function getSubscribedCount()
public function getSubscribedCount(): int
{
return $this->getSubCount(which: 'subscribed', column: 'subscriber_id');
}
public function getSubscriptions(): array
{
return DB::dql(<<<EOF
SELECT a FROM actor AS a
INNER JOIN subscription AS s
WITH a.id = s.subscribed_id
WHERE s.subscriber_id = :self AND a.id != :self
EOF, ['self' => $this->getId()]);
}
public function getSubscribers(): array
{
return DB::dql(<<<EOF
SELECT a FROM actor AS a
INNER JOIN subscription AS s
WITH a.id = s.subscriber_id
WHERE s.subscribed_id = :self AND a.id != :self
EOF, ['self' => $this->getId()]);
}
/**
* Resolve an ambiguous nickname reference, checking in following order:
* - Actors that $sender subscribes to

View File

@ -34,9 +34,9 @@ declare(strict_types = 1);
namespace App\Routes;
use App\Controller as C;
use App\Core\Router\RouteLoader;
use App\Util\Nickname;
use Component\Subscription\Controller as C;
abstract class Subscribers
{

View File

@ -34,9 +34,9 @@ declare(strict_types = 1);
namespace App\Routes;
use App\Controller as C;
use App\Core\Router\RouteLoader;
use App\Util\Nickname;
use Component\Subscription\Controller as C;
abstract class Subscriptions
{