Skip to content

Commit 5de29d6

Browse files
committed
"Query" class refactor and tested.
1 parent b71f72a commit 5de29d6

File tree

3 files changed

+213
-109
lines changed

3 files changed

+213
-109
lines changed

Diff for: classes/class-db-driver-wpdb.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@ class DB_Driver_WPDB implements DB_Driver {
3636
* Class constructor.
3737
*/
3838
public function __construct() {
39-
$this->query = new Query( $this );
40-
4139
global $wpdb;
40+
4241
$prefix = apply_filters( 'wp_stream_db_tables_prefix', $wpdb->base_prefix );
4342

4443
$this->table = $prefix . 'stream';
@@ -49,6 +48,8 @@ public function __construct() {
4948

5049
// Hack for get_metadata.
5150
$wpdb->recordmeta = $this->table_meta;
51+
52+
$this->query = new Query( $wpdb );
5253
}
5354

5455
/**

Diff for: classes/class-query.php

+123-107
Original file line numberDiff line numberDiff line change
@@ -70,104 +70,75 @@ public function query( $args ) {
7070
$join = '';
7171
$where = '';
7272

73-
/**
74-
* PARSE CORE PARAMS
75-
*/
76-
if ( is_numeric( $args['site_id'] ) ) {
77-
$where .= $wpdb->prepare( " AND $wpdb->stream.site_id = %d", $args['site_id'] );
78-
}
79-
80-
if ( is_numeric( $args['blog_id'] ) ) {
81-
$where .= $wpdb->prepare( " AND $wpdb->stream.blog_id = %d", $args['blog_id'] );
82-
}
83-
84-
if ( is_numeric( $args['object_id'] ) ) {
85-
$where .= $wpdb->prepare( " AND $wpdb->stream.object_id = %d", $args['object_id'] );
86-
}
87-
88-
if ( is_numeric( $args['user_id'] ) ) {
89-
$where .= $wpdb->prepare( " AND $wpdb->stream.user_id = %d", $args['user_id'] );
90-
}
91-
92-
if ( ! empty( $args['user_role'] ) ) {
93-
$where .= $wpdb->prepare( " AND $wpdb->stream.user_role = %s", $args['user_role'] );
94-
}
95-
96-
if ( ! empty( $args['search'] ) ) {
97-
$field = ! empty( $args['search_field'] ) ? $args['search_field'] : 'summary';
98-
$field = $this->lookup_field_validated( $field );
99-
100-
// Sanitize field.
101-
$allowed_fields = array( 'ID', 'site_id', 'blog_id', 'object_id', 'user_id', 'user_role', 'created', 'summary', 'connector', 'context', 'action', 'ip' );
102-
if ( in_array( $field, $allowed_fields, true ) ) {
103-
$where .= $wpdb->prepare( " AND $wpdb->stream.{$field} LIKE %s", "%{$args['search']}%" ); // @codingStandardsIgnoreLine can't prepare column name
73+
foreach ( $args as $query_arg => $query_value ) {
74+
if ( empty( $query_value ) ) {
75+
continue;
10476
}
105-
}
106-
107-
if ( ! empty( $args['connector'] ) ) {
108-
$where .= $wpdb->prepare( " AND $wpdb->stream.connector = %s", $args['connector'] );
109-
}
110-
111-
if ( ! empty( $args['context'] ) ) {
112-
$where .= $wpdb->prepare( " AND $wpdb->stream.context = %s", $args['context'] );
113-
}
114-
115-
if ( ! empty( $args['action'] ) ) {
116-
$where .= $wpdb->prepare( " AND $wpdb->stream.action = %s", $args['action'] );
117-
}
118-
119-
if ( ! empty( $args['ip'] ) ) {
120-
$where .= $wpdb->prepare( " AND $wpdb->stream.ip = %s", wp_stream_filter_var( $args['ip'], FILTER_VALIDATE_IP ) );
121-
}
122-
123-
/**
124-
* PARSE DATE PARAM FAMILY
125-
*/
126-
if ( ! empty( $args['date'] ) ) {
127-
$args['date_from'] = $args['date'];
128-
$args['date_to'] = $args['date'];
129-
}
130-
131-
if ( ! empty( $args['date_from'] ) ) {
132-
$date = get_gmt_from_date( gmdate( 'Y-m-d H:i:s', strtotime( $args['date_from'] . ' 00:00:00' ) ) );
133-
$where .= $wpdb->prepare( " AND DATE($wpdb->stream.created) >= %s", $date );
134-
}
135-
136-
if ( ! empty( $args['date_to'] ) ) {
137-
$date = get_gmt_from_date( gmdate( 'Y-m-d H:i:s', strtotime( $args['date_to'] . ' 23:59:59' ) ) );
138-
$where .= $wpdb->prepare( " AND DATE($wpdb->stream.created) <= %s", $date );
139-
}
140-
141-
if ( ! empty( $args['date_after'] ) ) {
142-
$date = get_gmt_from_date( gmdate( 'Y-m-d H:i:s', strtotime( $args['date_after'] ) ) );
143-
$where .= $wpdb->prepare( " AND DATE($wpdb->stream.created) > %s", $date );
144-
}
145-
146-
if ( ! empty( $args['date_before'] ) ) {
147-
$date = get_gmt_from_date( gmdate( 'Y-m-d H:i:s', strtotime( $args['date_before'] ) ) );
148-
$where .= $wpdb->prepare( " AND DATE($wpdb->stream.created) < %s", $date );
149-
}
150-
151-
/**
152-
* Parse __in and __not_in queries.
153-
*/
154-
foreach ( $args as $key => $value ) {
155-
$field = $this->key_to_field( $key );
15677

157-
if ( ! empty( $field ) ) {
158-
$values_prepared = implode( ', ', $this->db_prepare_list( $value ) );
159-
160-
if ( $this->key_is_in_lookup( $key ) ) {
161-
$where .= sprintf( " AND $wpdb->stream.%s IN (%s)", $field, $values_prepared );
162-
} elseif ( $this->key_is_in_not_lookup( $key ) ) {
163-
$where .= sprintf( " AND $wpdb->stream.%s NOT IN (%s)", $field, $values_prepared );
164-
}
78+
switch ( $query_arg ) {
79+
// Process core params.
80+
case 'site_id':
81+
case 'blog_id':
82+
case 'object_id':
83+
case 'user_id':
84+
case 'user_role':
85+
case 'connector':
86+
case 'context':
87+
case 'action':
88+
case 'ip':
89+
$where .= $this->and_where( $query_arg, $query_value );
90+
break;
91+
92+
// Process "search*" params.
93+
case 'search':
94+
$field = ! empty( $args['search_field'] ) ? $args['search_field'] : 'summary';
95+
$field = $this->lookup_field_validated( $field );
96+
97+
if ( ! empty( $field ) ) {
98+
$where .= $this->and_where( $field, "%{$query_value}%", 'LIKE' );
99+
}
100+
break;
101+
102+
// Process "date*" params.
103+
case 'date':
104+
$args['date_from'] = $args['date'];
105+
$args['date_to'] = $args['date'];
106+
break;
107+
case 'date_from':
108+
case 'date_to':
109+
case 'date_after':
110+
case 'date_before':
111+
if ( 'date_from' === $query_arg ) {
112+
$time = '00:00:00';
113+
} elseif ( 'date_to' === $query_arg ) {
114+
$time = '23:59:59';
115+
}
116+
117+
$compare = $this->get_date_compare( $query_arg );
118+
119+
$date = isset( $time ) ? strtotime( "{$query_value} {$time}" ) : strtotime( $query_value );
120+
$date = get_gmt_from_date( gmdate( 'Y-m-d H:i:s', $date ) );
121+
$where .= $this->and_where( 'created', $date, $compare, true );
122+
break;
123+
124+
// Process all other valid params except "fields", "order" and "pagination" params.
125+
default:
126+
$field = $this->lookup_field_validated( $query_arg );
127+
128+
if ( ! empty( $field ) && ! empty( $query_value ) ) {
129+
$values_prepared = implode( ', ', $this->db_prepare_list( $query_value ) );
130+
131+
if ( $this->key_is_in_lookup( $query_arg ) ) {
132+
$where .= sprintf( " AND $wpdb->stream.%s IN (%s)", $field, $values_prepared );
133+
} elseif ( $this->key_is_not_in_lookup( $query_arg ) ) {
134+
$where .= sprintf( " AND $wpdb->stream.%s NOT IN (%s)", $field, $values_prepared );
135+
}
136+
}
137+
break;
165138
}
166139
}
167140

168-
/**
169-
* PARSE PAGINATION PARAMS
170-
*/
141+
// Process pagination params.
171142
$limits = '';
172143
$page = absint( $args['paged'] );
173144
$per_page = absint( $args['records_per_page'] );
@@ -177,9 +148,7 @@ public function query( $args ) {
177148
$limits = "LIMIT {$offset}, {$per_page}";
178149
}
179150

180-
/**
181-
* PARSE ORDER PARAMS
182-
*/
151+
// Process order params.
183152
$order = esc_sql( $args['order'] );
184153
$orderby = esc_sql( $args['orderby'] );
185154
$orderable = array( 'ID', 'site_id', 'blog_id', 'object_id', 'user_id', 'user_role', 'summary', 'created', 'connector', 'context', 'action' );
@@ -196,9 +165,7 @@ public function query( $args ) {
196165

197166
$orderby = "ORDER BY {$orderby} {$order}";
198167

199-
/**
200-
* PARSE FIELDS PARAMETER
201-
*/
168+
// Process "fields" parameters.
202169
$fields = (array) $args['fields'];
203170
$selects = array();
204171

@@ -217,9 +184,7 @@ public function query( $args ) {
217184

218185
$select = implode( ', ', $selects );
219186

220-
/**
221-
* BUILD THE FINAL QUERY
222-
*/
187+
// Build the final query.
223188
$query = "SELECT SQL_CALC_FOUND_ROWS {$select}
224189
FROM $wpdb->stream
225190
{$join}
@@ -238,9 +203,8 @@ public function query( $args ) {
238203
$query = apply_filters( 'wp_stream_db_query', $query, $args );
239204

240205
$result = array();
241-
/**
242-
* QUERY THE DATABASE FOR RESULTS
243-
*/
206+
207+
// Execute query and return results.
244208
$result['items'] = $wpdb->get_results( $query ); // @codingStandardsIgnoreLine $query already prepared
245209
$result['count'] = $result['items'] ? absint( $wpdb->get_var( 'SELECT FOUND_ROWS()' ) ) : 0;
246210

@@ -305,7 +269,7 @@ protected function key_to_field( $key ) {
305269
if ( $this->key_is_in_lookup( $key ) || $this->key_is_not_in_lookup( $key ) ) {
306270
$field = str_replace( array( 'record_', '__in', '__not_in' ), '', $key );
307271

308-
$this->lookup_field( $field );
272+
return $field;
309273
}
310274

311275
return null;
@@ -319,10 +283,62 @@ protected function key_to_field( $key ) {
319283
* @return string|null
320284
*/
321285
protected function lookup_field_validated( $field ) {
322-
if ( in_array( $field, $this->lookup_fields, true ) ) {
286+
$field = $this->key_to_field( $field );
287+
if ( ! empty( $field ) && in_array( $field, $this->lookup_fields, true ) ) {
323288
return $field;
324289
}
325290

326291
return null;
327292
}
293+
294+
/**
295+
* Return partial of prepare WHERE statement.
296+
*
297+
* @param string $field Field being evaluated.
298+
* @param string|integer $value Value being compared.
299+
* @param string $compare String representation of how value should be compare (Eg. =, <=, ...).
300+
* @param bool $as_date A type for the value to be cast to.
301+
*
302+
* @return string
303+
*/
304+
protected function and_where( $field, $value, $compare = '=', $as_date = false ) {
305+
if ( empty( $value ) ) {
306+
return '';
307+
}
308+
309+
$field = "{$this->db->stream}.{$field}";
310+
if ( $as_date ) {
311+
$field = "DATE({$field})";
312+
}
313+
314+
if ( is_numeric( $value ) ) {
315+
$placeholder = '%d';
316+
} else {
317+
$placeholder = '%s';
318+
}
319+
320+
return $this->db->prepare( " AND {$field} {$compare} {$placeholder}", $value );
321+
}
322+
323+
/**
324+
* Return the proper compare operator for the date comparing type provided.
325+
*
326+
* @param string $date_type Date type.
327+
*
328+
* @return string|null
329+
*/
330+
protected function get_date_compare( $date_type ) {
331+
switch ( $date_type ) {
332+
case 'date_from':
333+
return '>=';
334+
case 'date_to':
335+
return '<=';
336+
case 'date_after':
337+
return '>';
338+
case 'date_before':
339+
return '<';
340+
}
341+
342+
return null;
343+
}
328344
}

Diff for: tests/tests/test-class-query.php

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
/**
3+
* Tests Query class functions.
4+
*/
5+
6+
namespace WP_Stream;
7+
8+
class Test_Query extends WP_StreamTestCase {
9+
/**
10+
* Runs before each test.
11+
*/
12+
public function setUp() {
13+
parent::setUp();
14+
15+
$this->user_id = self::factory()->user->create();
16+
wp_set_current_user( $this->user_id );
17+
}
18+
19+
public function test_query() {
20+
// Create log.
21+
$result = $this->plugin->log->log(
22+
'test_connector',
23+
'Test query',
24+
array(),
25+
0,
26+
'settings',
27+
'test',
28+
$this->user_id
29+
);
30+
31+
$this->assertNotEmpty( $result );
32+
33+
// Test some basic parameters.
34+
$records = $this->plugin->db->query( array( 'connector' => 'test_connector' ) );
35+
$this->assertEquals( $result, $records[0]->ID );
36+
37+
$records = $this->plugin->db->query( array( 'context' => 'settings' ) );
38+
$this->assertEquals( $result, $records[0]->ID );
39+
40+
// Test date parameters.
41+
$records = $this->plugin->db->query( array( 'date' => get_the_date( 'Y-m-d') ) );
42+
$this->assertEquals( $result, $records[0]->ID );
43+
44+
$records = $this->plugin->db->query( array( 'date_before' => current_time( 'mysql' ) ) );
45+
$this->assertEquals( $result, $records[0]->ID );
46+
47+
// Test __not_in parameters.
48+
$records = $this->plugin->db->query( array( 'ID__not_in' => $result ) );
49+
$ids = array_map(
50+
function ( $record ) {
51+
return $record->ID;
52+
},
53+
$records
54+
);
55+
$this->assertFalse( in_array( $result, $ids, true ) );
56+
57+
// Test __in parameters.
58+
$records = $this->plugin->db->query( array( 'ID__in' => $result ) );
59+
$this->assertEquals( $result, $records[0]->ID );
60+
61+
// Test order parameters.
62+
$result2 = $this->plugin->log->log(
63+
'test_connector',
64+
'Test query two',
65+
array(),
66+
0,
67+
'settings',
68+
'test',
69+
$this->user_id
70+
);
71+
72+
$records = $this->plugin->db->query(
73+
array(
74+
'ID__in' => array( $result, $result2 ),
75+
'order' => 'DESC',
76+
'orderby' => 'created',
77+
)
78+
);
79+
$ids = array_map(
80+
function ( $record ) {
81+
return $record->ID;
82+
},
83+
$records
84+
);
85+
$this->assertEquals( $ids, array( $result, $result2 ) );
86+
}
87+
}

0 commit comments

Comments
 (0)