class-studiou-wc-fpp-db.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. <?php
  2. if (!defined('WPINC')) {
  3. die;
  4. }
  5. class Studiou_WC_FPP_DB {
  6. const TABLE_NAME = 'studiou_fpp_files';
  7. public function __construct() {
  8. $this->register_media_taxonomy();
  9. }
  10. private function register_media_taxonomy() {
  11. add_action('init', array($this, 'register_media_category_taxonomy'));
  12. }
  13. public function register_media_category_taxonomy() {
  14. $labels = array(
  15. 'name' => __('Media Categories', 'studiou-wc-free-photo-product'),
  16. 'singular_name' => __('Media Category', 'studiou-wc-free-photo-product'),
  17. 'search_items' => __('Search Media Categories', 'studiou-wc-free-photo-product'),
  18. 'all_items' => __('All Media Categories', 'studiou-wc-free-photo-product'),
  19. 'parent_item' => __('Parent Media Category', 'studiou-wc-free-photo-product'),
  20. 'parent_item_colon' => __('Parent Media Category:', 'studiou-wc-free-photo-product'),
  21. 'edit_item' => __('Edit Media Category', 'studiou-wc-free-photo-product'),
  22. 'update_item' => __('Update Media Category', 'studiou-wc-free-photo-product'),
  23. 'add_new_item' => __('Add New Media Category', 'studiou-wc-free-photo-product'),
  24. 'new_item_name' => __('New Media Category Name', 'studiou-wc-free-photo-product'),
  25. 'menu_name' => __('Media Categories', 'studiou-wc-free-photo-product'),
  26. );
  27. register_taxonomy('studiou_media_category', 'attachment', array(
  28. 'hierarchical' => true,
  29. 'labels' => $labels,
  30. 'show_ui' => true,
  31. 'show_admin_column' => true,
  32. 'query_var' => true,
  33. 'rewrite' => array('slug' => 'media-category'),
  34. 'show_in_rest' => true,
  35. ));
  36. }
  37. public function get_table_name() {
  38. global $wpdb;
  39. return $wpdb->prefix . self::TABLE_NAME;
  40. }
  41. public function create_tables() {
  42. global $wpdb;
  43. $table_name = $this->get_table_name();
  44. $charset_collate = $wpdb->get_charset_collate();
  45. $sql = "CREATE TABLE {$table_name} (
  46. id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  47. product_id bigint(20) unsigned NOT NULL,
  48. variation_id bigint(20) unsigned DEFAULT 0,
  49. order_id bigint(20) unsigned DEFAULT 0,
  50. order_item_id bigint(20) unsigned DEFAULT 0,
  51. attachment_id bigint(20) unsigned NOT NULL,
  52. customer_id bigint(20) unsigned DEFAULT 0,
  53. session_key varchar(64) DEFAULT '',
  54. batch_token varchar(32) NOT NULL DEFAULT '',
  55. file_name varchar(255) NOT NULL,
  56. status varchar(20) NOT NULL DEFAULT 'ready',
  57. created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  58. updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  59. PRIMARY KEY (id),
  60. KEY product_id (product_id),
  61. KEY order_id (order_id),
  62. KEY attachment_id (attachment_id),
  63. KEY status (status),
  64. KEY customer_id (customer_id),
  65. KEY batch_token (batch_token)
  66. ) {$charset_collate};";
  67. require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
  68. dbDelta($sql);
  69. update_option('studiou_wcfpp_db_version', STUDIOU_WCFPP_VERSION);
  70. }
  71. public function insert_file_record($data) {
  72. global $wpdb;
  73. $defaults = array(
  74. 'product_id' => 0,
  75. 'variation_id' => 0,
  76. 'order_id' => 0,
  77. 'order_item_id' => 0,
  78. 'attachment_id' => 0,
  79. 'customer_id' => get_current_user_id(),
  80. 'session_key' => '',
  81. 'batch_token' => '',
  82. 'file_name' => '',
  83. 'status' => 'ready',
  84. 'created_at' => current_time('mysql'),
  85. 'updated_at' => current_time('mysql'),
  86. );
  87. $data = wp_parse_args($data, $defaults);
  88. $result = $wpdb->insert(
  89. $this->get_table_name(),
  90. $data,
  91. array('%d', '%d', '%d', '%d', '%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s')
  92. );
  93. if ($result === false) {
  94. return false;
  95. }
  96. return $wpdb->insert_id;
  97. }
  98. public function get_file_record($id) {
  99. global $wpdb;
  100. $table = $this->get_table_name();
  101. return $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE id = %d", $id));
  102. }
  103. public function get_file_records($args = array()) {
  104. global $wpdb;
  105. $table = $this->get_table_name();
  106. $defaults = array(
  107. 'product_id' => 0,
  108. 'order_id' => 0,
  109. 'status' => '',
  110. 'orderby' => 'created_at',
  111. 'order' => 'DESC',
  112. 'limit' => 50,
  113. 'offset' => 0,
  114. 'search' => '',
  115. );
  116. $args = wp_parse_args($args, $defaults);
  117. $where = array('1=1');
  118. $values = array();
  119. if (!empty($args['product_id'])) {
  120. $where[] = 'product_id = %d';
  121. $values[] = $args['product_id'];
  122. }
  123. if (!empty($args['order_id'])) {
  124. $where[] = 'order_id = %d';
  125. $values[] = $args['order_id'];
  126. }
  127. if (!empty($args['status'])) {
  128. $where[] = 'status = %s';
  129. $values[] = $args['status'];
  130. }
  131. if (!empty($args['search'])) {
  132. $where[] = 'file_name LIKE %s';
  133. $values[] = '%' . $wpdb->esc_like($args['search']) . '%';
  134. }
  135. $where_clause = implode(' AND ', $where);
  136. $allowed_orderby = array('id', 'product_id', 'order_id', 'file_name', 'status', 'created_at', 'updated_at');
  137. $orderby = in_array($args['orderby'], $allowed_orderby) ? $args['orderby'] : 'created_at';
  138. $order = strtoupper($args['order']) === 'ASC' ? 'ASC' : 'DESC';
  139. $sql = "SELECT * FROM {$table} WHERE {$where_clause} ORDER BY {$orderby} {$order} LIMIT %d OFFSET %d";
  140. $values[] = (int) $args['limit'];
  141. $values[] = (int) $args['offset'];
  142. if (!empty($values)) {
  143. $sql = $wpdb->prepare($sql, $values);
  144. }
  145. return $wpdb->get_results($sql);
  146. }
  147. public function count_file_records($args = array()) {
  148. global $wpdb;
  149. $table = $this->get_table_name();
  150. $where = array('1=1');
  151. $values = array();
  152. if (!empty($args['product_id'])) {
  153. $where[] = 'product_id = %d';
  154. $values[] = $args['product_id'];
  155. }
  156. if (!empty($args['order_id'])) {
  157. $where[] = 'order_id = %d';
  158. $values[] = $args['order_id'];
  159. }
  160. if (!empty($args['status'])) {
  161. $where[] = 'status = %s';
  162. $values[] = $args['status'];
  163. }
  164. if (!empty($args['search'])) {
  165. $where[] = 'file_name LIKE %s';
  166. $values[] = '%' . $wpdb->esc_like($args['search']) . '%';
  167. }
  168. $where_clause = implode(' AND ', $where);
  169. $sql = "SELECT COUNT(*) FROM {$table} WHERE {$where_clause}";
  170. if (!empty($values)) {
  171. $sql = $wpdb->prepare($sql, $values);
  172. }
  173. return (int) $wpdb->get_var($sql);
  174. }
  175. public function update_file_record($id, $data) {
  176. global $wpdb;
  177. $data['updated_at'] = current_time('mysql');
  178. return $wpdb->update(
  179. $this->get_table_name(),
  180. $data,
  181. array('id' => $id),
  182. null,
  183. array('%d')
  184. );
  185. }
  186. public function update_file_status($id, $status) {
  187. $allowed = array('ready', 'downloaded', 'solved');
  188. if (!in_array($status, $allowed)) {
  189. return false;
  190. }
  191. return $this->update_file_record($id, array('status' => $status));
  192. }
  193. public function delete_file_record($id) {
  194. global $wpdb;
  195. $record = $this->get_file_record($id);
  196. if (!$record) {
  197. return false;
  198. }
  199. // Delete the attachment from media library
  200. if ($record->attachment_id) {
  201. wp_delete_attachment($record->attachment_id, true);
  202. }
  203. return $wpdb->delete(
  204. $this->get_table_name(),
  205. array('id' => $id),
  206. array('%d')
  207. );
  208. }
  209. public function get_file_records_by_ids($ids) {
  210. global $wpdb;
  211. $table = $this->get_table_name();
  212. if (empty($ids)) {
  213. return array();
  214. }
  215. $ids = array_map('intval', $ids);
  216. $placeholders = implode(',', array_fill(0, count($ids), '%d'));
  217. $sql = $wpdb->prepare("SELECT * FROM {$table} WHERE id IN ({$placeholders})", $ids);
  218. return $wpdb->get_results($sql);
  219. }
  220. /**
  221. * Return all file records belonging to a batch token that are not yet bound to an order.
  222. *
  223. * @param string $batch_token
  224. * @param int $product_id Optional — restrict to a single product_id.
  225. * @return array of row objects, oldest first (ascending id).
  226. */
  227. public function get_batch_files($batch_token, $product_id = 0) {
  228. global $wpdb;
  229. if (empty($batch_token)) {
  230. return array();
  231. }
  232. $table = $this->get_table_name();
  233. if ((int) $product_id > 0) {
  234. $sql = $wpdb->prepare(
  235. "SELECT * FROM {$table}
  236. WHERE batch_token = %s
  237. AND order_id = 0
  238. AND product_id = %d
  239. ORDER BY id ASC",
  240. $batch_token,
  241. (int) $product_id
  242. );
  243. } else {
  244. $sql = $wpdb->prepare(
  245. "SELECT * FROM {$table}
  246. WHERE batch_token = %s
  247. AND order_id = 0
  248. ORDER BY id ASC",
  249. $batch_token
  250. );
  251. }
  252. return $wpdb->get_results($sql);
  253. }
  254. /**
  255. * Count batch files for a product (enforces per-product max-uploads cap).
  256. */
  257. public function count_batch_files($batch_token, $product_id) {
  258. global $wpdb;
  259. if (empty($batch_token) || (int) $product_id <= 0) {
  260. return 0;
  261. }
  262. $table = $this->get_table_name();
  263. return (int) $wpdb->get_var($wpdb->prepare(
  264. "SELECT COUNT(*) FROM {$table}
  265. WHERE batch_token = %s
  266. AND order_id = 0
  267. AND product_id = %d",
  268. $batch_token,
  269. (int) $product_id
  270. ));
  271. }
  272. public function link_file_to_order($file_record_id, $order_id, $order_item_id) {
  273. return $this->update_file_record($file_record_id, array(
  274. 'order_id' => $order_id,
  275. 'order_item_id' => $order_item_id,
  276. ));
  277. }
  278. public function get_media_categories() {
  279. $terms = get_terms(array(
  280. 'taxonomy' => 'studiou_media_category',
  281. 'hide_empty' => false,
  282. ));
  283. if (is_wp_error($terms)) {
  284. return array();
  285. }
  286. return $terms;
  287. }
  288. }