PluginProbe
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More / 2.2.0
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More v2.2.0
2.3.0 2.2.0 2.1.1 2.1.0 2.0.0 1.10.0 1.9.1 1.9.0 1.2.1 1.2.2 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 All 59 releases
storeengine / includes / models / product.php

product.php in StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More 2.2.0, at includes/models/product.php

410 lines 10.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace StoreEngine\Models;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 // @TODO: is this class needed? check post type.
10
11 use StoreEngine\Classes\AbstractModel;
12 use StoreEngine\Models\Price as PriceModel;
13 use StoreEngine\Utils\Helper;
14 use WP_Query;
15
16 /**
17 * @deprecated - Use `ProductFactory` to retrieve product data.
18 *
19 * @see ProductFactory
20 * @see \StoreEngine\Classes\AbstractProduct
21 */
22 class Product extends AbstractModel {
23 public string $table = 'posts';
24 public string $primary_key = 'ID';
25
26 public function save( array $args = [] ) {
27 wp_parse_args( $args, [
28 'post_title' => '',
29 'post_content' => '',
30 'post_excerpt' => '',
31 'post_status' => 'publish',
32 'post_type' => 'product',
33 'comment_status' => 'closed',
34 'ping_status' => 'closed',
35 'post_name' => '',
36 'created_at' => current_time( 'mysql', 1 ),
37 'updated_at' => current_time( 'mysql', 1 ),
38 ] );
39
40 return wp_insert_post( $args );
41 }
42
43 public function update( int $id, array $args ): bool {
44 wp_parse_args( $args, [
45 'ID' => $id,
46 'post_title' => '',
47 'post_content' => '',
48 'post_excerpt' => '',
49 'post_status' => 'publish',
50 'post_type' => 'product',
51 'comment_status' => 'closed',
52 'ping_status' => 'closed',
53 'post_name' => '',
54 'created_at' => current_time( 'mysql', 1 ),
55 'updated_at' => current_time( 'mysql', 1 ),
56 ] );
57
58 return wp_update_post( $args );
59 }
60
61 public function delete( ?int $id = null ): bool {
62 if ( ! $id ) {
63 return false;
64 }
65
66 return wp_delete_post( $id, true );
67 }
68
69 public function get_product_meta( $id ) {
70 // return key value instead array
71 return array_map( fn( $meta ) => $meta[0], get_post_meta( absint( $id ) ) );
72 }
73
74 public function get_products() {
75 $args = [
76 'post_type' => 'product',
77 'post_status' => 'publish',
78 'posts_per_page' => -1,
79 'orderby' => 'date',
80 'order' => 'DESC',
81 ];
82 $query = new WP_Query( $args );
83
84 return $query->posts;
85 }
86
87 public function get_product_by_slug( $slug ): array {
88 $args = [
89 'name' => $slug,
90 'post_type' => 'product',
91 'post_status' => 'publish',
92 'posts_per_page' => 1,
93 ];
94 $query = new WP_Query( $args );
95
96 return $query->posts;
97 }
98
99 public function get_products_by_category( $category ): array {
100 $args = [
101 'post_type' => 'product',
102 'post_status' => 'publish',
103 'posts_per_page' => -1,
104 'orderby' => 'date',
105 'order' => 'DESC',
106 'tax_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
107 [
108 'taxonomy' => 'product_cat',
109 'field' => 'slug',
110 'terms' => $category,
111 ],
112 ],
113 ];
114 $query = new WP_Query( $args );
115
116 return $query->posts;
117 }
118
119 public function get_products_by_tag( $tag ) {
120 // TODO: Implement
121 }
122
123
124 public function get_products_by_date_range( $date ) {
125 // TODO: Implement
126 }
127
128 public function get_products_by_status( $status ) {
129 // TODO: Implement
130 }
131
132 /**
133 * @deprecated
134 */
135 public function is_subscription( $price_id ): bool {
136 $price = $this->get_price_data_by_id( $price_id );
137 if ( isset( $price->price_type ) && 'subscription' === $price->price_type ) {
138 return true;
139 }
140
141 return false;
142 }
143
144 /**
145 * @deprecated
146 */
147 public function duration_frequency( $price_id ): array {
148 if ( $this->is_subscription( $price_id ) ) {
149 $price = $this->get_price_data_by_id( $price_id );
150 if ( isset( $price->settings['payment_duration'] ) && isset( $price->settings['payment_duration_type'] ) ) {
151 return [
152 'interval_type' => $price->settings['payment_duration_type'],
153 'interval' => $price->settings['payment_duration'],
154 ];
155 }
156 }
157
158 return [];
159 }
160
161 public function get_subscription_product_interval_and_count( $product_id ) {
162 if ( $this->is_subscription( $product_id ) ) {
163 return [
164 'interval' => get_post_meta( $product_id, '_storeengine_product_repeated_payment_duration_type', true ),
165 'interval_count' => get_post_meta( $product_id, '_storeengine_product_repeated_payment_duration', true ),
166 ];
167 }
168 }
169
170 public function get_product_prices( $product_id ): array {
171 return ( new PriceModel() )->get_prices_by_product_id( (int) $product_id );
172 }
173
174 public function get_product_price_details( $product_id, $meta_id ): array {
175 global $wpdb;
176 $price = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}postmeta WHERE post_id = %d AND meta_id = %d;", $product_id, $meta_id ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
177
178 if ( $price ) {
179 return array_merge( array( 'price_id' => $price->meta_id ), maybe_unserialize( $price->meta_value ) );
180 }
181
182 return [];
183 }
184
185 /**
186 * @deprecated
187 */
188 public function get_price_by_id( $price_id ) {
189 $price = $this->get_price_data_by_id( $price_id );
190 if ( $price ) {
191 return (float) $price->price ?? 0.0;
192 }
193
194 return null;
195 }
196
197 /**
198 * @param int|string $price_id
199 *
200 * @return ?object
201 * @deprecated
202 */
203 public function get_price_data_by_id( $price_id ): ?object {
204 return ( new PriceModel() )->get( absint( $price_id ) );
205 }
206
207
208 /**
209 * @deprecated
210 */
211 public function get_compare_price( $price_id ) {
212 $price = $this->get_price_data_by_id( $price_id );
213 if ( $price->compare_price ) {
214 return (float) $price->compare_price;
215 }
216
217 return null;
218 }
219
220 /**
221 * @deprecated
222 */
223 public function get_price_name( $price_id ) {
224 $price = $this->get_price_data_by_id( $price_id );
225 if ( $price->price_name ) {
226 return $price->price_name;
227 }
228
229 return null;
230 }
231
232 /**
233 * @deprecated
234 */
235 public function get_price_duration( $price_id ): string {
236 $price = $this->get_price_data_by_id( $price_id );
237 $payment_duration = $price->settings['payment_duration'];
238
239 // @TODO use array of duration types & i18n properly.
240 if ( 1 === $payment_duration ) {
241 return Helper::currency_format( $price->price ) . ' / Every ' . ucfirst( $price->settings['payment_duration_type'] );
242 }
243
244 // @FIXME use _n for plural translation.
245 return Helper::currency_format( $price->price ) . ' / ' . $payment_duration . '-' . $price->settings['payment_duration_type'] . 's';
246 }
247
248 /**
249 * @deprecated
250 */
251 public function next_payment_date( $price_id ) {
252 $price = $this->get_price_data_by_id( $price_id );
253 if ( $price ) {
254 $interval = $this->duration_frequency( $price_id );
255 if ( $interval ) {
256 return strtotime( '+' . $interval['interval'] . ' ' . $interval['interval_type'] );
257 }
258 }
259
260 return null;
261 }
262
263 public function is_all_product_product_autocomplete( $purchase_items ): bool {
264 $is_auto_complete = false;
265 foreach ( $purchase_items as $item ) {
266 $product_id = $item->product_id;
267 $product = $this->get_product_meta( $product_id );
268 if ( $product['_storeengine_product_digital_auto_complete'] && 'digital' === $product['_storeengine_product_shipping_type'] ) {
269 $is_auto_complete = true;
270 } else {
271 $is_auto_complete = false;
272 break;
273 }
274 }
275
276 return $is_auto_complete;
277 }
278
279 /**
280 * @deprecated
281 */
282 public function get_setup_fee( $price_id ) {
283 $price = $this->get_price_data_by_id( $price_id );
284 if ( $price->settings['setup_fee_price'] ) {
285 return (float) $price->settings['setup_fee_price'];
286 }
287
288 return null;
289 }
290
291 /**
292 * @deprecated
293 */
294 public function get_trial_days( $price_id ) {
295 $price = $this->get_price_data_by_id( $price_id );
296 if ( $price->settings['trial_days'] ) {
297 return (int) $price->settings['trial_days'];
298 }
299
300 return null;
301 }
302
303 /**
304 * @deprecated
305 */
306 public function trial_end_date( $price_id ) {
307 $price = $this->get_price_data_by_id( $price_id );
308 if ( $price->settings['trial_days'] ) {
309 return strtotime( '+' . $price->settings['trial_days'] . ' days' );
310 }
311
312 return null;
313 }
314
315 /**
316 * @deprecated
317 */
318 public function get_price_expiry_date( $price_id ) {
319 $price = $this->get_price_data_by_id( $price_id );
320 if ( $price ) {
321 if ( $price->settings['expire'] ) {
322 return wp_date( 'Y-m-d H:i:s', strtotime( '+' . $price->settings['expire_days'] . 'days' ) );
323 }
324 }
325
326 return null;
327 }
328
329 public static function get_product_rating( $product_id ) {
330 global $wpdb;
331
332 $ratings = array(
333 'rating_count' => 0,
334 'rating_sum' => 0,
335 'rating_avg' => 0.00,
336 'count_by_value' => array(
337 5 => 0,
338 4 => 0,
339 3 => 0,
340 2 => 0,
341 1 => 0,
342 ),
343 );
344
345 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
346 $rating = $wpdb->get_row(
347 $wpdb->prepare(
348 "SELECT
349 COUNT(meta_value) AS rating_count,
350 SUM(meta_value) AS rating_sum
351 FROM {$wpdb->comments} c
352 INNER JOIN {$wpdb->commentmeta} cm ON c.comment_ID = cm.comment_id
353 WHERE
354 c.comment_post_ID = %d
355 AND c.comment_approved = '1'
356 AND c.comment_type = 'storeengine_product'
357 AND meta_key = 'storeengine_rating';
358 ",
359 $product_id
360 )
361 );
362 // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
363
364 if ( $rating->rating_count ) {
365 $avg_rating = number_format( ( $rating->rating_sum / $rating->rating_count ), 1 );
366
367 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
368 $stars = $wpdb->get_results(
369 $wpdb->prepare(
370 "SELECT
371 cm.meta_value AS rating,
372 COUNT(cm.meta_value) as rating_count
373 FROM {$wpdb->comments} c
374 INNER JOIN {$wpdb->commentmeta} cm ON c.comment_ID = cm.comment_id
375 WHERE
376 c.comment_post_ID = %d
377 AND c.comment_approved = '1'
378 AND c.comment_type = 'storeengine_product'
379 AND cm.meta_key = 'storeengine_rating'
380 GROUP BY cm.meta_value;
381 ",
382 $product_id
383 )
384 );
385 // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
386
387 $ratings = array(
388 5 => 0,
389 4 => 0,
390 3 => 0,
391 2 => 0,
392 1 => 0,
393 );
394 foreach ( $stars as $star ) {
395 $index = (int) $star->rating;
396 array_key_exists( $index, $ratings ) ? $ratings[ $index ] = $star->rating_count : 0;
397 }
398
399 $ratings = array(
400 'rating_count' => $rating->rating_count,
401 'rating_sum' => $rating->rating_sum,
402 'rating_avg' => $avg_rating,
403 'count_by_value' => $ratings,
404 );
405 }//end if
406
407 return (object) $ratings;
408 }
409 }
410