[PLUGIN][Directory] Refactor directory controller, so it's hopefully clearer what's happening

This commit is contained in:
Hugo Sales 2022-01-01 20:50:04 +00:00
parent 175c98b043
commit f6311debbf
Signed by: someonewithpc
GPG Key ID: 7D0C7EAFC9D835A0

View File

@ -24,38 +24,45 @@ declare(strict_types = 1);
namespace Plugin\Directory\Controller; namespace Plugin\Directory\Controller;
use App\Core\DB\DB; use App\Core\DB\DB;
use function App\Core\I18n\_m;
use App\Entity\Actor; use App\Entity\Actor;
use App\Util\Exception\BugFoundException; use App\Util\Exception\BugFoundException;
use App\Util\Exception\ClientException; use App\Util\Exception\ClientException;
use Component\Feed\Util\FeedController; use Component\Feed\Util\FeedController;
use function App\Core\I18n\_m;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
class Directory extends FeedController class Directory extends FeedController
{ {
public const PER_PAGE = 32;
public const ALLOWED_FIELDS = ['nickname', 'created', 'modified', 'activity', 'subscribers'];
const PER_PAGE = 32; /**
const ALLOWED_FIELDS = ['nickname', 'created', 'modified', 'activity', 'subscribers']; * Function responsible for displaying a list of actors of a given
* $actor_type, sorted by the `order_by` GET parameter, if given
*/
private function impl(Request $request, string $template, int $actor_type): array private function impl(Request $request, string $template, int $actor_type): array
{ {
$page = $this->int('page') ?? 1; if ($actor_type !== Actor::PERSON && $actor_type !== Actor::GROUP) {
$limit = self::PER_PAGE; throw new BugFoundException("Unimplemented for actor type: {$actor_type}");
}
$page = $this->int('page') ?? 1;
$limit = self::PER_PAGE;
$offset = self::PER_PAGE * ($page - 1); $offset = self::PER_PAGE * ($page - 1);
// -------- Figure out the order by field and operator --------
$order_by_qs = $this->string('order_by'); $order_by_qs = $this->string('order_by');
if (!\is_null($order_by_qs) && mb_detect_encoding($order_by_qs, 'ASCII', strict: true) !== false) { if (!\is_null($order_by_qs) && mb_detect_encoding($order_by_qs, 'ASCII', strict: true) !== false) {
$order_by_op = mb_substr($order_by_qs, -1);
$order_by_op = substr($order_by_qs, -1);
if (\in_array($order_by_op, ['^', '<'])) { if (\in_array($order_by_op, ['^', '<'])) {
$order_by_field = substr($order_by_qs, 0, -1); $order_by_field = mb_substr($order_by_qs, 0, -1);
$order_by_op = 'ASC'; $order_by_op = 'ASC';
} else if (\in_array($order_by_op, ['v', '>'])) { } elseif (\in_array($order_by_op, ['v', '>'])) {
$order_by_field = substr($order_by_qs, 0, -1); $order_by_field = mb_substr($order_by_qs, 0, -1);
$order_by_op = 'DESC'; $order_by_op = 'DESC';
} else { } else {
$order_by_field = $order_by_qs; $order_by_field = $order_by_qs;
$order_by_op = 'ASC'; $order_by_op = 'ASC';
} }
if (!\in_array($order_by_field, self::ALLOWED_FIELDS)) { if (!\in_array($order_by_field, self::ALLOWED_FIELDS)) {
@ -63,16 +70,15 @@ class Directory extends FeedController
} }
} else { } else {
$order_by_field = 'nickname'; $order_by_field = 'nickname';
$order_by_op = 'ASC'; $order_by_op = 'ASC';
} }
$order_by = [$order_by_field => $order_by_op]; $order_by = [$order_by_field => $order_by_op];
$route = $request->get('_route'); // -------- *** --------
$query_fn = function (int $actor_type, string $table, string $join_field) use ($limit, $offset) { // -------- Query builder for selecting actors joined with another table, namely activity and group_inbox --------
return function (string $func, string $order) use ($actor_type, $table, $join_field, $limit, $offset) { $general_query_fn_fn = function (string $func, string $order) use ($limit, $offset) {
return DB::sql( return fn (string $table, string $join_field) => fn (int $actor_type) => DB::sql(
<<<EOQ <<<EOQ
select {select} select {select}
from actor actr from actor actr
join ( join (
@ -84,69 +90,52 @@ class Directory extends FeedController
order by actor_activity.{$order} order by actor_activity.{$order}
limit :limit offset :offset limit :limit offset :offset
EOQ, EOQ,
[ [
'type' => $actor_type, 'type' => $actor_type,
'limit' => $limit, 'limit' => $limit,
'offset' => $offset, 'offset' => $offset,
], ],
['actr' => Actor::class] ['actr' => Actor::class],
); );
};
}; };
// -------- *** --------
$person_activity_query = $query_fn(actor_type: Actor::PERSON, table: 'activity', join_field: 'actor_id'); // -------- Start setting up the queries --------
$group_activity_query = $query_fn(actor_type: Actor::GROUP, table: 'group_inbox', join_field: 'group_id'); $actor_query_fn = fn (int $actor_type) => DB::findBy(Actor::class, ['type' => $actor_type], order_by: $order_by, limit: $limit, offset: $offset);
$modified_query_fn = $general_query_fn_fn(func: $order_by_op === 'ASC' ? 'MAX' : 'MIN', order: "created {$order_by_op}");
$activity_query_fn = $general_query_fn_fn(func: 'COUNT', order: "created {$order_by_op}");
// -------- *** --------
switch ($order_by_field) { // -------- Figure out the final query --------
case 'nickname': $query_fn = match ($order_by_field) {
case 'created': 'nickname', 'created' => $actor_query_fn, // select only from actors
$actors = DB::findBy(Actor::class, ['type' => $actor_type], order_by: $order_by, limit: $limit, offset: $offset);
break;
case 'modified': 'modified' => match ($actor_type) { // select by most/least recent activity
$query = match ($actor_type) { Actor::PERSON => $modified_query_fn(table: 'activity', join_field: 'actor_id'),
Actor::PERSON => $person_activity_query, Actor::GROUP => $modified_query_fn(table: 'group_inbox', join_field: 'group_id'),
Actor::GROUP => $group_activity_query, },
default => throw new BugFoundException("Unimplemented for actor type: {$actor_type}"),
};
$actors = $query(func: $order_by_op === 'ASC' ? 'MAX' : 'MIN', order: "created {$order_by_op}");
break;
case 'activity': 'activity' => match ($actor_type) { // select by most/least activity amount
$query = match ($actor_type) { Actor::PERSON => $activity_query_fn(table: 'activity', join_field: 'actor_id'),
Actor::PERSON => $person_activity_query, Actor::GROUP => $activity_query_fn(table: 'group_inbox', join_field: 'group_id'),
Actor::GROUP => $group_activity_query, },
default => throw new BugFoundException("Unimplemented for actor type: {$actor_type}"),
};
$actors = $query(func: 'COUNT', order: "created {$order_by_op}");
break;
default: default => throw new BugFoundException("Unkown order by found, but should have been validated: {$order_by_field}"),
throw new BugFoundException("Unkown order by found, but should have been validated: {$order_by_field}"); };
} // -------- *** --------
return [ return [
'_template' => $template, '_template' => $template,
'actors' => $actors, 'actors' => $query_fn($actor_type),
'page' => $page, 'page' => $page,
]; ];
} }
/**
* people stream
*
* @return array template
*/
public function people(Request $request): array public function people(Request $request): array
{ {
return $this->impl($request, 'directory/people.html.twig', Actor::PERSON); return $this->impl($request, 'directory/people.html.twig', Actor::PERSON);
} }
/**
* groups stream
*
* @return array template
*/
public function groups(Request $request): array public function groups(Request $request): array
{ {
return $this->impl($request, 'directory/groups.html.twig', Actor::GROUP); return $this->impl($request, 'directory/groups.html.twig', Actor::GROUP);