浏览代码

Add Models\Poll 1

Visman 4 年之前
父节点
当前提交
69538737e3
共有 5 个文件被更改,包括 474 次插入0 次删除
  1. 62 0
      app/Models/Poll/Load.php
  2. 84 0
      app/Models/Poll/Manager.php
  3. 32 0
      app/Models/Poll/Model.php
  4. 142 0
      app/Models/Poll/Revision.php
  5. 154 0
      app/Models/Poll/Save.php

+ 62 - 0
app/Models/Poll/Load.php

@@ -0,0 +1,62 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ForkBB\Models\Poll;
+
+use ForkBB\Models\Action;
+use ForkBB\Models\Poll\Model as Poll;
+use InvalidArgumentException;
+
+class Load extends Action
+{
+    /**
+     * Загружает опрос из БД
+     */
+    public function load(int $id): ?Poll
+    {
+        if ($id < 1) {
+            throw new InvalidArgumentException('Expected a positive poll id');
+        }
+
+        $vars  = [
+            ':tid' => $id,
+        ];
+        $query = 'SELECT question_id, field_id, qna_text, votes
+            FROM ::poll
+            WHERE tid=?i:tid
+            ORDER BY question_id, field_id';
+
+        $stmt = $this->c->DB->query($query, $vars);
+
+        $i    = 0;
+        $data = [
+            'id'       => $id,
+            'question' => [],
+            'answer'   => [],
+            'vote'     => [],
+            'type'     => [],
+        ];
+
+        while ($row = $stmt->fetch()) {
+            $qid = $row['question_id'];
+            $fid = $row['field_id'];
+
+            if (0 === $fid) {
+                $data['question'][$qid]     = $row['qna_text'];
+                $data['type'][$qid]         = $row['votes'];
+            } else {
+                $data['answer'][$qid][$fid] = $row['qna_text'];
+                $data['vote'][$qid][$fid]   = $row['votes'];
+            }
+
+            ++$i;
+        }
+
+        if (0 === $i) {
+            return null;
+        } else {
+            return $this->manager->create($data);
+        }
+    }
+}

+ 84 - 0
app/Models/Poll/Manager.php

@@ -0,0 +1,84 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ForkBB\Models\Poll;
+
+use ForkBB\Models\ManagerModel;
+use ForkBB\Models\Poll\Model as Poll;
+use RuntimeException;
+
+class Manager extends ManagerModel
+{
+    /**
+     * Создает новый опрос
+     */
+    public function create(array $attrs = []): Poll
+    {
+        return $this->c->PollModel->setAttrs($attrs);
+    }
+
+    /**
+     * Получает опрос по id
+     */
+    public function load(int $id): ?Poll
+    {
+        if ($this->isset($id)) {
+            return $this->get($id);
+        } else {
+            $data = $this->c->Cache->get("poll{$id}", false);
+
+            if (null === $data) {
+                $poll = null;
+            } elseif (\is_array($data)) {
+                $poll = $this->create($data);
+            } else {
+                $poll = $this->Load->load($id);
+                $data = $poll instanceof Poll ? $poll->getAttrs() : null; // ????
+
+                $this->c->Cache->set("poll{$id}", $data);
+            }
+
+            $this->set($id, $poll);
+
+            return $poll;
+        }
+    }
+
+    /**
+     * Обновляет опрос в БД
+     */
+    public function update(Poll $poll): Poll
+    {
+        $poll = $this->Save->update($poll);
+
+        if (true === $poll->itWasModified) {
+            $this->reset($poll->id);
+        }
+
+        return $poll;
+    }
+
+    /**
+     * Добавляет новый опрос в БД
+     */
+    public function insert(Poll $poll): int
+    {
+        $id = $this->Save->insert($poll);
+        $this->set($id, $poll);
+
+        return $id;
+    }
+
+    /**
+     * Сбрасывает кеш указанного голосования
+     */
+    public function reset(int $id): Manager
+    {
+        if (true !== $this->c->Cache->delete("poll{$id}")) {
+            throw new RuntimeException("Unable to remove key from cache - poll{$id}");
+        }
+
+        return $this;
+    }
+}

+ 32 - 0
app/Models/Poll/Model.php

@@ -0,0 +1,32 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ForkBB\Models\Poll;
+
+use ForkBB\Core\Container;
+use ForkBB\Models\DataModel;
+use ForkBB\Models\Topic\Model as Topic;
+use PDO;
+use RuntimeException;
+
+class Model extends DataModel
+{
+    /**
+     * Получение родительской темы
+     */
+    protected function getparent(): Topic
+    {
+        if ($this->tid < 1) {
+            throw new RuntimeException('Parent is not defined');
+        }
+
+        $topic = $this->c->topics->get($this->tid);
+
+        if (! $topic instanceof Topic) {
+            throw new RuntimeException('Parent not found');
+        }
+
+        return $topic;
+    }
+}

+ 142 - 0
app/Models/Poll/Revision.php

@@ -0,0 +1,142 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ForkBB\Models\Poll;
+
+use ForkBB\Models\Action;
+use ForkBB\Models\Poll\Model as Poll;
+use InvalidArgumentException;
+use function \ForkBB\__;
+
+class Revision extends Action
+{
+    protected $error;
+    protected $question;
+    protected $answer;
+    protected $type;
+
+    /**
+     * Проверяет/нормализует опрос
+     */
+    public function revision(Poll $poll, bool $normalize = false) /* : true|string */
+    {
+        $this->error    = null;
+        $this->question = [];
+        $this->answer   = [];
+        $this->type     = [];
+
+
+        if (
+            empty($poll->question)
+            || ! \is_array($poll->question)
+        ) {
+            $this->error = __('The poll structure is broken');
+        } else {
+            $this->test($poll);
+        }
+
+        if (
+            $normalize
+            && null === $this->error
+        ) {
+            $poll->__question = $this->question;
+            $poll->__answer   = $this->answer;
+            $poll->__type     = $this->type;
+        }
+
+        return $this->error ?? true;
+    }
+
+    protected function test(Poll $poll): void
+    {
+        $questions = $poll->question;
+        $answers   = $poll->answer;
+        $types     = $poll->type;
+
+        $countQ = 0;
+        $emptyQ = false;
+
+        for ($qid = 1; $qid <= $this->c->config->i_poll_max_questions; $qid++) {
+            if (
+                ! isset($questions[$qid])
+                || '' == $questions[$qid]
+            ) {
+                $emptyQ = true;
+
+                unset($questions[$qid], $answers[$qid]);
+
+                continue;
+            } elseif ($emptyQ) {
+                $this->error = __('Question number %s is preceded by an empty question', $qid);
+
+                return;
+            }
+
+            if (
+                empty($answers[$qid])
+                || ! \is_array($answers[$qid])
+            ) {
+                $this->error = __('For question number %s, the structure of answers is broken', $qid);
+
+                return;
+            }
+
+            $countA = 0;
+            $emptyA = false;
+
+            for ($fid = 1; $fid <= $this->c->config->i_poll_max_fields; $fid++) {
+                if (
+                    ! isset($answers[$qid][$fid])
+                    || '' == $answers[$qid][$fid]
+                ) {
+                    $emptyA = true;
+
+                    unset($answers[$qid][$fid]);
+
+                    continue;
+                } elseif ($emptyA) {
+                    $this->error = __('Answer number %1$s is preceded by an empty answer (question number %2$s)', $fid, $qid);
+
+                    return;
+                }
+
+                ++$countA;
+                $this->answer[$qid][$fid] = $answers[$qid][$fid];
+
+                unset($answers[$qid][$fid]);
+            }
+
+            if (! empty($answers[$qid])) {
+                $this->error = __('For question number %s, the structure of answers is broken', $qid);
+
+                return;
+            } elseif ($countA < 2) {
+                $this->error = __('Requires at least two answers per question (%s)', $qid);
+
+                return;
+            } elseif (! isset($types[$qid])) {
+                $this->error = __('For question number %s, there is no value for the maximum number of answers for voting', $qid);
+
+                return;
+            } elseif ($types[$qid] > $countA) {
+                $this->error = __('For question number %s, the maximum number of answers for voting more answers', $qid);
+
+                return;
+            }
+
+            ++$countQ;
+            $this->question[$qid] = $questions[$qid];
+            $this->type[$qid]     = $types[$qid];
+
+            unset($questions[$qid], $answers[$qid]);
+        }
+
+        if (
+            0 === $countQ
+            || ! empty($questions)
+        ) {
+            $this->error = __('The poll structure is broken');
+        }
+    }
+}

+ 154 - 0
app/Models/Poll/Save.php

@@ -0,0 +1,154 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ForkBB\Models\Poll;
+
+use ForkBB\Models\Action;
+use ForkBB\Models\Poll\Model as Poll;
+use ForkBB\Models\Topic\Model as Topic;
+use RuntimeException;
+
+class Save extends Action
+{
+    /**
+     * Обновляет опрос в БД
+     */
+    public function update(Poll $poll): Poll
+    {
+        $poll->itWasModified = false;
+
+        if (true !== $this->manager->revision($poll, true)) {
+            throw new RuntimeException('The poll model has errors');
+        }
+
+        $old = $this->manager->Load->load($poll->id);
+
+        if (! $old instanceof Poll) {
+            throw new RuntimeException('No such poll found');
+        }
+
+        $vars = [
+            ':tid' => $poll->id,
+        ];
+        $queryIn = 'INSERT INTO ::poll (tid, question_id, field_id, qna_text, votes)
+            VALUES (?i:tid, ?i:qid, ?i:fid, ?s:qna, ?i:votes)';
+        $queryU1 = 'UPDATE :poll
+            SET qna_text=?s:qna, votes=?i:votes
+            WHERE tid=?i:tid AND question_id=?i:qid AND field_id=?i:fid';
+        $queryU1 = 'UPDATE :poll
+            SET qna_text=?s:qna
+            WHERE tid=?i:tid AND question_id=?i:qid AND field_id=?i:fid';
+        $queryD1 = 'DELETE FROM :poll
+            WHERE tid=?i:tid AND question_id IN(?ai:qids)';
+        $queryD2 = 'DELETE FROM :poll
+            WHERE tid=?i:tid AND question_id=?i:qid AND field_id IN(?ai:fid)';
+
+        $modified    = false;
+        $oldQuestion = $old->question;
+        $oldType     = $old->type;
+
+        foreach ($poll->question as $qid => $qna) {
+            $vars[':qid']   = $qid;
+            $vars[':fid']   = 0;
+            $vars[':qna']   = $qna;
+            $vars[':votes'] = $poll->type[$qid];
+
+            if (! isset($oldQuestion[$qid])) {
+                $modified = true;
+
+                $this->c->DB->exec($queryIn, $vars);
+            } elseif (
+                $qna !== $oldQuestion[$qid]
+                || $poll->type[$qid] !== $oldType[$qid]
+            ) {
+                $modified = true;
+
+                $this->c->DB->exec($queryU1, $vars);
+            }
+
+            $vars[':votes'] = 0;
+            $oldAnswer      = $old->answer[$qid] ?? [];
+
+            foreach ($poll->answer[$qid] as $fid => $qna) {
+                $vars[':fid']   = $fid;
+                $vars[':qna']   = $qna;
+
+                if (! isset($oldAnswer[$fid])) {
+                    $modified = true;
+
+                    $this->c->DB->exec($queryIn, $vars);
+                } elseif ($qna !== $oldAnswer[$fid]) {
+                    $modified = true;
+
+                    $this->c->DB->exec($queryU2, $vars);
+                }
+
+                unset($oldAnswer[$fid]);
+            }
+
+            if (! empty($oldAnswer)) {
+                $modified     = true;
+                $vars[':fid'] = \array_keys($oldAnswer);
+
+                $this->c->DB->exec($queryD2, $vars);
+            }
+
+            unset($oldQuestion[$qid]);
+        }
+
+        if (! empty($oldQuestion)) {
+            $modified     = true;
+            $vars[':qid'] = \array_keys($oldQuestion);
+
+            $this->c->DB->exec($queryD1, $vars);
+        }
+
+        $poll->itWasModified = $modified;
+        $poll->resModified();
+
+        return $poll;
+    }
+
+    /**
+     * Добавляет новый опрос в БД
+     */
+    public function insert(Poll $poll): int
+    {
+        if (true !== $this->manager->revision($poll, true)) {
+            throw new RuntimeException('The poll model has errors');
+        }
+
+        if (null !== $this->manager->Load->load($poll->id)) {
+            throw new RuntimeException('Such the poll already exists');
+        }
+
+        $vars = [
+            ':tid' => $poll->id,
+        ];
+        $query = 'INSERT INTO ::poll (tid, question_id, field_id, qna_text, votes)
+            VALUES (?i:tid, ?i:qid, ?i:fid, ?s:qna, ?i:votes)';
+
+        foreach ($poll->question as $qid => $qna) {
+            $vars[':qid']   = $qid;
+            $vars[':fid']   = 0;
+            $vars[':qna']   = $qna;
+            $vars[':votes'] = $poll->type[$qid];
+
+            $this->c->DB->exec($query, $vars);
+
+            $vars[':votes'] = 0;
+
+            foreach ($poll->answer[$qid] as $fid => $qna) {
+                $vars[':fid']   = $fid;
+                $vars[':qna']   = $qna;
+
+                $this->c->DB->exec($query, $vars);
+            }
+        }
+
+        $poll->resModified();
+
+        return $poll->id;
+    }
+}