Add PM\Model model

This commit is contained in:
Visman 2021-03-20 22:35:15 +07:00
parent 11b91b1873
commit 45d5498a86

403
app/Models/PM/Model.php Normal file
View file

@ -0,0 +1,403 @@
<?php
/**
* This file is part of the ForkBB <https://github.com/forkbb>.
*
* @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
* @license The MIT License (MIT)
*/
declare(strict_types=1);
namespace ForkBB\Models\PM;
use ForkBB\Core\Container;
use ForkBB\Models\Model as ParentModel;
use ForkBB\Models\PM\Cnst;
use ForkBB\Models\PM\PPost;
use ForkBB\Models\PM\PTopic;
use ForkBB\Models\User\Model as User;
use InvalidArgumentException;
use RuntimeException;
class Model extends ParentModel
{
/**
* @var array
*/
protected $repository;
public function __construct(Container $container)
{
parent::__construct($container);
$this->zDepend = [
'area' => ['numPages', 'pagination'],
];
$this->repository = [
Cnst::PTOPIC => [],
Cnst::PPOST => [],
];
}
protected function checkType(int $type): void
{
switch ($type) {
case Cnst::PTOPIC:
case Cnst::PPOST:
break;
default:
throw new InvalidArgumentException("Wrong type: {$type}");
}
}
public function get(int $type, int $key): ?ParentModel
{
$this->checkType($type);
return $this->repository[$type][$key] ?? null;
}
public function set(int $type, int $key, /* mixed */ $value): self
{
$this->checkType($type);
$this->repository[$type][$key] = $value;
return $this;
}
public function isset(int $type, int $key): bool
{
$this->checkType($type);
return \array_key_exists($key, $this->repository[$type]);
}
public function create(int $type, array $attrs = []): ParentModel
{
switch ($type) {
case Cnst::PTOPIC:
return $this->c->PTopicModel->setAttrs($attrs);
case Cnst::PPOST:
return $this->c->PPostModel->setAttrs($attrs);
case Cnst::PRND:
return $this->c->PRndModel->setAttrs($attrs);
default:
throw new InvalidArgumentException("Wrong type: {$type}");
}
}
public function accessTopic(int $id): bool
{
return isset($this->idsCurrent[$id]) || isset($this->numArchive[$id]);
}
public function load(int $type, int $id): ?ParentModel
{
$this->checkType($type);
if ($this->isset($type, $id)) {
return $this->get($type, $id);
}
switch ($type) {
case Cnst::PTOPIC:
if ($this->accessTopic($id)) {
$model = $this->Load->load($type, $id);
} else {
$model = null;
}
break;
case Cnst::PPOST:
$model = $this->Load->load($type, $id);
if (
$model instanceof PPost
&& ! $this->accessTopic($model->topic_id)
) {
$model = null;
}
break;
}
$this->set($type, $id, $model);
return $model;
}
public function loadByIds(int $type, array $ids): array
{
$this->checkType($type);
$result = [];
$data = [];
foreach ($ids as $id) {
if ($this->isset($type, $id)) {
$result[$id] = $this->get($type, $id);
} else {
switch ($type) {
case Cnst::PTOPIC:
if (! $this->accessTopic($id)) {
break;
}
default:
$data[] = $id;
}
$result[$id] = null;
$this->set($type, $id, null);
}
}
if (empty($data)) {
return $result;
}
foreach ($this->Load->loadByIds($type, $data) as $model) {
if ($model instanceof PPost) {
if (! $this->accessTopic($model->topic_id)) {
continue;
}
} elseif (! $model instanceof PTopic) {
continue;
}
$result[$model->id] = $model;
$this->set($type, $model->id, $model);
}
return $result;
}
public function update(int $type, ParentModel $model): ParentModel
{
$this->checkType($type);
return $this->Save->update($type, $model);
}
public function insert(int $type, ParentModel $model): int
{
$this->checkType($type);
$id = $this->Save->insert($type, $model);
$this->set($type, $id, $model);
return $id;
}
/**
* Инициализирует массивы индексов приватных тем текущего пользователя
* Инициализирует число приватных тем без фильтра
* Может использовать фильтр по второму пользователю: id или username
*/
public function init(/* null|int|string */ $second = null): self
{
list(
$this->idsNew,
$this->idsCurrent,
$this->idsArchive,
$this->totalCurrent,
$this->totalArchive
) = $this->infoForUser($this->c->user, $second);
$this->second = $second;
$this->numNew = \count($this->idsNew);
$this->numCurrent = \count($this->idsCurrent);
$this->numArchive = \count($this->idsArchive);
return $this;
}
/**
* Возвращает данные по приватным темам (индексы) любого пользователя
*/
public function infoForUser(User $user, /* null|int|string */ $second = null): array
{
if (
$user->isGuest
|| $user->isUnverified
) {
throw new RuntimeException('Unexpected Guest or Unverified');
}
$vars = [
':id' => $user->id,
':var' => $second,
];
$query = 'SELECT pr.topic_id, pt.last_post, pr.pt_visit, pr.pt_status %repl1%
FROM ::pm_rnd AS pr
INNER JOIN ::pm_topics AS pt ON pt.id=pr.topic_id %repl2%
WHERE pr.user_id=?i:id AND pr.pt_status>1
ORDER BY pt.last_post DESC';
if (null === $second) {
$repl1 = '';
$repl2 = '';
} elseif (
\is_int($second)
&& $second > 0
) {
$repl1 = ', prs.user_id AS pmsecond';
$repl2 = 'LEFT JOIN ::pm_rnd AS prs ON prs.user_id=?i:var AND prs.topic_id=pr.topic_id';
} elseif (
\is_string($second)
&& '' !== $second
) {
$repl1 = ', prs.username AS pmsecond';
$repl2 = 'LEFT JOIN ::pm_rnd AS prs ON prs.topic_id=pr.topic_id AND prs.username=?s:var'; // на имени нет индекса
} else {
throw new InvalidArgumentException('Wrong second user');
}
$query = \str_replace(['%repl1%', '%repl2%'], [$repl1, $repl2], $query);
// deleted // pt_status = 0
// unsent // pt_status = 1
$idsNew = []; // pt_status = 2 and last_post > last_visit
$idsCur = []; // pt_status = 2 or last_post > last_visit
$idsArc = []; // pt_status = 3
$totalCur = 0;
$totalArc = 0;
$stmt = $this->c->DB->query($query, $vars);
while ($row = $stmt->fetch()) {
switch ($row['pt_status']) {
case Cnst::PT_ARCHIVE:
++$totalArc;
if (
null === $second
|| $row['pmsecond'] == $second
) {
$idsArc[$row['topic_id']] = $row['last_post'];
}
break;
case Cnst::PT_NORMAL:
++$totalCur;
if (
null === $second
|| $row['pmsecond'] == $second
) {
if ($row['last_post'] > $row['pt_visit']) {
$idsNew[$row['topic_id']] = $row['last_post'];
}
$idsCur[$row['topic_id']] = $row['last_post'];
}
break;
}
}
return [$idsNew, $idsCur, $idsArc, $totalCur, $totalArc];
}
/**
* Возвращает список приватных тем в зависимости от активной папки
* Номер темы в индексе, а не в значении
*/
protected function idsList(): array
{
switch ($this->area) {
case Cnst::ACTION_NEW:
$list = $this->idsNew;
break;
case Cnst::ACTION_ARCHIVE:
$list = $this->idsArchive;
break;
default:
$list = $this->idsCurrent;
}
if (\is_array($list)) {
return $list;
}
throw new RuntimeException('Init() method was not executed');
}
/**
* $this->area = ...
*/
protected function setarea(string $area): self
{
switch ($area) {
case Cnst::ACTION_NEW:
case Cnst::ACTION_CURRENT:
case Cnst::ACTION_ARCHIVE:
break;
default:
$area = Cnst::ACTION_CURRENT;
}
$this->setAttr('area', $area);
return $this;
}
/**
* ... = $this->numPages;
*/
protected function getnumPages(): int
{
return (int) \ceil((\count($this->idsList()) ?: 1) / $this->c->user->disp_topics);
}
/**
* Статус наличия установленной страницы
*/
public function hasPage(): bool
{
return \is_int($this->page) && $this->page > 0 && $this->page <= $this->numPages;
}
/**
* ... = $this->pagination;
*/
protected function getpagination(): array
{
if (
$this->page < 1
&& 1 === $this->numPages
) {
return [];
} else {
return $this->c->Func->paginate(
$this->numPages,
$this->page,
'PMAction',
[
'second' => $this->second,
'action' => $this->area,
]
);
}
}
/**
* Возвращает массив приватных тем с установленной страницы
*/
public function pmListCurPage(): array
{
if (! $this->hasPage()) {
throw new InvalidArgumentException('Bad number of displayed page');
}
$ids = \array_slice(
$this->idsList(),
($this->page - 1) * $this->c->user->disp_topics,
$this->c->user->disp_topics
);
return $this->loadByIds(Cnst::PTOPIC, \array_keys($ids));
}
}