Completed
Push — master ( fd5325...d7e193 )
by Schlaefer
05:54 queued 03:00
created

DraftsTable   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 108
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
dl 0
loc 108
rs 10
c 0
b 0
f 0
wmc 11
lcom 1
cbo 6

6 Methods

Rating   Name   Duplication   Size   Complexity  
A initialize() 0 10 1
A validationDefault() 0 19 1
A beforeMarshal() 0 10 3
A buildRules() 0 25 3
A deleteDraftForPosting() 0 6 2
A outdatedGc() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Saito - The Threaded Web Forum
7
 *
8
 * @copyright Copyright (c) the Saito Project Developers
9
 * @link https://github.com/Schlaefer/Saito
10
 * @license http://opensource.org/licenses/MIT
11
 */
12
13
namespace App\Model\Table;
14
15
use App\Lib\Model\Table\AppTable;
16
use App\Model\Entity\Entry;
17
use Cake\Chronos\Chronos;
18
use Cake\Event\Event;
19
use Cake\ORM\RulesChecker;
20
use Cake\ORM\Rule\IsUnique;
21
use Cake\Validation\Validator;
22
23
/**
24
 * Table storing the drafts for unfinished posting submissions.
25
 *
26
 * Indices:
27
 * - user_id, pid - Main lookup index for retrieving and checking for uniqness.
28
 * Have user_id first to use it for other purposes (find drafts for user).
29
 * - modified - Used for garbage collection deleting outdated drafts.
30
 */
31
class DraftsTable extends AppTable
32
{
33
    /** @var string Creation time after a draft is considered outdated. */
34
    public const OUTDATED = '-30 days';
35
36
    /**
37
     * {@inheritDoc}
38
     */
39
    public function initialize(array $config)
40
    {
41
        parent::initialize($config);
42
43
        $this->addBehavior(
44
            'Cron.Cron',
45
            ['outdatedGc' => ['id' => 'Drafts.outdatedGc', 'due' => 'daily']]
46
        );
47
        $this->addBehavior('Timestamp');
48
    }
49
50
    /**
51
     * {@inheritDoc}
52
     */
53
    public function validationDefault(Validator $validator)
54
    {
55
        /// subject
56
        $validator
57
            ->allowEmptyString('subject')
58
            ->add(
59
                'subject',
60
                [
61
                    'maxLength' => [
62
                        'rule' => ['maxLength', $this->getConfig('subject_maxlength')],
63
                    ]
64
                ]
65
            );
66
67
        /// text
68
        $validator->allowEmptyString('text');
69
70
        return $validator;
71
    }
72
73
    /**
74
     * {@inheritDoc}
75
     */
76
    public function beforeMarshal(Event $event, \ArrayObject $data, \ArrayObject $options)
77
    {
78
        /// Trim whitespace on subject and text
79
        $toTrim = ['subject', 'text'];
80
        foreach ($toTrim as $field) {
81
            if (!empty($data[$field])) {
82
                $data[$field] = trim($data[$field]);
83
            }
84
        }
85
    }
86
87
    /**
88
     * {@inheritDoc}
89
     */
90
    public function buildRules(RulesChecker $rules)
91
    {
92
        $rules = parent::buildRules($rules);
93
94
        $rules->addCreate(new IsUnique(['pid', 'user_id'], ['allowMultipleNulls' => false]));
95
96
        $rules->add(
97
            function ($entity) {
98
                $validated = false;
99
                $fields = ['subject', 'text'];
100
                foreach ($fields as $field) {
101
                    if (!empty($entity->get($field))) {
102
                        $validated = true;
103
                        break;
104
                    }
105
                }
106
107
                return $validated;
108
            },
109
            'checkThatAtLeastOnFieldIsPopulated',
110
            ['errorField' => 'oneNotEmpty', 'message' => 'dummy']
111
        );
112
113
        return $rules;
114
    }
115
116
    /**
117
     * Deletes a draft which might have been the source for a posting.
118
     *
119
     * @param Entry $entry The posting which might have been created by a draft.
120
     * @return void
121
     */
122
    public function deleteDraftForPosting(Entry $entry): void
123
    {
124
        $where = ['user_id' => $entry->get('user_id')];
125
        $entry->isRoot() ? $where[] = 'pid IS NULL' : $where['pid'] = $entry->get('pid');
126
        $this->deleteAll($where);
127
    }
128
129
    /**
130
     * Garbage collect outdated drafts.
131
     *
132
     * @return void
133
     */
134
    public function outdatedGc(): void
135
    {
136
        $this->deleteAll(['modified <' => new Chronos(self::OUTDATED)]);
137
    }
138
}
139