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

Sql::clauseAndRatingField()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
nc 1
nop 0
crap 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
11
trait Sql
12
{
13
    /**
14
     * @var array
15
     */
16
    public $args;
17
18
    /**
19
     * @var \wpdb
20
     */
21
    public $db;
22
23
    /**
24
     * @param string $clause
25
     * @return array
26
     */
27 8
    public function clauses($clause, array $values = [])
28
    {
29 8
        $prefix = Str::restrictTo('and,join', $clause);
30 8
        foreach ($this->args as $key => $value) {
31 8
            $method = Helper::buildMethodName($key, 'clause-'.$prefix);
32 8
            if (!method_exists($this, $method) || Helper::isEmpty($value)) {
33 8
                continue;
34
            }
35 8
            if ($statement = call_user_func([$this, $method])) {
36 8
                $values[$key] = $statement;
37
            }
38
        }
39 8
        return $values;
40
    }
41
42
    /**
43
     * @return string
44
     */
45 14
    public function escFieldsForInsert(array $fields)
46
    {
47 14
        return sprintf('(`%s`)', implode('`,`', $fields));
48
    }
49
50
    /**
51
     * @return string
52
     */
53 22
    public function escValuesForInsert(array $values)
54
    {
55 22
        $values = array_values(array_map('esc_sql', $values));
56 22
        return sprintf("('%s')", implode("','", $values));
57
    }
58
59
    /**
60
     * @param string $statement
61
     * @return string
62
     */
63 22
    public function sql($statement)
64
    {
65 22
        $handle = $this->sqlHandle();
66 22
        $statement = glsr()->filterString('database/sql/'.$handle, $statement);
67 22
        glsr()->action('database/sql/'.$handle, $statement);
68 22
        glsr()->action('database/sql', $statement, $handle);
69 22
        return $statement;
70
    }
71
72
    /**
73
     * @return string
74
     */
75 7
    public function sqlJoin()
76
    {
77 7
        $join = $this->clauses('join');
78 7
        $join = glsr()->filterArrayUnique('query/sql/join', $join, $this->sqlHandle(), $this);
79 7
        return implode(' ', $join);
80
    }
81
82
    /**
83
     * @return string
84
     */
85 1
    public function sqlLimit()
86
    {
87 1
        $limit = Helper::ifTrue($this->args['per_page'] > 0,
88 1
            $this->db->prepare('LIMIT %d', $this->args['per_page'])
89
        );
90 1
        return glsr()->filterString('query/sql/limit', $limit, $this->sqlHandle(), $this);
91
    }
92
93
    /**
94
     * @return string
95
     */
96 1
    public function sqlOffset()
97
    {
98 1
        $offsetBy = (($this->args['page'] - 1) * $this->args['per_page']) + $this->args['offset'];
99 1
        $offset = Helper::ifTrue($offsetBy > 0,
100 1
            $this->db->prepare('OFFSET %d', $offsetBy)
101
        );
102 1
        return glsr()->filterString('query/sql/offset', $offset, $this->sqlHandle(), $this);
103
    }
104
105
    /**
106
     * @return string|void
107
     */
108 1
    public function sqlOrderBy()
109
    {
110
        $values = [
111 1
            'random' => 'RAND()',
112
        ];
113 1
        $order = $this->args['order'];
114 1
        $orderby = $this->args['orderby'];
115 1
        $orderedby = [];
116 1
        if (Str::startsWith(['p.', 'r.'], $orderby)) {
117 1
            $orderedby[] = "r.is_pinned {$order}";
118 1
            $orderedby[] = "{$orderby} {$order}";
119
        } elseif (array_key_exists($orderby, $values)) {
120
            $orderedby[] = $values[$orderby];
121
        }
122 1
        $orderedby = glsr()->filterArrayUnique('query/sql/order-by', $orderedby, $this->sqlHandle(), $this);
123 1
        if (!empty($orderedby)) {
124 1
            return 'ORDER BY '.implode(', ', $orderedby);
125
        }
126
    }
127
128
    /**
129
     * @return string
130
     */
131 7
    public function sqlWhere()
132
    {
133 7
        $and = $this->clauses('and');
134 7
        $and = glsr()->filterArrayUnique('query/sql/and', $and, $this->sqlHandle(), $this);
135 7
        $and = $this->normalizeAndClauses($and);
136 7
        return 'WHERE 1=1 '.implode(' ', $and);
137
    }
138
139
    /**
140
     * @return string
141
     */
142 23
    public function table($table)
143
    {
144 23
        return glsr(SqlSchema::class)->table($table);
145
    }
146
147
    /**
148
     * @return string
149
     */
150 2
    protected function clauseAndAssignedPosts()
151
    {
152 2
        return $this->clauseIfValueNotEmpty('(apt.post_id IN (%s) AND apt.is_published = 1)', $this->args['assigned_posts']);
153
    }
154
155
    /**
156
     * @return string
157
     */
158 2
    protected function clauseAndAssignedTerms()
159
    {
160 2
        return $this->clauseIfValueNotEmpty('(att.term_id IN (%s))', $this->args['assigned_terms']);
161
    }
162
163
    /**
164
     * @return string
165
     */
166 2
    protected function clauseAndAssignedUsers()
167
    {
168 2
        return $this->clauseIfValueNotEmpty('(aut.user_id IN (%s))', $this->args['assigned_users']);
169
    }
170
171
    /**
172
     * @return string
173
     */
174 7
    protected function clauseAndDate()
175
    {
176 7
        $clauses = [];
177 7
        $date = $this->args['date'];
178 7
        if (!empty($date['after'])) {
179
            $clauses[] = $this->db->prepare("(p.post_date >{$date['inclusive']} %s)", $date['after']);
180
        }
181 7
        if (!empty($date['before'])) {
182
            $clauses[] = $this->db->prepare("(p.post_date <{$date['inclusive']} %s)", $date['before']);
183
        }
184 7
        if (!empty($date['year'])) {
185
            $clauses[] = $this->db->prepare('(YEAR(p.post_date) = %d AND MONTH(p.post_date) = %d AND DAYOFMONTH(p.post_date) = %d)',
186
                $date['year'], $date['month'], $date['day']
187
            );
188
        }
189 7
        if ($clauses = implode(' AND ', $clauses)) {
190
            return sprintf('AND (%s)', $clauses);
191
        }
192 7
        return '';
193
    }
194
195
    /**
196
     * @return string
197
     */
198 1
    protected function clauseAndEmail()
199
    {
200 1
        return $this->clauseIfValueNotEmpty('AND r.email = %s', $this->args['email']);
201
    }
202
203
    /**
204
     * @return string
205
     */
206 1
    protected function clauseAndIpAddress()
207
    {
208 1
        return $this->clauseIfValueNotEmpty('AND r.ip_address = %s', $this->args['ip_address']);
209
    }
210
211
    /**
212
     * @return string
213
     */
214
    protected function clauseAndPostIn()
215
    {
216
        return $this->clauseIfValueNotEmpty('AND r.review_id IN (%s)', $this->args['post__in']);
217
    }
218
219
    /**
220
     * @return string
221
     */
222
    protected function clauseAndPostNotIn()
223
    {
224
        return $this->clauseIfValueNotEmpty('AND r.review_id NOT IN (%s)', $this->args['post__not_in']);
225
    }
226
227
    /**
228
     * @return string
229
     */
230 7
    protected function clauseAndRating()
231
    {
232 7
        $column = $this->isCustomRatingField() ? 'pm.meta_value' : 'r.rating';
233 7
        return Helper::ifTrue($this->args['rating'] > 0,
234 7
            $this->db->prepare("AND {$column} > %d", --$this->args['rating'])
235
        );
236
    }
237
238
    /**
239
     * @return string
240
     */
241 7
    protected function clauseAndRatingField()
242
    {
243 7
        return Helper::ifTrue($this->isCustomRatingField(),
244 7
            $this->db->prepare("AND pm.meta_key = %s", sprintf('_custom_%s', $this->args['rating_field']))
245
        );
246
    }
247
248
    /**
249
     * @return string
250
     */
251 20
    protected function clauseAndStatus()
252
    {
253 20
        return $this->clauseIfValueNotEmpty('AND r.is_approved = %d', $this->args['status']);
254
    }
255
256
    /**
257
     * @return string
258
     */
259
    protected function clauseAndTerms()
260
    {
261
        if (Helper::isEmpty($this->args['terms'])) {
262
            return '';
263
        }
264
        $value = Cast::toInt(Cast::toBool($this->args['terms']));
265
        return $this->clauseIfValueNotEmpty('AND r.terms = %d', $value);
266
    }
267
268
    /**
269
     * @return string
270
     */
271 14
    protected function clauseAndType()
272
    {
273 14
        return $this->clauseIfValueNotEmpty('AND r.type = %s', $this->args['type']);
274
    }
275
276
    /**
277
     * @return string
278
     */
279 1
    protected function clauseAndUserIn()
280
    {
281 1
        return $this->clauseIfValueNotEmpty('AND p.post_author IN (%s)', $this->args['user__in']);
282
    }
283
284
    /**
285
     * @return string
286
     */
287
    protected function clauseAndUserNotIn()
288
    {
289
        return $this->clauseIfValueNotEmpty('AND p.post_author NOT IN (%s)', $this->args['user__not_in']);
290
    }
291
292
    /**
293
     * @param string $clause
294
     * @param array|int|string $value
295
     * @param bool $prepare
296
     * @return string
297
     */
298 21
    protected function clauseIfValueNotEmpty($clause, $value, $prepare = true)
299
    {
300 21
        if (Helper::isEmpty($value)) {
301 21
            return '';
302
        }
303 21
        if (!$prepare) {
304 8
            return $clause;
305
        }
306 20
        if (is_array($value)) {
307 7
            $value = implode(',', $value);
308
        }
309 20
        return $this->db->prepare($clause, $value);
310
    }
311
312
    /**
313
     * @return string
314
     */
315 3
    protected function clauseJoinAssignedPosts()
316
    {
317 3
        return $this->clauseIfValueNotEmpty(
318 3
            "{$this->joinMethod()} {$this->table('assigned_posts')} AS apt ON r.ID = apt.rating_id",
319 3
            $this->args['assigned_posts'],
320 3
            $prepare = false
321
        );
322
    }
323
324
    /**
325
     * @return string
326
     */
327 3
    protected function clauseJoinAssignedTerms()
328
    {
329 3
        return $this->clauseIfValueNotEmpty(
330 3
            "{$this->joinMethod()} {$this->table('assigned_terms')} AS att ON r.ID = att.rating_id",
331 3
            $this->args['assigned_terms'],
332 3
            $prepare = false
333
        );
334
    }
335
336
    /**
337
     * @return string
338
     */
339 3
    protected function clauseJoinAssignedUsers()
340
    {
341 3
        return $this->clauseIfValueNotEmpty(
342 3
            "{$this->joinMethod()} {$this->table('assigned_users')} AS aut ON r.ID = aut.rating_id",
343 3
            $this->args['assigned_users'],
344 3
            $prepare = false
345
        );
346
    }
347
348
    /**
349
     * @return string
350
     */
351 8
    protected function clauseJoinDate()
352
    {
353 8
        return $this->clauseIfValueNotEmpty(
354 8
            "INNER JOIN {$this->db->posts} AS p ON r.review_id = p.ID",
355 8
            array_filter($this->args['date']),
356 8
            $prepare = false
357
        );
358
    }
359
360
    /**
361
     * @return string
362
     */
363 1
    protected function clauseJoinUserIn()
364
    {
365 1
        return $this->clauseIfValueNotEmpty(
366 1
            "INNER JOIN {$this->db->posts} AS p ON r.review_id = p.ID",
367 1
            $this->args['user__in'],
368 1
            $prepare = false
369
        );
370
    }
371
372
    /**
373
     * @return string
374
     */
375
    protected function clauseJoinUserNotIn()
376
    {
377
        return $this->clauseIfValueNotEmpty(
378
            "INNER JOIN {$this->db->posts} AS p ON r.review_id = p.ID",
379
            $this->args['user__not_in'],
380
            $prepare = false
381
        );
382
    }
383
384
    /**
385
     * @return string
386
     */
387 2
    protected function clauseJoinOrderBy()
388
    {
389 2
        return Helper::ifTrue(Str::startsWith('p.', $this->args['orderby']),
390 2
            "INNER JOIN {$this->db->posts} AS p ON r.review_id = p.ID"
391
        );
392
    }
393
394
    /**
395
     * @return string
396
     */
397 8
    protected function clauseJoinRatingField()
398
    {
399 8
        return Helper::ifTrue($this->isCustomRatingField(), 
400 8
            "INNER JOIN {$this->db->postmeta} AS pm ON r.review_id = pm.post_id"
401
        );
402
    }
403
404
    /**
405
     * @return bool
406
     */
407 21
    protected function isCustomRatingField()
408
    {
409 21
        return 'rating' !== $this->args['rating_field'] && !empty($this->args['rating_field']);
410
    }
411
412
    /**
413
     * Used to determine the join method used in review assignments
414
     * @return string
415
     */
416 7
    protected function joinMethod()
417
    {
418 7
        $joins = ['loose' => 'LEFT JOIN', 'strict' => 'INNER JOIN'];
419 7
        return Arr::get($joins, glsr_get_option('reviews.assignment', 'strict'), 'INNER JOIN');
420
    }
421
422
    /**
423
     * @return array
424
     */
425 7
    protected function normalizeAndClauses(array $and)
426
    {
427 7
        $clauses = [];
428 7
        foreach ($and as $key => $value) {
429 7
            if (Str::startsWith('assigned_', $key)) {
430 6
                $clauses[] = $value;
431 6
                unset($and[$key]);
432
            }
433
        }
434 7
        $operator = glsr()->filterString('query/sql/clause/operator', 'OR', $clauses, $this->args);
435 7
        $operator = strtoupper($operator);
436 7
        $operator = Helper::ifTrue(in_array($operator, ['AND', 'OR']), $operator, 'OR');
437 7
        if ($clauses = implode(" {$operator} ", $clauses)) {
438 6
            $and['assigned'] = "AND ($clauses)";
439
        }
440 7
        return $and;
441
    }
442
443
    /**
444
     * @return string
445
     */
446 20
    protected function ratingColumn()
447
    {
448 20
        return Helper::ifTrue($this->isCustomRatingField(), 'pm.meta_value', 'r.rating');
449
    }
450
451
    /**
452
     * @param int $depth
453
     * @return string
454
     */
455 22
    protected function sqlHandle($depth = 2)
456
    {
457 22
        return Str::dashCase(Arr::get((new \Exception())->getTrace(), $depth.'.function'));
458
    }
459
}
460