/wp-content/plugins/google-listings-and-ads/src/Product/ProductRepository.php

https://gitlab.com/remyvianne/krowkaramel · PHP · 354 lines · 160 code · 42 blank · 152 comment · 7 complexity · d6aebbf75ce7ae7aff49b1a3a412a0b9 MD5 · raw file

  1. <?php
  2. declare( strict_types=1 );
  3. namespace Automattic\WooCommerce\GoogleListingsAndAds\Product;
  4. use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Service;
  5. use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper;
  6. use Automattic\WooCommerce\GoogleListingsAndAds\Value\ChannelVisibility;
  7. use Automattic\WooCommerce\GoogleListingsAndAds\Value\MCStatus;
  8. use WC_Product;
  9. defined( 'ABSPATH' ) || exit;
  10. /**
  11. * Class ProductRepository
  12. *
  13. * Contains methods to find and retrieve products from database.
  14. *
  15. * @package Automattic\WooCommerce\GoogleListingsAndAds\Product
  16. */
  17. class ProductRepository implements Service {
  18. use PluginHelper;
  19. /**
  20. * @var ProductMetaHandler
  21. */
  22. protected $meta_handler;
  23. /**
  24. * @var ProductFilter
  25. */
  26. protected $product_filter;
  27. /**
  28. * ProductRepository constructor.
  29. *
  30. * @param ProductMetaHandler $meta_handler
  31. * @param ProductFilter $product_filter
  32. */
  33. public function __construct( ProductMetaHandler $meta_handler, ProductFilter $product_filter ) {
  34. $this->meta_handler = $meta_handler;
  35. $this->product_filter = $product_filter;
  36. }
  37. /**
  38. * Find and return an array of WooCommerce product objects based on the provided arguments.
  39. *
  40. * @param array $args Array of WooCommerce args (except 'return'), and product metadata.
  41. * @param int $limit Maximum number of results to retrieve or -1 for unlimited.
  42. * @param int $offset Amount to offset product results.
  43. *
  44. * @see execute_woocommerce_query For more information about the arguments.
  45. *
  46. * @return WC_Product[] Array of WooCommerce product objects
  47. */
  48. public function find( array $args = [], int $limit = -1, int $offset = 0 ): array {
  49. $args['return'] = 'objects';
  50. return $this->execute_woocommerce_query( $args, $limit, $offset );
  51. }
  52. /**
  53. * Find and return an array of WooCommerce product IDs based on the provided arguments.
  54. *
  55. * @param array $args Array of WooCommerce args (except 'return'), and product metadata.
  56. * @param int $limit Maximum number of results to retrieve or -1 for unlimited.
  57. * @param int $offset Amount to offset product results.
  58. *
  59. * @see execute_woocommerce_query For more information about the arguments.
  60. *
  61. * @return int[] Array of WooCommerce product IDs
  62. */
  63. public function find_ids( array $args = [], int $limit = -1, int $offset = 0 ): array {
  64. $args['return'] = 'ids';
  65. return $this->execute_woocommerce_query( $args, $limit, $offset );
  66. }
  67. /**
  68. * Find and return an array of WooCommerce product objects based on the provided product IDs.
  69. *
  70. * @param int[] $ids Array of WooCommerce product IDs
  71. * @param int $limit Maximum number of results to retrieve or -1 for unlimited.
  72. * @param int $offset Amount to offset product results.
  73. *
  74. * @return WC_Product[] Array of WooCommerce product objects
  75. */
  76. public function find_by_ids( array $ids, int $limit = -1, int $offset = 0 ): array {
  77. $args['include'] = $ids;
  78. return $this->find( $args, $limit, $offset );
  79. }
  80. /**
  81. * Find and return an array of WooCommerce product objects already submitted to Google Merchant Center.
  82. *
  83. * @param array $args Array of WooCommerce args (except 'return' and 'meta_query').
  84. * @param int $limit Maximum number of results to retrieve or -1 for unlimited.
  85. * @param int $offset Amount to offset product results.
  86. *
  87. * @return WC_Product[] Array of WooCommerce product objects
  88. */
  89. public function find_synced_products( array $args = [], int $limit = -1, int $offset = 0 ): array {
  90. $args['meta_query'] = $this->get_synced_products_meta_query();
  91. return $this->find( $args, $limit, $offset );
  92. }
  93. /**
  94. * Find and return an array of WooCommerce product IDs already submitted to Google Merchant Center.
  95. *
  96. * Note: Includes product variations.
  97. *
  98. * @param array $args Array of WooCommerce args (except 'return' and 'meta_query').
  99. * @param int $limit Maximum number of results to retrieve or -1 for unlimited.
  100. * @param int $offset Amount to offset product results.
  101. *
  102. * @return int[] Array of WooCommerce product IDs
  103. */
  104. public function find_synced_product_ids( array $args = [], int $limit = -1, int $offset = 0 ): array {
  105. $args['meta_query'] = $this->get_synced_products_meta_query();
  106. return $this->find_ids( $args, $limit, $offset );
  107. }
  108. /**
  109. * @return array
  110. */
  111. protected function get_synced_products_meta_query(): array {
  112. return [
  113. [
  114. 'key' => ProductMetaHandler::KEY_GOOGLE_IDS,
  115. 'compare' => 'EXISTS',
  116. ],
  117. ];
  118. }
  119. /**
  120. * Find and return an array of WooCommerce product objects ready to be submitted to Google Merchant Center.
  121. *
  122. * @param array $args Array of WooCommerce args (except 'return'), and product metadata.
  123. * @param int $limit Maximum number of results to retrieve or -1 for unlimited.
  124. * @param int $offset Amount to offset product results.
  125. *
  126. * @return FilteredProductList List of WooCommerce product objects after filtering.
  127. */
  128. public function find_sync_ready_products( array $args = [], int $limit = - 1, int $offset = 0 ): FilteredProductList {
  129. $results = $this->find( $this->get_sync_ready_products_query_args( $args ), $limit, $offset );
  130. return $this->product_filter->filter_sync_ready_products( $results, false );
  131. }
  132. /**
  133. * Find and return an array of WooCommerce product IDs ready to be submitted to Google Merchant Center.
  134. *
  135. * @param array $args Array of WooCommerce args (except 'return'), and product metadata.
  136. * @param int $limit Maximum number of results to retrieve or -1 for unlimited.
  137. * @param int $offset Amount to offset product results.
  138. *
  139. * @return FilteredProductList List of WooCommerce product IDs after filtering.
  140. */
  141. public function find_sync_ready_product_ids( array $args = [], int $limit = - 1, int $offset = 0 ): FilteredProductList {
  142. $results = $this->find( $this->get_sync_ready_products_query_args( $args ), $limit, $offset );
  143. return $this->product_filter->filter_sync_ready_products( $results, true );
  144. }
  145. /**
  146. * @return array
  147. */
  148. protected function get_sync_ready_products_meta_query(): array {
  149. return [
  150. 'relation' => 'OR',
  151. [
  152. 'key' => ProductMetaHandler::KEY_VISIBILITY,
  153. 'compare' => 'NOT EXISTS',
  154. ],
  155. [
  156. 'key' => ProductMetaHandler::KEY_VISIBILITY,
  157. 'compare' => '!=',
  158. 'value' => ChannelVisibility::DONT_SYNC_AND_SHOW,
  159. ],
  160. ];
  161. }
  162. /**
  163. * @param array $args Array of WooCommerce args (except 'return'), and product metadata.
  164. *
  165. * @return array
  166. */
  167. protected function get_sync_ready_products_query_args( array $args = [] ): array {
  168. $args['meta_query'] = $this->get_sync_ready_products_meta_query();
  169. // don't include variable products in query
  170. $args['type'] = array_diff( ProductSyncer::get_supported_product_types(), [ 'variable' ] );
  171. // only include published products
  172. if ( empty( $args['status'] ) ) {
  173. $args['status'] = [ 'publish' ];
  174. }
  175. return $args;
  176. }
  177. /**
  178. * @return array
  179. */
  180. protected function get_valid_products_meta_query(): array {
  181. return [
  182. 'relation' => 'OR',
  183. [
  184. 'key' => ProductMetaHandler::KEY_ERRORS,
  185. 'compare' => 'NOT EXISTS',
  186. ],
  187. [
  188. 'key' => ProductMetaHandler::KEY_ERRORS,
  189. 'compare' => '=',
  190. 'value' => '',
  191. ],
  192. ];
  193. }
  194. /**
  195. * Find and return an array of WooCommerce product IDs nearly expired and ready to be re-submitted to Google Merchant Center.
  196. *
  197. * @param int $limit Maximum number of results to retrieve or -1 for unlimited.
  198. * @param int $offset Amount to offset product results.
  199. *
  200. * @return int[] Array of WooCommerce product IDs
  201. */
  202. public function find_expiring_product_ids( int $limit = - 1, int $offset = 0 ): array {
  203. $args['meta_query'] = [
  204. 'relation' => 'AND',
  205. $this->get_sync_ready_products_meta_query(),
  206. $this->get_valid_products_meta_query(),
  207. [
  208. [
  209. 'key' => ProductMetaHandler::KEY_SYNCED_AT,
  210. 'compare' => '<',
  211. 'value' => strtotime( '-25 days' ),
  212. ],
  213. ],
  214. ];
  215. return $this->find_ids( $args, $limit, $offset );
  216. }
  217. /**
  218. * Find and return an array of WooCommerce product IDs that are marked as MC not_synced.
  219. * Excludes variations and variable products without variations.
  220. *
  221. * @param int $limit Maximum number of results to retrieve or -1 for unlimited.
  222. * @param int $offset Amount to offset product results.
  223. *
  224. * @return int[] Array of WooCommerce product IDs
  225. */
  226. public function find_mc_not_synced_product_ids( int $limit = -1, int $offset = 0 ): array {
  227. $types = ProductSyncer::get_supported_product_types();
  228. $types = array_diff( $types, [ 'variation' ] );
  229. $args = [
  230. 'status' => 'publish',
  231. 'type' => $types,
  232. 'meta_query' => [
  233. [
  234. 'key' => ProductMetaHandler::KEY_MC_STATUS,
  235. 'compare' => '=',
  236. 'value' => MCStatus::NOT_SYNCED,
  237. ],
  238. ],
  239. ];
  240. return $this->find_ids( $args, $limit, $offset );
  241. }
  242. /**
  243. * Returns an array of Google Product IDs associated with all synced WooCommerce products.
  244. * Note: excludes variable parent products as only the child variation products are actually synced
  245. * to Merchant Center
  246. *
  247. * @since 1.1.0
  248. *
  249. * @return array Google Product IDS
  250. */
  251. public function find_all_synced_google_ids(): array {
  252. // Don't include variable parent products as they aren't actually synced to Merchant Center.
  253. $args['type'] = array_diff( ProductSyncer::get_supported_product_types(), [ 'variable' ] );
  254. $synced_product_ids = $this->find_synced_product_ids( $args );
  255. $google_ids_meta_key = $this->prefix_meta_key( ProductMetaHandler::KEY_GOOGLE_IDS );
  256. $synced_google_ids = [];
  257. foreach ( $synced_product_ids as $product_id ) {
  258. $meta_google_ids = get_post_meta( $product_id, $google_ids_meta_key, true );
  259. if ( ! is_array( $meta_google_ids ) ) {
  260. do_action(
  261. 'woocommerce_gla_debug_message',
  262. sprintf( 'Invalid Google IDs retrieve for product %d', $product_id ),
  263. __METHOD__
  264. );
  265. continue;
  266. }
  267. $synced_google_ids = array_merge( $synced_google_ids, array_values( $meta_google_ids ) );
  268. }
  269. return $synced_google_ids;
  270. }
  271. /**
  272. * Find and return an array of WooCommerce products based on the provided arguments.
  273. *
  274. * @param array $args Array of WooCommerce args (see below), and product metadata.
  275. * @param int $limit Maximum number of results to retrieve or -1 for unlimited.
  276. * @param int $offset Amount to offset product results.
  277. *
  278. * @link https://github.com/woocommerce/woocommerce/wiki/wc_get_products-and-WC_Product_Query
  279. * @see ProductMetaHandler::TYPES For the list of meta data that can be used as query arguments.
  280. *
  281. * @return WC_Product[]|int[] Array of WooCommerce product objects or IDs, depending on the 'return' argument.
  282. */
  283. protected function execute_woocommerce_query( array $args = [], int $limit = -1, int $offset = 0 ): array {
  284. $args['limit'] = $limit;
  285. $args['offset'] = $offset;
  286. return wc_get_products( $this->prepare_query_args( $args ) );
  287. }
  288. /**
  289. * @param array $args Array of WooCommerce args (except 'return'), and product metadata.
  290. *
  291. * @see execute_woocommerce_query For more information about the arguments.
  292. *
  293. * @return array
  294. */
  295. protected function prepare_query_args( array $args = [] ): array {
  296. if ( empty( $args ) ) {
  297. return [];
  298. }
  299. if ( ! empty( $args['meta_query'] ) ) {
  300. $args['meta_query'] = $this->meta_handler->prefix_meta_query_keys( $args['meta_query'] );
  301. }
  302. // only include supported product types
  303. if ( empty( $args['type'] ) ) {
  304. $args['type'] = ProductSyncer::get_supported_product_types();
  305. }
  306. // use no ordering unless specified in arguments. overrides the default WooCommerce query args
  307. if ( empty( $args['orderby'] ) ) {
  308. $args['orderby'] = 'none';
  309. }
  310. return $args;
  311. }
  312. }