Passed
Push — master ( 6268c8...c33117 )
by Paul
14:11 queued 01:11
created

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