Passed
Push — master ( 18d41c...f9af5f )
by Paul
13:52 queued 07:13
created

Query   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 348
Duplicated Lines 0 %

Test Coverage

Coverage 75.17%

Importance

Changes 6
Bugs 3 Features 0
Metric Value
wmc 37
eloc 127
c 6
b 3
f 0
dl 0
loc 348
ccs 112
cts 149
cp 0.7517
rs 9.44

24 Methods

Rating   Name   Duplication   Size   Complexity  
A review() 0 14 3
A totalReviews() 0 7 3
A ratings() 0 5 1
A export() 0 4 1
A ratingsFor() 0 9 2
A setArgs() 0 7 2
A import() 0 4 1
A queryExport() 0 13 1
A queryHasRevisions() 0 7 1
A revisionIds() 0 3 1
A normalizeRatingsByAssignedId() 0 12 3
A queryImport() 0 11 1
A normalizeRatings() 0 11 3
A hasRevisions() 0 3 1
A __construct() 0 4 1
A reviews() 0 15 3
A queryReviewIds() 0 11 1
A queryRevisionIds() 0 7 1
A queryReviews() 0 24 2
A queryTotalReviews() 0 7 1
A queryRatingsForUsermeta() 0 10 1
A queryRatingsForPostmeta() 0 10 1
A queryRatingsForTermmeta() 0 10 1
A queryRatings() 0 8 1
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Database;
4
5
use GeminiLabs\SiteReviews\Database;
6
use GeminiLabs\SiteReviews\Helper;
7
use GeminiLabs\SiteReviews\Helpers\Arr;
8
use GeminiLabs\SiteReviews\Helpers\Cast;
9
use GeminiLabs\SiteReviews\Helpers\Str;
10
use GeminiLabs\SiteReviews\Modules\Rating;
11
use GeminiLabs\SiteReviews\Review;
12
13
/**
14
 * @property array $args
15
 * @property \wpdb $db
16
 */
17
class Query
18
{
19
    use Sql;
20
21 23
    public function __construct()
22
    {
23 23
        global $wpdb;
24 23
        $this->db = $wpdb;
25 23
    }
26
27
    /**
28
     * @return array
29
     */
30
    public function export(array $args = [])
31
    {
32
        $this->setArgs($args);
33
        return glsr(Database::class)->dbGetResults($this->queryExport(), ARRAY_A);
34
    }
35
36
    /**
37
     * @param int $postId
38
     * @return bool
39
     */
40
    public function hasRevisions($postId)
41
    {
42
        return (int) glsr(Database::class)->dbGetVar($this->queryHasRevisions($postId)) > 0;
43
    }
44
45
    /**
46
     * @return array
47
     */
48
    public function import(array $args = [])
49
    {
50
        $this->setArgs($args);
51
        return glsr(Database::class)->dbGetResults($this->queryImport(), ARRAY_A);
52
    }
53
54
    /**
55
     * @return array
56
     */
57 6
    public function ratings(array $args = [])
58
    {
59 6
        $this->setArgs($args, $unset = ['orderby']);
60 6
        $results = glsr(Database::class)->dbGetResults($this->queryRatings(), ARRAY_A);
61 6
        return $this->normalizeRatings($results);
62
    }
63
64
    /**
65
     * @param string $metaType
66
     * @return array
67
     */
68 14
    public function ratingsFor($metaType, array $args = [])
69
    {
70 14
        $method = Helper::buildMethodName($metaType, 'queryRatingsFor');
71 14
        if (!method_exists($this, $method)) {
72
            return [];
73
        }
74 14
        $this->setArgs($args, $unset = ['orderby']);
75 14
        $results = glsr(Database::class)->dbGetResults($this->$method(), ARRAY_A);
76 14
        return $this->normalizeRatingsByAssignedId($results);
77
    }
78
79
    /**
80
     * @param bool $bypassCache
81
     * @param int $postId
82
     * @return Review
83
     */
84 14
    public function review($postId, $bypassCache = false)
85
    {
86 14
        $reviewId = Cast::toInt($postId);
87
        $review = Helper::ifTrue($bypassCache, null, function () use ($reviewId) {
88 14
            return glsr(Cache::class)->get($reviewId, 'reviews');
89 14
        });
90 14
        if (!$review instanceof Review) {
91 14
            $result = glsr(Database::class)->dbGetRow($this->queryReviews($reviewId), OBJECT);
92 14
            $review = new Review($result);
93 14
            if ($review->isValid()) {
94 14
                glsr(Cache::class)->store($review->ID, 'reviews', $review);
95
            }
96
        }
97 14
        return $review;
98
    }
99
100
    /**
101
     * @return array
102
     */
103 1
    public function reviews(array $args = [], array $postIds = [])
104
    {
105 1
        $this->setArgs($args);
106 1
        if (empty($postIds)) {
107
            // We previously used a subquery here, but MariaDB didn't support it.
108 1
            $postIds = glsr(Database::class)->dbGetCol($this->queryReviewIds());
109
        }
110 1
        $reviewIds = implode(',', Arr::uniqueInt(Cast::toArray($postIds)));
111 1
        $reviewIds = Str::fallback($reviewIds, '0'); // if there are no review IDs, default to 0
112 1
        $results = glsr(Database::class)->dbGetResults($this->queryReviews($reviewIds), OBJECT);
113 1
        foreach ($results as &$result) {
114 1
            $result = new Review($result);
115 1
            glsr(Cache::class)->store($result->ID, 'reviews', $result);
116
        }
117 1
        return $results;
118
    }
119
120
    /**
121
     * @param int $postId
122
     * @return array
123
     */
124
    public function revisionIds($postId)
125
    {
126
        return glsr(Database::class)->dbGetCol($this->queryRevisionIds($postId));
127
    }
128
129
    /**
130
     * @return void
131
     */
132 21
    public function setArgs(array $args = [], array $unset = [])
133
    {
134 21
        $args = (new NormalizeQueryArgs($args))->toArray();
135 21
        foreach ($unset as $key) {
136 20
            $args[$key] = '';
137
        }
138 21
        $this->args = $args;
139 21
    }
140
141
    /**
142
     * @return int
143
     */
144 1
    public function totalReviews(array $args = [], array $reviews = [])
145
    {
146 1
        $this->setArgs($args, $unset = ['orderby']);
147 1
        if (empty($this->sqlLimit()) && !empty($reviews)) {
148
            return count($reviews);
149
        }
150 1
        return (int) glsr(Database::class)->dbGetVar($this->queryTotalReviews());
151
    }
152
153
    /**
154
     * @return array
155
     */
156 6
    protected function normalizeRatings(array $ratings = [])
157
    {
158 6
        $normalized = [];
159 6
        foreach ($ratings as $result) {
160 6
            $type = $result['type'];
161 6
            if (!array_key_exists($type, $normalized)) {
162 6
                $normalized[$type] = glsr(Rating::class)->emptyArray();
163
            }
164 6
            $normalized[$type] = Arr::set($normalized[$type], $result['rating'], $result['count']);
165
        }
166 6
        return $normalized;
167
    }
168
169
    /**
170
     * @return array
171
     */
172 14
    protected function normalizeRatingsByAssignedId(array $ratings = [])
173
    {
174 14
        $normalized = [];
175 14
        foreach ($ratings as $result) {
176
            $id = $result['ID'];
177
            unset($result['ID']);
178
            if (!array_key_exists($id, $normalized)) {
179
                $normalized[$id] = [];
180
            }
181
            $normalized[$id][] = $result;
182
        }
183 14
        return array_map([$this, 'normalizeRatings'], $normalized);
184
    }
185
186
    /**
187
     * @return string
188
     */
189
    protected function queryExport()
190
    {
191
        return $this->sql("
192
            SELECT r.*,
193
                GROUP_CONCAT(DISTINCT apt.post_id) AS post_ids,
194
                GROUP_CONCAT(DISTINCT aut.user_id) AS user_ids
195
            FROM {$this->table('ratings')} AS r
196
            LEFT JOIN {$this->table('assigned_posts')} AS apt ON r.ID = apt.rating_id
197
            LEFT JOIN {$this->table('assigned_users')} AS aut ON r.ID = aut.rating_id
198
            GROUP BY r.ID
199
            ORDER BY r.ID
200
            {$this->sqlLimit()}
201
            {$this->sqlOffset()}
202
        ");
203
    }
204
205
    /**
206
     * @return string
207
     */
208
    protected function queryHasRevisions($reviewId)
209
    {
210
        return $this->sql($this->db->prepare("
211
            SELECT COUNT(*) 
212
            FROM {$this->db->posts}
213
            WHERE post_type = 'revision' AND post_parent = %d
214
        ", $reviewId));
215
    }
216
217
    /**
218
     * @return string
219
     */
220
    protected function queryImport()
221
    {
222
        return $this->sql($this->db->prepare("
223
            SELECT m.post_id, m.meta_value
224
            FROM {$this->db->postmeta} AS m
225
            INNER JOIN {$this->db->posts} AS p ON m.post_id = p.ID
226
            WHERE p.post_type = %s AND m.meta_key = %s
227
            ORDER BY m.meta_id
228
            {$this->sqlLimit()}
229
            {$this->sqlOffset()}
230
        ", glsr()->post_type, glsr()->export_key));
231
    }
232
233
    /**
234
     * @return string
235
     */
236 6
    protected function queryRatings()
237
    {
238 6
        return $this->sql("
239 6
            SELECT {$this->ratingColumn()} AS rating, r.type, COUNT(DISTINCT r.ID) AS count
240 6
            FROM {$this->table('ratings')} AS r
241 6
            {$this->sqlJoin()}
242 6
            {$this->sqlWhere()}
243 6
            GROUP BY r.type, {$this->ratingColumn()}
244
        ");
245
    }
246
247
    /**
248
     * @return string
249
     */
250 14
    public function queryRatingsForPostmeta()
251
    {
252 14
        return $this->sql("
253 14
            SELECT apt.post_id AS ID, {$this->ratingColumn()} AS rating, r.type, COUNT(DISTINCT r.ID) AS count
254 14
            FROM {$this->table('ratings')} AS r
255 14
            INNER JOIN {$this->table('assigned_posts')} AS apt ON r.ID = apt.rating_id
256
            WHERE 1=1
257 14
            {$this->clauseAndStatus()}
258 14
            {$this->clauseAndType()}
259 14
            GROUP BY r.type, {$this->ratingColumn()}, apt.post_id
260
        ");
261
    }
262
263
    /**
264
     * @return string
265
     */
266 14
    protected function queryRatingsForTermmeta()
267
    {
268 14
        return $this->sql("
269 14
            SELECT att.term_id AS ID, {$this->ratingColumn()} AS rating, r.type, COUNT(DISTINCT r.ID) AS count
270 14
            FROM {$this->table('ratings')} AS r
271 14
            INNER JOIN {$this->table('assigned_terms')} AS att ON r.ID = att.rating_id
272
            WHERE 1=1
273 14
            {$this->clauseAndStatus()}
274 14
            {$this->clauseAndType()}
275 14
            GROUP BY r.type, {$this->ratingColumn()}, att.term_id
276
        ");
277
    }
278
279
    /**
280
     * @return string
281
     */
282 14
    protected function queryRatingsForUsermeta()
283
    {
284 14
        return $this->sql("
285 14
            SELECT aut.user_id AS ID, {$this->ratingColumn()} AS rating, r.type, COUNT(DISTINCT r.ID) AS count
286 14
            FROM {$this->table('ratings')} AS r
287 14
            INNER JOIN {$this->table('assigned_users')} AS aut ON r.ID = aut.rating_id
288
            WHERE 1=1
289 14
            {$this->clauseAndStatus()}
290 14
            {$this->clauseAndType()}
291 14
            GROUP BY r.type, {$this->ratingColumn()}, aut.user_id
292
        ");
293
    }
294
295
    /**
296
     * @return string
297
     */
298 1
    protected function queryReviewIds()
299
    {
300 1
        return $this->sql("
301
            SELECT r.review_id
302 1
            FROM {$this->table('ratings')} AS r
303 1
            {$this->sqlJoin()}
304 1
            {$this->sqlWhere()}
305
            GROUP BY r.review_id
306 1
            {$this->sqlOrderBy()}
307 1
            {$this->sqlLimit()}
308 1
            {$this->sqlOffset()}
309
        ");
310
    }
311
312
    /**
313
     * @param int|string $reviewIds
314
     * @return string
315
     */
316 14
    protected function queryReviews($reviewIds)
317
    {
318 14
        $orderBy = !empty($this->args['order']) ? $this->sqlOrderBy() : '';
319 14
        $postType = glsr()->post_type;
320 14
        return $this->sql("
321
            SELECT
322
                r.*,
323
                p.post_author AS author_id,
324
                p.post_date AS date,
325
                p.post_date_gmt AS date_gmt,
326
                p.post_content AS content,
327
                p.post_title AS title,
328
                p.post_status AS status,
329
                GROUP_CONCAT(DISTINCT apt.post_id) AS post_ids,
330
                GROUP_CONCAT(DISTINCT att.term_id) AS term_ids,
331
                GROUP_CONCAT(DISTINCT aut.user_id) AS user_ids
332 14
            FROM {$this->table('ratings')} AS r
333 14
            INNER JOIN {$this->db->posts} AS p ON r.review_id = p.ID
334 14
            LEFT JOIN {$this->table('assigned_posts')} AS apt ON r.ID = apt.rating_id
335 14
            LEFT JOIN {$this->table('assigned_terms')} AS att ON r.ID = att.rating_id
336 14
            LEFT JOIN {$this->table('assigned_users')} AS aut ON r.ID = aut.rating_id
337 14
            WHERE r.review_id IN ({$reviewIds}) AND p.post_type = '{$postType}'
338
            GROUP BY r.ID
339 14
            {$orderBy}
340
        ");
341
    }
342
343
    /**
344
     * @return string
345
     */
346
    protected function queryRevisionIds($reviewId)
347
    {
348
        return $this->sql($this->db->prepare("
349
            SELECT ID
350
            FROM {$this->db->posts}
351
            WHERE post_type = 'revision' AND post_parent = %d
352
        ", $reviewId));
353
    }
354
355
    /**
356
     * @return string
357
     */
358 1
    protected function queryTotalReviews()
359
    {
360 1
        return $this->sql("
361
            SELECT COUNT(DISTINCT r.ID) AS count
362 1
            FROM {$this->table('ratings')} AS r
363 1
            {$this->sqlJoin()}
364 1
            {$this->sqlWhere()}
365
        ");
366
    }
367
}
368