PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.19
Booktics – Appointment Booking Calendar for Service Businesses v1.0.19
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 1.0.4 1.0.5 1.0.6 1.0.7 All 27 releases
booktics / base / query-builders / query-builder.php

query-builder.php in Booktics – Appointment Booking Calendar for Service Businesses 1.0.19, at base/query-builders/query-builder.php

349 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 }
349