Explorar el Código

Add PM\Model model

Visman hace 4 años
padre
commit
45d5498a86
Se han modificado 1 ficheros con 403 adiciones y 0 borrados
  1. 403 0
      app/Models/PM/Model.php

+ 403 - 0
app/Models/PM/Model.php

@@ -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));
+    }
+}