Passed
Push — master ( f510e9...9822a2 )
by Paul
10:29
created

Database   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 375
Duplicated Lines 0 %

Test Coverage

Coverage 66.67%

Importance

Changes 3
Bugs 0 Features 2
Metric Value
wmc 38
eloc 119
dl 0
loc 375
ccs 94
cts 141
cp 0.6667
rs 9.36
c 3
b 0
f 2

28 Methods

Rating   Name   Duplication   Size   Complexity  
A searchAssignedPosts() 0 3 1
A delete() 0 5 1
A dbGetRow() 0 4 1
A users() 0 9 1
A logErrors() 0 7 2
A deleteMeta() 0 8 1
A finishTransaction() 0 6 2
A update() 0 5 1
A meta() 0 5 1
A dbQuery() 0 3 1
A deleteInvalidPostAssignments() 0 12 1
A deleteInvalidReviews() 0 11 1
A deleteInvalidTermAssignments() 0 13 1
A dbGetResults() 0 4 1
A beginTransaction() 0 6 2
A isMigrationNeeded() 0 9 2
A searchUsers() 0 3 1
A insertBulk() 0 16 3
A dbGetVar() 0 3 1
A version() 0 10 3
A __construct() 0 8 1
A terms() 0 15 2
A searchPosts() 0 3 1
A deleteInvalidUserAssignments() 0 12 1
A insert() 0 9 2
A searchAssignedUsers() 0 3 1
A dbGetCol() 0 3 1
A metaSet() 0 5 1
1
<?php
2
3
namespace GeminiLabs\SiteReviews;
4
5
use GeminiLabs\SiteReviews\Database\Query;
6
use GeminiLabs\SiteReviews\Database\Search\SearchAssignedPosts;
7
use GeminiLabs\SiteReviews\Database\Search\SearchAssignedUsers;
8
use GeminiLabs\SiteReviews\Database\Search\SearchPosts;
9
use GeminiLabs\SiteReviews\Database\Search\SearchUsers;
10
use GeminiLabs\SiteReviews\Database\SqlSchema;
11
use GeminiLabs\SiteReviews\Helpers\Arr;
12
use GeminiLabs\SiteReviews\Helpers\Cast;
13
use GeminiLabs\SiteReviews\Helpers\Str;
14
15
/**
16
 * @property array $mappedDeprecatedMethods
17
 */
18
class Database
19
{
20
    use Deprecated;
21
22
    protected $db;
23
24 24
    public function __construct()
25
    {
26 24
        global $wpdb;
27 24
        $this->db = $wpdb;
28 24
        $this->mappedDeprecatedMethods = [
29
            'get' => 'meta',
30
            'getTerms' => 'terms',
31
            'set' => 'metaSet',
32
        ];
33 24
    }
34
35
    /**
36
     * Use this before bulk insert (see: $this->finishTransaction()).
37
     * @param string $table
38
     * @return void
39
     */
40 14
    public function beginTransaction($table)
41
    {
42 14
        $sql = glsr(SqlSchema::class)->isInnodb($table)
43 14
            ? 'START TRANSACTION;'
44 14
            : 'SET autocommit = 0;';
45 14
        $this->dbQuery($sql);
46 14
    }
47
48
    /**
49
     * @param string $sql
50
     * @return array
51
     */
52 1
    public function dbGetCol($sql)
53
    {
54 1
        return $this->logErrors($this->db->get_col($sql));
55
    }
56
57
    /**
58
     * @param string $sql
59
     * @param string $output
60
     * @return array|object|null
61
     */
62 20
    public function dbGetResults($sql, $output = 'OBJECT')
63
    {
64 20
        $output = Str::restrictTo(['ARRAY_A', 'ARRAY_N', 'OBJECT', 'OBJECT_K'], $output, OBJECT);
65 20
        return $this->logErrors($this->db->get_results($sql, $output));
66
    }
67
68
    /**
69
     * @param string $sql
70
     * @param string $output
71
     * @return array|object|void|null
72
     */
73 14
    public function dbGetRow($sql, $output)
74
    {
75 14
        $output = Str::restrictTo(['ARRAY_A', 'ARRAY_N', 'OBJECT'], $output, OBJECT);
76 14
        return $this->logErrors($this->db->get_row($sql, $output));
77
    }
78
79
    /**
80
     * @param string $sql
81
     * @return string|null
82
     */
83 1
    public function dbGetVar($sql)
84
    {
85 1
        return $this->logErrors($this->db->get_var($sql));
86
    }
87
88
    /**
89
     * @param string $sql
90
     * @return int|bool
91
     */
92 22
    public function dbQuery($sql)
93
    {
94 22
        return $this->logErrors($this->db->query($sql));
95
    }
96
97
    /**
98
     * @param string $table
99
     * @return int|false
100
     */
101 3
    public function delete($table, array $where)
102
    {
103 3
        $result = $this->db->delete(glsr(Query::class)->table($table), $where);
104 3
        glsr(Query::class)->sql($this->db->last_query); // for logging use only
105 3
        return $this->logErrors($result);
106
    }
107
108
    /**
109
     * @return int|bool
110
     */
111 14
    public function deleteInvalidPostAssignments()
112
    {
113 14
        return $this->dbQuery(sprintf(
114 14
            glsr(Query::class)->sql("
115
                DELETE t
116
                FROM %s AS t
117
                LEFT JOIN %s AS r ON t.rating_id = r.ID
118 14
                LEFT JOIN {$this->db->posts} AS f ON t.post_id = f.ID
119
                WHERE (r.ID IS NULL OR f.ID IS NULL)
120
            "),
121 14
            glsr(Query::class)->table('assigned_posts'),
122 14
            glsr(Query::class)->table('ratings')
123
        ));
124
    }
125
126
    /**
127
     * @return int|bool
128
     */
129 14
    public function deleteInvalidReviews()
130
    {
131 14
        return $this->dbQuery(sprintf(
132 14
            glsr(Query::class)->sql("
133
                DELETE r
134
                FROM %s AS r
135 14
                LEFT JOIN {$this->db->posts} AS p ON r.review_id = p.ID
136
                WHERE (p.post_type IS NULL OR p.post_type != '%s')
137
            "),
138 14
            glsr(Query::class)->table('ratings'),
139 14
            glsr()->post_type
140
        ));
141
    }
142
143
    /**
144
     * @return int|bool
145
     */
146 14
    public function deleteInvalidTermAssignments()
147
    {
148 14
        return $this->dbQuery(sprintf(
149 14
            glsr(Query::class)->sql("
150
                DELETE t
151
                FROM %s AS t
152
                LEFT JOIN %s AS r ON t.rating_id = r.ID
153 14
                LEFT JOIN {$this->db->term_taxonomy} AS f ON t.term_id = f.term_id
154
                WHERE (r.ID IS NULL OR f.term_id IS NULL) OR f.taxonomy != '%s'
155
            "),
156 14
            glsr(Query::class)->table('assigned_terms'),
157 14
            glsr(Query::class)->table('ratings'),
158 14
            glsr()->taxonomy
159
        ));
160
    }
161
162
    /**
163
     * @return int|bool
164
     */
165 14
    public function deleteInvalidUserAssignments()
166
    {
167 14
        return $this->dbQuery(sprintf(
168 14
            glsr(Query::class)->sql("
169
                DELETE t
170
                FROM %s AS t
171
                LEFT JOIN %s AS r ON t.rating_id = r.ID
172 14
                LEFT JOIN {$this->db->users} AS f ON t.user_id = f.ID
173
                WHERE (r.ID IS NULL OR f.ID IS NULL)
174
            "),
175 14
            glsr(Query::class)->table('assigned_users'),
176 14
            glsr(Query::class)->table('ratings')
177
        ));
178
    }
179
180
    /**
181
     * @param string|string[] $keys
182
     * @param string $table
183
     * @return int|bool
184
     */
185 14
    public function deleteMeta($keys, $table = 'postmeta')
186
    {
187 14
        $table = glsr(Query::class)->table($table);
188 14
        $metaKeys = glsr(Query::class)->escValuesForInsert(Arr::convertFromString($keys));
189 14
        $sql = glsr(Query::class)->sql("
190 14
            DELETE FROM {$table} WHERE meta_key IN {$metaKeys}
191
        ");
192 14
        return $this->dbQuery($sql);
193
    }
194
195
    /**
196
     * Use this after bulk insert (see: $this->beginTransaction()).
197
     * @param string $table
198
     * @return void
199
     */
200 14
    public function finishTransaction($table)
201
    {
202 14
        $sql = glsr(SqlSchema::class)->isInnodb($table)
203 14
            ? 'COMMIT;'
204 14
            : 'SET autocommit = 1;';
205 14
        $this->dbQuery($sql);
206 14
    }
207
208
    /**
209
     * @param string $table
210
     * @return int|bool
211
     */
212 14
    public function insert($table, array $data)
213
    {
214 14
        $this->db->insert_id = 0;
215 14
        $table = glsr(Query::class)->table($table);
216 14
        $fields = glsr(Query::class)->escFieldsForInsert(array_keys($data));
217 14
        $values = glsr(Query::class)->escValuesForInsert($data);
218 14
        $sql = glsr(Query::class)->sql("INSERT IGNORE INTO {$table} {$fields} VALUES {$values}");
219 14
        $result = $this->dbQuery($sql);
220 14
        return empty($result) ? false : $result;
221
    }
222
223
    /**
224
     * @param string $table
225
     * @return int|false
226
     */
227
    public function insertBulk($table, array $values, array $fields)
228
    {
229
        $this->db->insert_id = 0;
230
        $data = [];
231
        foreach ($values as $value) {
232
            $value = array_intersect_key($value, array_flip($fields)); // only keep field values
233
            if (count($value) === count($fields)) {
234
                $value = array_merge(array_flip($fields), $value); // make sure the order is correct
235
                $data[] = glsr(Query::class)->escValuesForInsert($value);
236
            }
237
        }
238
        $table = glsr(Query::class)->table($table);
239
        $fields = glsr(Query::class)->escFieldsForInsert($fields);
240
        $values = implode(',', $data);
241
        $sql = glsr(Query::class)->sql("INSERT IGNORE INTO {$table} {$fields} VALUES {$values}");
242
        return $this->dbQuery($sql);
243
    }
244
245
    /**
246
     * @return bool
247
     */
248 7
    public function isMigrationNeeded()
249
    {
250 7
        $table = glsr(Query::class)->table('ratings');
251 7
        $postCount = wp_count_posts(glsr()->post_type)->publish;
252 7
        if (empty($postCount)) {
253 7
            return false;
254
        }
255
        $sql = glsr(Query::class)->sql("SELECT COUNT(*) FROM {$table} WHERE is_approved = 1");
256
        return empty($this->dbGetVar($sql));
257
    }
258
259
    /**
260
     * @param mixed $result
261
     * @return mixed
262
     */
263 22
    public function logErrors($result = null)
264
    {
265 22
        if ($this->db->last_error) {
266
            glsr_log()->error($this->db->last_error);
267
            glsr_trace();
268
        }
269 22
        return $result;
270
    }
271
272
    /**
273
     * @param int $postId
274
     * @param string $key
275
     * @param bool $single
276
     * @return mixed
277
     */
278
    public function meta($postId, $key, $single = true)
279
    {
280
        $key = Str::prefix($key, '_');
281
        $postId = Cast::toInt($postId);
282
        return get_post_meta($postId, $key, $single);
283
    }
284
285
    /**
286
     * @param int $postId
287
     * @param string $key
288
     * @param mixed $value
289
     * @return int|bool
290
     */
291
    public function metaSet($postId, $key, $value)
292
    {
293
        $key = Str::prefix($key, '_');
294
        $postId = Cast::toInt($postId);
295
        return update_metadata('post', $postId, $key, $value); // update_metadata works with revisions
296
    }
297
298
    /**
299
     * @param string $searchTerm
300
     * @return SearchAssignedPosts
301
     */
302
    public function searchAssignedPosts($searchTerm)
303
    {
304
        return glsr(SearchAssignedPosts::class)->search($searchTerm);
305
    }
306
307
    /**
308
     * @param string $searchTerm
309
     * @return SearchAssignedUsers
310
     */
311
    public function searchAssignedUsers($searchTerm)
312
    {
313
        return glsr(SearchAssignedUsers::class)->search($searchTerm);
314
    }
315
316
    /**
317
     * @param string $searchTerm
318
     * @return SearchPosts
319
     */
320
    public function searchPosts($searchTerm)
321
    {
322
        return glsr(SearchPosts::class)->search($searchTerm);
323
    }
324
325
    /**
326
     * @param string $searchTerm
327
     * @return SearchUsers
328
     */
329
    public function searchUsers($searchTerm)
330
    {
331
        return glsr(SearchUsers::class)->search($searchTerm);
332
    }
333
334
    /**
335
     * @return array
336
     */
337 7
    public function terms(array $args = [])
338
    {
339 7
        $args = wp_parse_args($args, [
340 7
            'count' => false,
341 7
            'fields' => 'id=>name',
342
            'hide_empty' => false,
343 7
            'taxonomy' => glsr()->taxonomy,
344
        ]);
345 7
        $terms = get_terms($args);
346 7
        if (is_wp_error($terms)) {
347
            glsr_log()->error($terms->get_error_message());
348
            glsr_trace();
349
            return [];
350
        }
351 7
        return $terms;
352
    }
353
354
    /**
355
     * @param string $table
356
     * @return int|bool
357
     */
358
    public function update($table, array $data, array $where)
359
    {
360
        $result = $this->db->update(glsr(Query::class)->table($table), $data, $where);
361
        glsr(Query::class)->sql($this->db->last_query); // for logging use only
362
        return $this->logErrors($result);
363
    }
364
365
    /**
366
     * @return array
367
     */
368
    public function users(array $args = [])
369
    {
370
        $args = wp_parse_args($args, [
371
            'fields' => ['ID', 'display_name'],
372
            'number' => 50, // only get the first 50 users!
373
            'orderby' => 'display_name',
374
        ]);
375
        $users = get_users($args);
376
        return wp_list_pluck($users, 'display_name', 'ID');
377
    }
378
379
    /**
380
     * @param string $compareToVersion
381
     * @return bool|string
382
     */
383 24
    public function version($compareToVersion = null)
384
    {
385 24
        $dbVersion = Cast::toString(get_option(glsr()->prefix.'db_version'));
386 24
        if (version_compare($dbVersion, '2', '>')) { // @compat version should always be less than 2 for now
387
            update_option(glsr()->prefix.'db_version', '1.0');
388
            $dbVersion = '1.0';
389
        }
390 24
        return isset($compareToVersion)
391 24
            ? version_compare($dbVersion, Cast::toString($compareToVersion), '>=')
392 24
            : $dbVersion;
393
    }
394
}
395