Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Web/Models/Entities/Club.php
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,11 @@ public function getFollowersQuery(string $sort = "follower ASC"): GroupedSelecti

public function getFollowersCount(): int
{
return sizeof($this->getFollowersQuery()->where('flags', 0));
return $this->getRecord()->related('subscriptions.target')
->select('target, COUNT(DISTINCT follower) AS unique_followers')
->where('flags', 0)
->where('model', 'openvk\\Web\\Models\\Entities\\Club')
->group('target')->fetch()->unique_followers;
}

public function getFollowers(int $page = 1, int $perPage = 6, string $sort = "target DESC"): \Traversable
Expand Down
53 changes: 49 additions & 4 deletions Web/Models/Repositories/Clubs.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace openvk\Web\Models\Repositories;

use openvk\Web\Models\Entities\{Club, Manager};
use openvk\Web\Models\Entities\{Club, Manager, User};
use openvk\Web\Models\Repositories\{Aliases, Users};
use Nette\Database\Table\ActiveRow;
use Chandler\Database\DatabaseConnection;
Expand Down Expand Up @@ -71,12 +71,57 @@ public function getByIds(array $ids = []): array
public function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false], int $page = 1, ?int $perPage = null, bool $andEvents = false): \Traversable
{
$query = "%$query%";
$result = $this->clubs;
$order_str = 'id';
$result = $this->clubs->where("name LIKE ? OR about LIKE ?", $query, $query);

switch ($order['type']) {
case 'id':
$order_str = 'id ' . ($order['invert'] ? 'ASC' : 'DESC');
$result->order('id ' . ($order['invert'] ? 'DESC' : 'ASC'));
break;
case 'members':
return new Util\RawEntityStream(
"Club",
"SELECT `groups`.id, COUNT(subscriptions.follower) AS subscribers
FROM `groups`
LEFT JOIN subscriptions
ON subscriptions.target = groups.id
AND subscriptions.model = ?
GROUP BY groups.id
ORDER BY subscribers " . ($order['invert'] ? 'ASC' : 'DESC'),
[Club::class],
$this
);
case 'popular':
$days = isset($params['days']) && $params['days'] > 0 ? $params['days'] : 7;
return new Util\RawEntityStream(
"Club",
"SELECT `groups`.id, COUNT(subscriptions.follower) AS recent_subscribers
FROM `groups`
LEFT JOIN subscriptions
ON subscriptions.target = groups.id
AND subscriptions.model = ?
AND subscriptions.created_at > DATE_SUB(NOW(), INTERVAL ? DAY)
GROUP BY groups.id
ORDER BY recent_subscribers " . ($order['invert'] ? 'ASC' : 'DESC'),
[Club::class, $days],
$this
);
case 'recommended':
return new Util\RawEntityStream(
"Club",
"SELECT `groups`.id, COUNT(subscriptions.follower) AS friends_subscribers
FROM `groups`
LEFT JOIN subscriptions
ON subscriptions.target = `groups`.id
AND subscriptions.model = ?
WHERE subscriptions.follower in (
SELECT target FROM subscriptions s where s.model=? and s.follower=?
)
GROUP BY groups.id
ORDER BY friends_subscribers " . ($order['invert'] ? 'ASC' : 'DESC'),
[Club::class, User::class, $params['auth_user_id']],
$this
);
default:
break;
}

Expand Down
66 changes: 66 additions & 0 deletions Web/Models/Repositories/Util/RawEntityStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

namespace openvk\Web\Models\Repositories\Util;

use Nette\Database\Row;
use Chandler\Database\DatabaseConnection;

class RawEntityStream implements \IteratorAggregate
{
private string $query;
private array $bindings;
private string $entityClass;

private $repository;

public function __construct(string $class, string $query, array $bindings = [], $repository = null)
{
$this->entityClass = $class[0] === "\\" ? $class : "openvk\\Web\\Models\\Entities\\$class";

$this->query = $query;
$this->bindings = $bindings;

$this->repository = $repository;
}

private function dbs($query = null, $bindings = []): \Traversable
{
$query ??= $this->query;
$bindings = array_merge($this->bindings, $bindings);

return DatabaseConnection::i()->getConnection()->query($query, ...$bindings);
}

private function getEntity(Row $result)
{
return $this->repository->get($result->id);
}

private function stream(\Traversable $iterator): \Traversable
{
foreach ($iterator as $result) {
yield $this->getEntity($result);
}
}

public function getIterator(): \Traversable
{
trigger_error("Trying to use EntityStream as iterator directly. Are you sure this is what you want?", E_USER_WARNING);

return $this->stream($this->dbs());
}

public function page(int $page, ?int $perPage = null): \Traversable
{
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
return $this->stream($this->dbs($this->query . " limit ? offset ?", [$perPage, ($page - 1) * $perPage]));
}

public function size(): int
{
$result = $this->dbs("select count(*) as cnt from ({$this->query}) as q")->fetch();
return $result->cnt;
}
}
4 changes: 4 additions & 0 deletions Web/Presenters/SearchPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,13 @@ public function renderIndex(): void
$parameters['from_me'] = $this->user->id;

break;
case "period":
$parameters['days'] = (int) $param_value;
}
}

$parameters['auth_user_id'] = $this->user->id;

$repo = $repos[$section] or $this->throwError(400, "Bad Request", "Invalid search entity $section.");

$results = null;
Expand Down
19 changes: 19 additions & 0 deletions Web/Presenters/templates/Search/Index.latte
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,11 @@
<option value="length" n:attr="selected => $order == 'length'">{_s_order_by_length}</option>
<option value="listens" n:attr="selected => $order == 'listens'">{_s_order_by_listens}</option>
{/if}
{if $section == "groups"}
<option value="members" n:attr="selected => $order == 'members'">{_s_order_by_members}</option>
<option value="popular" n:attr="selected => $order == 'popular'">{_s_order_by_popular}</option>
<option value="recommended" n:attr="selected => $order == 'recommended'">{_s_order_by_recommended}</option>
{/if}
</select>

<label n:if="$order != 'rating'">
Expand All @@ -442,6 +447,20 @@
</div>
</div>

<div n:if="$section == 'groups' && $order == 'popular'" class="search_option">
<div class="search_option_name">
<div class='search_option_name_ico'></div>
{_s_popular_for}
</div>
<label>
<select name="days" form="search_form" data-default='7'>
<option value="1" n:attr="selected => $_REQUEST['days'] == '1'">{_s_for_day}</option>
<option value="7" n:attr="selected => $_REQUEST['days'] == '7'">{_s_for_week}</option>
<option value="31" n:attr="selected => $_REQUEST['days'] == '31'">{_s_for_month}</option>
</select>
</label>
</div>

<div n:if="$section == 'users'" class="search_option">
<div class="search_option_name">
<div class='search_option_name_ico'></div>
Expand Down
8 changes: 4 additions & 4 deletions Web/Presenters/templates/components/globalalert.latte
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
let alertDismissed = false;
for (let i = 0; i < cookie.length; i++) {
let c = cookie[i].trim();

if (c == "alertDismissed=" + alertContent) {
alertDismissed = true;
break;
Expand All @@ -25,9 +25,9 @@

function dismissGlobalAlert() {
let expires = (new Date(Date.now() + 40000000000)).toUTCString();

document.cookie = "alertDismissed=" + alertContent + ";expires=" + expires + ";SameSite=Strict";
u(".global-alert").nodes[0].style.display = "none";
}
</script>

</script>
1 change: 1 addition & 0 deletions install/sqls/00061-subscriptions-time.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE `subscriptions` ADD `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP AFTER `flags`;
8 changes: 8 additions & 0 deletions locales/ru.strings
Original file line number Diff line number Diff line change
Expand Up @@ -2207,6 +2207,14 @@
"s_order_by_creation_date" = "По дате создания";
"s_order_by_publishing_date" = "По дате публикации";
"s_order_by_upload_date" = "По дате загрузки";
"s_order_by_members" = "По количеству подписчиков";
"s_order_by_popular" = "По росту числа подписчиков";
"s_order_by_recommended" = "Рекомендуемые вам";

"s_popular_for" = "Считать прирост";
"s_for_day" = "За прошедший день";
"s_for_week" = "За неделю";
"s_for_month" = "За месяц";

"s_by_date" = "По дате";
"s_registered_before" = "Зарегистрирован до";
Expand Down
Loading