PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.27
Booktics – Appointment Booking Calendar for Service Businesses v1.0.27
1.0.29 1.0.28 1.0.27 1.0.26 1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.2 1.0.3 All 31 releases
← All changes | base/query-builders/query-builder.php +348 -346 1.0.1 → 1.0.27 View file →
@@ -1,346 +1,348 @@
1 -<?php
2 -/**
3 - * Query_Bulder Class
4 - *
5 - * @package Booktics/Query_Builders
6 - */
7 -
8 -namespace Booktics\Query_Builders;
9 -
10 -use Booktics\Abstracts\Post_Model;
11 -use WP_Query;
12 -
13 -class Query_Builder {
14 - /**
15 - * Store post model object
16 - *
17 - * @var object
18 - */
19 - protected object $model;
20 -
21 - /**
22 - * Store meta query
23 - *
24 - * @var array<int,array<string,mixed>>
25 - */
26 - protected array $meta_query = array();
27 -
28 - /**
29 - * Store or meta query data
30 - *
31 - * @var array<int,array<string,mixed>>
32 - */
33 - protected array $meta_query_or = array();
34 -
35 - /**
36 - * Store tax query
37 - *
38 - * @var array<int,array<string,mixed>>
39 - */
40 - protected array $tax_query = array();
41 -
42 - /**
43 - * Store all args
44 - *
45 - * @var array<string,mixed>
46 - */
47 - protected array $args = array();
48 -
49 - /**
50 - * Constructor
51 - *
52 - * @param Object $model The model class name
53 - */
54 - public function __construct( $model ) {
55 - $this->model = $model;
56 - $this->args['post_type'] = $model->get_post_type();
57 - $this->args['post_status'] = Post_Model::$accepted_statuses;
58 - }
59 -
60 - /**
61 - * Add a meta query condition (AND)
62 - *
63 - * @param string|array $key Meta key or array of conditions
64 - * @param string $compare Comparison operator
65 - * @param mixed $value Meta value
66 - *
67 - * @return $this
68 - */
69 - public function where( $key, $value = null, $compare = '=' ) {
70 - if ( is_array( $key ) ) {
71 - foreach ( $key as $condition ) {
72 - $this->where( ...$condition );
73 - }
74 -
75 - return $this;
76 - }
77 -
78 - $this->meta_query[] = compact( 'key', 'value', 'compare' );
79 -
80 - return $this;
81 - }
82 -
83 - /**
84 - * Add an OR meta query condition
85 - *
86 - * @param string $key
87 - * @param string $compare
88 - * @param mixed $value
89 - *
90 - * @return $this
91 - */
92 - public function or_where( $key, $value = null, $compare = '=' ) {
93 - $this->meta_query_or[] = compact( 'key', 'value', 'compare' );
94 -
95 - return $this;
96 - }
97 -
98 - /**
99 - * Add a WHERE IN meta query condition
100 - *
101 - * @param string $key
102 - * @param array $values
103 - *
104 - * @return $this
105 - */
106 - public function where_in( $key, array $values ) {
107 - $this->meta_query[] = array(
108 - 'key' => $key,
109 - 'value' => $values,
110 - 'compare' => 'IN',
111 - );
112 -
113 - return $this;
114 - }
115 -
116 - /**
117 - * Exclude posts by ID from query results
118 - *
119 - * @param int|array $ids Post ID or array of post IDs to exclude
120 - *
121 - * @return $this
122 - */
123 - public function where_not_ids( $ids ) {
124 - $this->args['post__not_in'] = (array) $ids;
125 -
126 - return $this;
127 - }
128 -
129 - /**
130 - * Add a meta query condition for NULL value
131 - *
132 - * @param string $key
133 - *
134 - * @return $this
135 - */
136 - public function where_null( $key ) {
137 - $this->meta_query[] = array(
138 - 'key' => $key,
139 - 'compare' => 'NOT EXISTS',
140 - );
141 -
142 - return $this;
143 - }
144 -
145 - /**
146 - * Add a taxonomy query (generic)
147 - *
148 - * @param string $taxonomy
149 - * @param string|array $terms
150 - * @param string $field
151 - *
152 - * @return $this
153 - */
154 - public function taxonomy( $taxonomy, $terms, $field = 'slug' ) {
155 - $this->tax_query[] = array(
156 - 'taxonomy' => $taxonomy,
157 - 'field' => $field,
158 - 'terms' => (array) $terms,
159 - );
160 -
161 - return $this;
162 - }
163 -
164 - /**
165 - * Add a category query
166 - *
167 - * @param string|int $slugOrId
168 - *
169 - * @return $this
170 - */
171 - public function category( $slugOrId ) {
172 - return $this->taxonomy( 'category', $slugOrId, is_numeric( $slugOrId ) ? 'term_id' : 'slug' );
173 - }
174 -
175 - /**
176 - * Add a tag query
177 - *
178 - * @param string|int $slugOrId
179 - *
180 - * @return $this
181 - */
182 - public function tag( $slugOrId ) {
183 - return $this->taxonomy( 'post_tag', $slugOrId, is_numeric( $slugOrId ) ? 'term_id' : 'slug' );
184 - }
185 -
186 - /**
187 - * Set order by custom field
188 - *
189 - * @param string $field
190 - * @param string $direction
191 - *
192 - * @return $this
193 - */
194 - public function orderBy( $field, $direction = 'ASC' ) {
195 - $this->args['orderby'] = 'meta_value';
196 - $this->args['meta_key'] = $field;
197 - $this->args['order'] = $direction;
198 -
199 - return $this;
200 - }
201 -
202 - /**
203 - * Execute query and return all matched model instances
204 - *
205 - * @return array
206 - */
207 - public function get() {
208 - $this->args['numberposts'] = - 1;
209 -
210 - if ( $meta = $this->build_meta_query() ) {
211 - $this->args['meta_query'] = $meta;
212 - }
213 -
214 - if ( $this->tax_query ) {
215 - $this->args['tax_query'] = count( $this->tax_query ) > 1
216 - ? array_merge( array( 'relation' => 'AND' ), $this->tax_query )
217 - : $this->tax_query;
218 - }
219 -
220 - $posts = get_posts( $this->args );
221 -
222 - return array_map(
223 - function ( $post ) {
224 - return new $this->model( $post );
225 - }, $posts
226 - );
227 - }
228 -
229 - /**
230 - * Paginate the query results
231 - *
232 - * @param int $per_page
233 - * @param int $page
234 - *
235 - * @return array
236 - */
237 - public function paginate( $per_page = 10, $page = 1 ) {
238 - $this->args['posts_per_page'] = $per_page;
239 - $this->args['paged'] = $page;
240 -
241 - if ( $meta = $this->build_meta_query() ) {
242 - $this->args['meta_query'] = $meta;
243 - }
244 -
245 - if ( $this->tax_query ) {
246 - $this->args['tax_query'] = count( $this->tax_query ) > 1
247 - ? array_merge( array( 'relation' => 'AND' ), $this->tax_query )
248 - : $this->tax_query;
249 - }
250 -
251 - $query = new WP_Query( $this->args );
252 -
253 - return array(
254 - 'items' => array_map(
255 - function ( $p ) {
256 - return new $this->model( $p );
257 - }, $query->posts
258 - ),
259 - 'total' => $query->found_posts,
260 - 'per_page' => $per_page,
261 - 'current_page' => $page,
262 - 'last_page' => ceil( $query->found_posts / $per_page ),
263 - );
264 - }
265 -
266 - /**
267 - * Return first matched model or null
268 - *
269 - * @return null|PostModel
270 - */
271 - public function first() {
272 - $this->args['numberposts'] = 1;
273 -
274 - if ( $meta = $this->build_meta_query() ) {
275 - $this->args['meta_query'] = $meta;
276 - }
277 -
278 - if ( $this->tax_query ) {
279 - $this->args['tax_query'] = $this->tax_query;
280 - }
281 -
282 - $posts = get_posts( $this->args );
283 -
284 - return $posts ? new $this->model( $posts[0] ) : null;
285 - }
286 -
287 - /**
288 - * Count number of matched posts
289 - *
290 - * @return int
291 - */
292 - public function count() {
293 - $this->args['posts_per_page'] = 1;
294 - $this->args['fields'] = 'ids';
295 -
296 - if ( $meta = $this->build_meta_query() ) {
297 - $this->args['meta_query'] = $meta;
298 - }
299 -
300 - if ( $this->tax_query ) {
301 - $this->args['tax_query'] = $this->tax_query;
302 - }
303 -
304 - $query = new WP_Query( $this->args );
305 -
306 - return $query->found_posts;
307 - }
308 -
309 - /**
310 - * Check if any record exists
311 - *
312 - * @return bool
313 - */
314 - public function exists() {
315 - return $this->count() > 0;
316 - }
317 -
318 - /**
319 - * Build meta_query with AND/OR relations
320 - *
321 - * @return array
322 - */
323 - protected function build_meta_query() {
324 - $meta = array();
325 -
326 - if ( $this->meta_query ) {
327 - $meta[] = count( $this->meta_query ) > 0
328 - ? array_merge( array( 'relation' => 'AND' ), $this->meta_query )
329 - : $this->meta_query[0];
330 - }
331 -
332 - if ( $this->meta_query_or ) {
333 - $meta[] = count( $this->meta_query_or ) > 0
334 - ? array_merge( array( 'relation' => 'OR' ), $this->meta_query_or )
335 - : $this->meta_query_or[0];
336 - }
337 -
338 - if ( count( $meta ) === 1 ) {
339 - return $meta[0];
340 - } elseif ( count( $meta ) > 0 ) {
341 - return array_merge( array( 'relation' => 'AND' ), $meta );
342 - }
343 -
344 - return array();
345 - }
346 -}
1 +<?php
2 +/**
3 + * Query_Bulder Class
4 + *
5 + * @package Booktics/Query_Builders
6 + */
7 +
8 +namespace Booktics\Query_Builders;
9 +
10 +use Booktics\Abstracts\Post_Model;
11 +use WP_Query;
12 +
13 +class Query_Builder {
14 + /**
15 + * Store post model object
16 + *
17 + * @var object
18 + */
19 + protected object $model;
20 +
21 + /**
22 + * Store meta query
23 + *
24 + * @var array<int,array<string,mixed>>
25 + */
26 + protected array $meta_query = array();
27 +
28 + /**
29 + * Store or meta query data
30 + *
31 + * @var array<int,array<string,mixed>>
32 + */
33 + protected array $meta_query_or = array();
34 +
35 + /**
36 + * Store tax query
37 + *
38 + * @var array<int,array<string,mixed>>
39 + */
40 + protected array $tax_query = array();
41 +
42 + /**
43 + * Store all args
44 + *
45 + * @var array<string,mixed>
46 + */
47 + protected array $args = array();
48 +
49 + /**
50 + * Constructor
51 + *
52 + * @param Object $model The model class name
53 + */
54 + public function __construct( $model ) {
55 + $this->model = $model;
56 + $this->args['post_type'] = $model->get_post_type();
57 + $this->args['post_status'] = Post_Model::$accepted_statuses;
58 + }
59 +
60 + /**
61 + * Add a meta query condition (AND)
62 + *
63 + * @param string|array $key Meta key or array of conditions
64 + * @param string $compare Comparison operator
65 + * @param mixed $value Meta value
66 + *
67 + * @return $this
68 + */
69 + public function where( $key, $value = null, $compare = '=' ) {
70 + if ( is_array( $key ) ) {
71 + foreach ( $key as $condition ) {
72 + $this->where( ...$condition );
73 + }
74 +
75 + return $this;
76 + }
77 +
78 + $this->meta_query[] = compact( 'key', 'value', 'compare' );
79 +
80 + return $this;
81 + }
82 +
83 + /**
84 + * Add an OR meta query condition
85 + *
86 + * @param string $key
87 + * @param string $compare
88 + * @param mixed $value
89 + *
90 + * @return $this
91 + */
92 + public function or_where( $key, $value = null, $compare = '=' ) {
93 + $this->meta_query_or[] = compact( 'key', 'value', 'compare' );
94 +
95 + return $this;
96 + }
97 +
98 + /**
99 + * Add a WHERE IN meta query condition
100 + *
101 + * @param string $key
102 + * @param array $values
103 + *
104 + * @return $this
105 + */
106 + public function where_in( $key, array $values ) {
107 + $this->meta_query[] = array(
108 + 'key' => $key,
109 + 'value' => $values,
110 + 'compare' => 'IN',
111 + );
112 +
113 + return $this;
114 + }
115 +
116 + /**
117 + * Exclude posts by ID from query results
118 + *
119 + * @param int|array $ids Post ID or array of post IDs to exclude
120 + *
121 + * @return $this
122 + */
123 + public function where_not_ids( $ids ) {
124 + $this->args['post__not_in'] = (array) $ids; // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Intentional exclusion, dataset is controlled and limited
125 +
126 + return $this;
127 + }
128 +
129 + /**
130 + * Add a meta query condition for NULL value
131 + *
132 + * @param string $key
133 + *
134 + * @return $this
135 + */
136 + public function where_null( $key ) {
137 + $this->meta_query[] = array(
138 + 'key' => $key,
139 + 'compare' => 'NOT EXISTS',
140 + );
141 +
142 + return $this;
143 + }
144 +
145 + /**
146 + * Add a taxonomy query (generic)
147 + *
148 + * @param string $taxonomy
149 + * @param string|array $terms
150 + * @param string $field
151 + *
152 + * @return $this
153 + */
154 + public function taxonomy( $taxonomy, $terms, $field = 'slug' ) {
155 + $this->tax_query[] = array(
156 + 'taxonomy' => $taxonomy,
157 + 'field' => $field,
158 + 'terms' => (array) $terms,
159 + );
160 +
161 + return $this;
162 + }
163 +
164 + /**
165 + * Add a category query
166 + *
167 + * @param string|int $slug_or_id
168 + *
169 + * @return $this
170 + */
171 + public function category( $slug_or_id ) {
172 + return $this->taxonomy( 'category', $slug_or_id, is_numeric( $slug_or_id ) ? 'term_id' : 'slug' );
173 + }
174 +
175 + /**
176 + * Add a tag query
177 + *
178 + * @param string|int $slug_or_id
179 + *
180 + * @return $this
181 + */
182 + public function tag( $slug_or_id ) {
183 + return $this->taxonomy( 'post_tag', $slug_or_id, is_numeric( $slug_or_id ) ? 'term_id' : 'slug' );
184 + }
185 +
186 + /**
187 + * Set order by custom field
188 + *
189 + * @param string $field
190 + * @param string $direction
191 + *
192 + * @return $this
193 + */
194 + public function orderBy( $field, $direction = 'ASC' ) {
195 + $this->args['orderby'] = 'meta_value';
196 + $this->args['meta_key'] = $field; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Required for ordering by custom field
197 + $this->args['order'] = $direction;
198 +
199 + return $this;
200 + }
201 +
202 + /**
203 + * Execute query and return all matched model instances
204 + *
205 + * @return array
206 + */
207 + public function get() {
208 + $this->args['numberposts'] = - 1;
209 +
210 + if ( $meta = $this->build_meta_query() ) {
211 + $this->args['meta_query'] = $meta; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query is necessary for filtering posts by custom fields
212 + }
213 +
214 + if ( $this->tax_query ) {
215 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Required for filtering posts by taxonomy
216 + $this->args['tax_query'] = count( $this->tax_query ) > 1
217 + ? array_merge( array( 'relation' => 'AND' ), $this->tax_query )
218 + : $this->tax_query;
219 + }
220 +
221 + $posts = get_posts( $this->args );
222 +
223 + return array_map(
224 + function ( $post ) {
225 + return new $this->model( $post );
226 + }, $posts
227 + );
228 + }
229 +
230 + /**
231 + * Paginate the query results
232 + *
233 + * @param int $per_page
234 + * @param int $page
235 + *
236 + * @return array
237 + */
238 + public function paginate( $per_page = 10, $page = 1 ) {
239 + $this->args['posts_per_page'] = $per_page;
240 + $this->args['paged'] = $page;
241 +
242 + if ( $meta = $this->build_meta_query() ) {
243 + $this->args['meta_query'] = $meta; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query is necessary for filtering posts by custom fields
244 + }
245 +
246 + if ( $this->tax_query ) {
247 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Required for filtering posts by taxonomy
248 + $this->args['tax_query'] = count( $this->tax_query ) > 1
249 + ? array_merge( array( 'relation' => 'AND' ), $this->tax_query )
250 + : $this->tax_query;
251 + }
252 +
253 + $query = new WP_Query( $this->args );
254 +
255 + return array(
256 + 'items' => array_map(
257 + function ( $p ) {
258 + return new $this->model( $p );
259 + }, $query->posts
260 + ),
261 + 'total' => $query->found_posts,
262 + 'per_page' => $per_page,
263 + 'current_page' => $page,
264 + 'last_page' => ceil( $query->found_posts / $per_page ),
265 + );
266 + }
267 +
268 + /**
269 + * Return first matched model or null
270 + *
271 + * @return null|PostModel
272 + */
273 + public function first() {
274 + $this->args['numberposts'] = 1;
275 +
276 + if ( $meta = $this->build_meta_query() ) {
277 + $this->args['meta_query'] = $meta; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query is necessary for filtering posts by custom fields
278 + }
279 +
280 + if ( $this->tax_query ) {
281 + $this->args['tax_query'] = $this->tax_query; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Required for filtering posts by taxonomy
282 + }
283 +
284 + $posts = get_posts( $this->args );
285 +
286 + return $posts ? new $this->model( $posts[0] ) : null;
287 + }
288 +
289 + /**
290 + * Count number of matched posts
291 + *
292 + * @return int
293 + */
294 + public function count() {
295 + $this->args['posts_per_page'] = 1;
296 + $this->args['fields'] = 'ids';
297 +
298 + if ( $meta = $this->build_meta_query() ) {
299 + $this->args['meta_query'] = $meta; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query is necessary for filtering posts by custom fields
300 + }
301 +
302 + if ( $this->tax_query ) {
303 + $this->args['tax_query'] = $this->tax_query; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Required for filtering posts by taxonomy
304 + }
305 +
306 + $query = new WP_Query( $this->args );
307 +
308 + return $query->found_posts;
309 + }
310 +
311 + /**
312 + * Check if any record exists
313 + *
314 + * @return bool
315 + */
316 + public function exists() {
317 + return $this->count() > 0;
318 + }
319 +
320 + /**
321 + * Build meta_query with AND/OR relations
322 + *
323 + * @return array
324 + */
325 + protected function build_meta_query() {
326 + $meta = array();
327 +
328 + if ( $this->meta_query ) {
329 + $meta[] = count( $this->meta_query ) > 0
330 + ? array_merge( array( 'relation' => 'AND' ), $this->meta_query )
331 + : $this->meta_query[0];
332 + }
333 +
334 + if ( $this->meta_query_or ) {
335 + $meta[] = count( $this->meta_query_or ) > 0
336 + ? array_merge( array( 'relation' => 'OR' ), $this->meta_query_or )
337 + : $this->meta_query_or[0];
338 + }
339 +
340 + if ( count( $meta ) === 1 ) {
341 + return $meta[0];
342 + } elseif ( count( $meta ) > 0 ) {
343 + return array_merge( array( 'relation' => 'AND' ), $meta );
344 + }
345 +
346 + return array();
347 + }
348 +}