/includes/abstracts/abstract-wc-product.php

  1. <?php 
  2. if ( ! defined( 'ABSPATH' ) ) { 
  3. exit; 
  4.  
  5. /** 
  6. * Legacy product contains all deprecated methods for this class and can be 
  7. * removed in the future. 
  8. */ 
  9. include_once( WC_ABSPATH . 'includes/legacy/abstract-wc-legacy-product.php' ); 
  10.  
  11. /** 
  12. * Abstract Product Class 
  13. * 
  14. * The WooCommerce product class handles individual product data. 
  15. * 
  16. * @version 3.0.0 
  17. * @package WooCommerce/Abstracts 
  18. * @category Abstract Class 
  19. * @author WooThemes 
  20. */ 
  21. class WC_Product extends WC_Abstract_Legacy_Product { 
  22.  
  23. /** 
  24. * This is the name of this object type. 
  25. * @var string 
  26. */ 
  27. protected $object_type = 'product'; 
  28.  
  29. /** 
  30. * Post type. 
  31. * @var string 
  32. */ 
  33. protected $post_type = 'product'; 
  34.  
  35. /** 
  36. * Cache group. 
  37. * @var string 
  38. */ 
  39. protected $cache_group = 'products'; 
  40.  
  41. /** 
  42. * Stores product data. 
  43. * 
  44. * @var array 
  45. */ 
  46. protected $data = array( 
  47. 'name' => '',  
  48. 'slug' => '',  
  49. 'date_created' => null,  
  50. 'date_modified' => null,  
  51. 'status' => false,  
  52. 'featured' => false,  
  53. 'catalog_visibility' => 'visible',  
  54. 'description' => '',  
  55. 'short_description' => '',  
  56. 'sku' => '',  
  57. 'price' => '',  
  58. 'regular_price' => '',  
  59. 'sale_price' => '',  
  60. 'date_on_sale_from' => null,  
  61. 'date_on_sale_to' => null,  
  62. 'total_sales' => '0',  
  63. 'tax_status' => 'taxable',  
  64. 'tax_class' => '',  
  65. 'manage_stock' => false,  
  66. 'stock_quantity' => null,  
  67. 'stock_status' => 'instock',  
  68. 'backorders' => 'no',  
  69. 'sold_individually' => false,  
  70. 'weight' => '',  
  71. 'length' => '',  
  72. 'width' => '',  
  73. 'height' => '',  
  74. 'upsell_ids' => array(),  
  75. 'cross_sell_ids' => array(),  
  76. 'parent_id' => 0,  
  77. 'reviews_allowed' => true,  
  78. 'purchase_note' => '',  
  79. 'attributes' => array(),  
  80. 'default_attributes' => array(),  
  81. 'menu_order' => 0,  
  82. 'virtual' => false,  
  83. 'downloadable' => false,  
  84. 'category_ids' => array(),  
  85. 'tag_ids' => array(),  
  86. 'shipping_class_id' => 0,  
  87. 'downloads' => array(),  
  88. 'image_id' => '',  
  89. 'gallery_image_ids' => array(),  
  90. 'download_limit' => -1,  
  91. 'download_expiry' => -1,  
  92. 'rating_counts' => array(),  
  93. 'average_rating' => 0,  
  94. 'review_count' => 0,  
  95. ); 
  96.  
  97. /** 
  98. * Supported features such as 'ajax_add_to_cart'. 
  99. * 
  100. * @var array 
  101. */ 
  102. protected $supports = array(); 
  103.  
  104. /** 
  105. * Get the product if ID is passed, otherwise the product is new and empty. 
  106. * This class should NOT be instantiated, but the wc_get_product() function 
  107. * should be used. It is possible, but the wc_get_product() is preferred. 
  108. * 
  109. * @param int|WC_Product|object $product Product to init. 
  110. */ 
  111. public function __construct( $product = 0 ) { 
  112. parent::__construct( $product ); 
  113. if ( is_numeric( $product ) && $product > 0 ) { 
  114. $this->set_id( $product ); 
  115. } elseif ( $product instanceof self ) { 
  116. $this->set_id( absint( $product->get_id() ) ); 
  117. } elseif ( ! empty( $product->ID ) ) { 
  118. $this->set_id( absint( $product->ID ) ); 
  119. } else { 
  120. $this->set_object_read( true ); 
  121.  
  122. $this->data_store = WC_Data_Store::load( 'product-' . $this->get_type() ); 
  123. if ( $this->get_id() > 0 ) { 
  124. $this->data_store->read( $this ); 
  125.  
  126. /** 
  127. * Get internal type. Should return string and *should be overridden* by child classes. 
  128. * 
  129. * The product_type property is deprecated but is used here for BW compat with child classes which may be defining product_type and not have a get_type method. 
  130. * 
  131. * @since 3.0.0 
  132. * @return string 
  133. */ 
  134. public function get_type() { 
  135. return isset( $this->product_type ) ? $this->product_type : 'simple'; 
  136.  
  137. /** 
  138. |-------------------------------------------------------------------------- 
  139. | Getters 
  140. |-------------------------------------------------------------------------- 
  141. | 
  142. | Methods for getting data from the product object. 
  143. */ 
  144.  
  145. /** 
  146. * Get product name. 
  147. * 
  148. * @since 3.0.0 
  149. * @param string $context 
  150. * @return string 
  151. */ 
  152. public function get_name( $context = 'view' ) { 
  153. return $this->get_prop( 'name', $context ); 
  154.  
  155. /** 
  156. * Get product slug. 
  157. * 
  158. * @since 3.0.0 
  159. * @param string $context 
  160. * @return string 
  161. */ 
  162. public function get_slug( $context = 'view' ) { 
  163. return $this->get_prop( 'slug', $context ); 
  164.  
  165. /** 
  166. * Get product created date. 
  167. * 
  168. * @since 3.0.0 
  169. * @param string $context 
  170. * @return WC_DateTime|NULL object if the date is set or null if there is no date. 
  171. */ 
  172. public function get_date_created( $context = 'view' ) { 
  173. return $this->get_prop( 'date_created', $context ); 
  174.  
  175. /** 
  176. * Get product modified date. 
  177. * 
  178. * @since 3.0.0 
  179. * @param string $context 
  180. * @return WC_DateTime|NULL object if the date is set or null if there is no date. 
  181. */ 
  182. public function get_date_modified( $context = 'view' ) { 
  183. return $this->get_prop( 'date_modified', $context ); 
  184.  
  185. /** 
  186. * Get product status. 
  187. * 
  188. * @since 3.0.0 
  189. * @param string $context 
  190. * @return string 
  191. */ 
  192. public function get_status( $context = 'view' ) { 
  193. return $this->get_prop( 'status', $context ); 
  194.  
  195. /** 
  196. * If the product is featured. 
  197. * 
  198. * @since 3.0.0 
  199. * @param string $context 
  200. * @return boolean 
  201. */ 
  202. public function get_featured( $context = 'view' ) { 
  203. return $this->get_prop( 'featured', $context ); 
  204.  
  205. /** 
  206. * Get catalog visibility. 
  207. * 
  208. * @since 3.0.0 
  209. * @param string $context 
  210. * @return string 
  211. */ 
  212. public function get_catalog_visibility( $context = 'view' ) { 
  213. return $this->get_prop( 'catalog_visibility', $context ); 
  214.  
  215. /** 
  216. * Get product description. 
  217. * 
  218. * @since 3.0.0 
  219. * @param string $context 
  220. * @return string 
  221. */ 
  222. public function get_description( $context = 'view' ) { 
  223. return $this->get_prop( 'description', $context ); 
  224.  
  225. /** 
  226. * Get product short description. 
  227. * 
  228. * @since 3.0.0 
  229. * @param string $context 
  230. * @return string 
  231. */ 
  232. public function get_short_description( $context = 'view' ) { 
  233. return $this->get_prop( 'short_description', $context ); 
  234.  
  235. /** 
  236. * Get SKU (Stock-keeping unit) - product unique ID. 
  237. * 
  238. * @param string $context 
  239. * @return string 
  240. */ 
  241. public function get_sku( $context = 'view' ) { 
  242. return $this->get_prop( 'sku', $context ); 
  243.  
  244. /** 
  245. * Returns the product's active price. 
  246. * 
  247. * @param string $context 
  248. * @return string price 
  249. */ 
  250. public function get_price( $context = 'view' ) { 
  251. return $this->get_prop( 'price', $context ); 
  252.  
  253. /** 
  254. * Returns the product's regular price. 
  255. * 
  256. * @param string $context 
  257. * @return string price 
  258. */ 
  259. public function get_regular_price( $context = 'view' ) { 
  260. return $this->get_prop( 'regular_price', $context ); 
  261.  
  262. /** 
  263. * Returns the product's sale price. 
  264. * 
  265. * @param string $context 
  266. * @return string price 
  267. */ 
  268. public function get_sale_price( $context = 'view' ) { 
  269. return $this->get_prop( 'sale_price', $context ); 
  270.  
  271. /** 
  272. * Get date on sale from. 
  273. * 
  274. * @since 3.0.0 
  275. * @param string $context 
  276. * @return WC_DateTime|NULL object if the date is set or null if there is no date. 
  277. */ 
  278. public function get_date_on_sale_from( $context = 'view' ) { 
  279. return $this->get_prop( 'date_on_sale_from', $context ); 
  280.  
  281. /** 
  282. * Get date on sale to. 
  283. * 
  284. * @since 3.0.0 
  285. * @param string $context 
  286. * @return WC_DateTime|NULL object if the date is set or null if there is no date. 
  287. */ 
  288. public function get_date_on_sale_to( $context = 'view' ) { 
  289. return $this->get_prop( 'date_on_sale_to', $context ); 
  290.  
  291. /** 
  292. * Get number total of sales. 
  293. * 
  294. * @since 3.0.0 
  295. * @param string $context 
  296. * @return int 
  297. */ 
  298. public function get_total_sales( $context = 'view' ) { 
  299. return $this->get_prop( 'total_sales', $context ); 
  300.  
  301. /** 
  302. * Returns the tax status. 
  303. * 
  304. * @param string $context 
  305. * @return string 
  306. */ 
  307. public function get_tax_status( $context = 'view' ) { 
  308. return $this->get_prop( 'tax_status', $context ); 
  309.  
  310. /** 
  311. * Returns the tax class. 
  312. * 
  313. * @param string $context 
  314. * @return string 
  315. */ 
  316. public function get_tax_class( $context = 'view' ) { 
  317. return $this->get_prop( 'tax_class', $context ); 
  318.  
  319. /** 
  320. * Return if product manage stock. 
  321. * 
  322. * @since 3.0.0 
  323. * @param string $context 
  324. * @return boolean 
  325. */ 
  326. public function get_manage_stock( $context = 'view' ) { 
  327. return $this->get_prop( 'manage_stock', $context ); 
  328.  
  329. /** 
  330. * Returns number of items available for sale. 
  331. * 
  332. * @param string $context 
  333. * @return int|null 
  334. */ 
  335. public function get_stock_quantity( $context = 'view' ) { 
  336. return $this->get_prop( 'stock_quantity', $context ); 
  337.  
  338. /** 
  339. * Return the stock status. 
  340. * 
  341. * @param string $context 
  342. * @since 3.0.0 
  343. * @return string 
  344. */ 
  345. public function get_stock_status( $context = 'view' ) { 
  346. return $this->get_prop( 'stock_status', $context ); 
  347.  
  348. /** 
  349. * Get backorders. 
  350. * 
  351. * @param string $context 
  352. * @since 3.0.0 
  353. * @return string yes no or notify 
  354. */ 
  355. public function get_backorders( $context = 'view' ) { 
  356. return $this->get_prop( 'backorders', $context ); 
  357.  
  358. /** 
  359. * Return if should be sold individually. 
  360. * 
  361. * @param string $context 
  362. * @since 3.0.0 
  363. * @return boolean 
  364. */ 
  365. public function get_sold_individually( $context = 'view' ) { 
  366. return $this->get_prop( 'sold_individually', $context ); 
  367.  
  368. /** 
  369. * Returns the product's weight. 
  370. * 
  371. * @param string $context 
  372. * @return string 
  373. */ 
  374. public function get_weight( $context = 'view' ) { 
  375. return $this->get_prop( 'weight', $context ); 
  376.  
  377. /** 
  378. * Returns the product length. 
  379. * 
  380. * @param string $context 
  381. * @return string 
  382. */ 
  383. public function get_length( $context = 'view' ) { 
  384. return $this->get_prop( 'length', $context ); 
  385.  
  386. /** 
  387. * Returns the product width. 
  388. * 
  389. * @param string $context 
  390. * @return string 
  391. */ 
  392. public function get_width( $context = 'view' ) { 
  393. return $this->get_prop( 'width', $context ); 
  394.  
  395. /** 
  396. * Returns the product height. 
  397. * 
  398. * @param string $context 
  399. * @return string 
  400. */ 
  401. public function get_height( $context = 'view' ) { 
  402. return $this->get_prop( 'height', $context ); 
  403.  
  404. /** 
  405. * Returns formatted dimensions. 
  406. * 
  407. * @param $formatted True by default for legacy support - will be false/not set in future versions to return the array only. Use wc_format_dimensions for formatted versions instead. 
  408. * @return string|array 
  409. */ 
  410. public function get_dimensions( $formatted = true ) { 
  411. if ( $formatted ) { 
  412. wc_deprecated_argument( 'WC_Product::get_dimensions', '3.0', 'By default, get_dimensions has an argument set to true so that HTML is returned. This is to support the legacy version of the method. To get HTML dimensions, instead use wc_format_dimensions() function. Pass false to this method to return an array of dimensions. This will be the new default behavior in future versions.' ); 
  413. return apply_filters( 'woocommerce_product_dimensions', wc_format_dimensions( $this->get_dimensions( false ) ), $this ); 
  414. return array( 
  415. 'length' => $this->get_length(),  
  416. 'width' => $this->get_width(),  
  417. 'height' => $this->get_height(),  
  418. ); 
  419.  
  420. /** 
  421. * Get upsel IDs. 
  422. * 
  423. * @since 3.0.0 
  424. * @param string $context 
  425. * @return array 
  426. */ 
  427. public function get_upsell_ids( $context = 'view' ) { 
  428. return $this->get_prop( 'upsell_ids', $context ); 
  429.  
  430. /** 
  431. * Get cross sell IDs. 
  432. * 
  433. * @since 3.0.0 
  434. * @param string $context 
  435. * @return array 
  436. */ 
  437. public function get_cross_sell_ids( $context = 'view' ) { 
  438. return $this->get_prop( 'cross_sell_ids', $context ); 
  439.  
  440. /** 
  441. * Get parent ID. 
  442. * 
  443. * @since 3.0.0 
  444. * @param string $context 
  445. * @return int 
  446. */ 
  447. public function get_parent_id( $context = 'view' ) { 
  448. return $this->get_prop( 'parent_id', $context ); 
  449.  
  450. /** 
  451. * Return if reviews is allowed. 
  452. * 
  453. * @since 3.0.0 
  454. * @param string $context 
  455. * @return bool 
  456. */ 
  457. public function get_reviews_allowed( $context = 'view' ) { 
  458. return $this->get_prop( 'reviews_allowed', $context ); 
  459.  
  460. /** 
  461. * Get purchase note. 
  462. * 
  463. * @since 3.0.0 
  464. * @param string $context 
  465. * @return string 
  466. */ 
  467. public function get_purchase_note( $context = 'view' ) { 
  468. return $this->get_prop( 'purchase_note', $context ); 
  469.  
  470. /** 
  471. * Returns product attributes. 
  472. * 
  473. * @param string $context 
  474. * @return array 
  475. */ 
  476. public function get_attributes( $context = 'view' ) { 
  477. return $this->get_prop( 'attributes', $context ); 
  478.  
  479. /** 
  480. * Get default attributes. 
  481. * 
  482. * @since 3.0.0 
  483. * @param string $context 
  484. * @return array 
  485. */ 
  486. public function get_default_attributes( $context = 'view' ) { 
  487. return $this->get_prop( 'default_attributes', $context ); 
  488.  
  489. /** 
  490. * Get menu order. 
  491. * 
  492. * @since 3.0.0 
  493. * @param string $context 
  494. * @return int 
  495. */ 
  496. public function get_menu_order( $context = 'view' ) { 
  497. return $this->get_prop( 'menu_order', $context ); 
  498.  
  499. /** 
  500. * Get category ids. 
  501. * 
  502. * @since 3.0.0 
  503. * @param string $context 
  504. * @return array 
  505. */ 
  506. public function get_category_ids( $context = 'view' ) { 
  507. return $this->get_prop( 'category_ids', $context ); 
  508.  
  509. /** 
  510. * Get tag ids. 
  511. * 
  512. * @since 3.0.0 
  513. * @param string $context 
  514. * @return array 
  515. */ 
  516. public function get_tag_ids( $context = 'view' ) { 
  517. return $this->get_prop( 'tag_ids', $context ); 
  518.  
  519. /** 
  520. * Get virtual. 
  521. * 
  522. * @since 3.0.0 
  523. * @param string $context 
  524. * @return bool 
  525. */ 
  526. public function get_virtual( $context = 'view' ) { 
  527. return $this->get_prop( 'virtual', $context ); 
  528.  
  529. /** 
  530. * Returns the gallery attachment ids. 
  531. * 
  532. * @param string $context 
  533. * @return array 
  534. */ 
  535. public function get_gallery_image_ids( $context = 'view' ) { 
  536. return $this->get_prop( 'gallery_image_ids', $context ); 
  537.  
  538. /** 
  539. * Get shipping class ID. 
  540. * 
  541. * @since 3.0.0 
  542. * @param string $context 
  543. * @return int 
  544. */ 
  545. public function get_shipping_class_id( $context = 'view' ) { 
  546. return $this->get_prop( 'shipping_class_id', $context ); 
  547.  
  548. /** 
  549. * Get downloads. 
  550. * 
  551. * @since 3.0.0 
  552. * @param string $context 
  553. * @return array 
  554. */ 
  555. public function get_downloads( $context = 'view' ) { 
  556. return $this->get_prop( 'downloads', $context ); 
  557.  
  558. /** 
  559. * Get download expiry. 
  560. * 
  561. * @since 3.0.0 
  562. * @param string $context 
  563. * @return int 
  564. */ 
  565. public function get_download_expiry( $context = 'view' ) { 
  566. return $this->get_prop( 'download_expiry', $context ); 
  567.  
  568. /** 
  569. * Get downloadable. 
  570. * 
  571. * @since 3.0.0 
  572. * @param string $context 
  573. * @return bool 
  574. */ 
  575. public function get_downloadable( $context = 'view' ) { 
  576. return $this->get_prop( 'downloadable', $context ); 
  577.  
  578. /** 
  579. * Get download limit. 
  580. * 
  581. * @since 3.0.0 
  582. * @param string $context 
  583. * @return int 
  584. */ 
  585. public function get_download_limit( $context = 'view' ) { 
  586. return $this->get_prop( 'download_limit', $context ); 
  587.  
  588. /** 
  589. * Get main image ID. 
  590. * 
  591. * @since 3.0.0 
  592. * @param string $context 
  593. * @return string 
  594. */ 
  595. public function get_image_id( $context = 'view' ) { 
  596. return $this->get_prop( 'image_id', $context ); 
  597.  
  598. /** 
  599. * Get rating count. 
  600. * @param string $context 
  601. * @return array of counts 
  602. */ 
  603. public function get_rating_counts( $context = 'view' ) { 
  604. return $this->get_prop( 'rating_counts', $context ); 
  605.  
  606. /** 
  607. * Get average rating. 
  608. * @param string $context 
  609. * @return float 
  610. */ 
  611. public function get_average_rating( $context = 'view' ) { 
  612. return $this->get_prop( 'average_rating', $context ); 
  613.  
  614. /** 
  615. * Get review count. 
  616. * @param string $context 
  617. * @return int 
  618. */ 
  619. public function get_review_count( $context = 'view' ) { 
  620. return $this->get_prop( 'review_count', $context ); 
  621.  
  622. /** 
  623. |-------------------------------------------------------------------------- 
  624. | Setters 
  625. |-------------------------------------------------------------------------- 
  626. | 
  627. | Functions for setting product data. These should not update anything in the 
  628. | database itself and should only change what is stored in the class 
  629. | object. 
  630. */ 
  631.  
  632. /** 
  633. * Set product name. 
  634. * 
  635. * @since 3.0.0 
  636. * @param string $name Product name. 
  637. */ 
  638. public function set_name( $name ) { 
  639. $this->set_prop( 'name', $name ); 
  640.  
  641. /** 
  642. * Set product slug. 
  643. * 
  644. * @since 3.0.0 
  645. * @param string $slug Product slug. 
  646. */ 
  647. public function set_slug( $slug ) { 
  648. $this->set_prop( 'slug', $slug ); 
  649.  
  650. /** 
  651. * Set product created date. 
  652. * 
  653. * @since 3.0.0 
  654. * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date. 
  655. */ 
  656. public function set_date_created( $date = null ) { 
  657. $this->set_date_prop( 'date_created', $date ); 
  658.  
  659. /** 
  660. * Set product modified date. 
  661. * 
  662. * @since 3.0.0 
  663. * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date. 
  664. */ 
  665. public function set_date_modified( $date = null ) { 
  666. $this->set_date_prop( 'date_modified', $date ); 
  667.  
  668. /** 
  669. * Set product status. 
  670. * 
  671. * @since 3.0.0 
  672. * @param string $status Product status. 
  673. */ 
  674. public function set_status( $status ) { 
  675. $this->set_prop( 'status', $status ); 
  676.  
  677. /** 
  678. * Set if the product is featured. 
  679. * 
  680. * @since 3.0.0 
  681. * @param bool|string 
  682. */ 
  683. public function set_featured( $featured ) { 
  684. $this->set_prop( 'featured', wc_string_to_bool( $featured ) ); 
  685.  
  686. /** 
  687. * Set catalog visibility. 
  688. * 
  689. * @since 3.0.0 
  690. * @throws WC_Data_Exception 
  691. * @param string $visibility Options: 'hidden', 'visible', 'search' and 'catalog'. 
  692. */ 
  693. public function set_catalog_visibility( $visibility ) { 
  694. $options = array_keys( wc_get_product_visibility_options() ); 
  695. if ( ! in_array( $visibility, $options, true ) ) { 
  696. $this->error( 'product_invalid_catalog_visibility', __( 'Invalid catalog visibility option.', 'woocommerce' ) ); 
  697. $this->set_prop( 'catalog_visibility', $visibility ); 
  698.  
  699. /** 
  700. * Set product description. 
  701. * 
  702. * @since 3.0.0 
  703. * @param string $description Product description. 
  704. */ 
  705. public function set_description( $description ) { 
  706. $this->set_prop( 'description', $description ); 
  707.  
  708. /** 
  709. * Set product short description. 
  710. * 
  711. * @since 3.0.0 
  712. * @param string $short_description Product short description. 
  713. */ 
  714. public function set_short_description( $short_description ) { 
  715. $this->set_prop( 'short_description', $short_description ); 
  716.  
  717. /** 
  718. * Set SKU. 
  719. * 
  720. * @since 3.0.0 
  721. * @throws WC_Data_Exception 
  722. * @param string $sku Product SKU. 
  723. */ 
  724. public function set_sku( $sku ) { 
  725. $sku = (string) $sku; 
  726. if ( $this->get_object_read() && ! empty( $sku ) && ! wc_product_has_unique_sku( $this->get_id(), $sku ) ) { 
  727. $sku_found = wc_get_product_id_by_sku( $sku ); 
  728.  
  729. $this->error( 'product_invalid_sku', __( 'Invalid or duplicated SKU.', 'woocommerce' ), 400, array( 'resource_id' => $sku_found ) ); 
  730. $this->set_prop( 'sku', $sku ); 
  731.  
  732. /** 
  733. * Set the product's active price. 
  734. * 
  735. * @param string $price Price. 
  736. */ 
  737. public function set_price( $price ) { 
  738. $this->set_prop( 'price', wc_format_decimal( $price ) ); 
  739.  
  740. /** 
  741. * Set the product's regular price. 
  742. * 
  743. * @since 3.0.0 
  744. * @param string $price Regular price. 
  745. */ 
  746. public function set_regular_price( $price ) { 
  747. $this->set_prop( 'regular_price', wc_format_decimal( $price ) ); 
  748.  
  749. /** 
  750. * Set the product's sale price. 
  751. * 
  752. * @since 3.0.0 
  753. * @param string $price sale price. 
  754. */ 
  755. public function set_sale_price( $price ) { 
  756. $this->set_prop( 'sale_price', wc_format_decimal( $price ) ); 
  757.  
  758. /** 
  759. * Set date on sale from. 
  760. * 
  761. * @since 3.0.0 
  762. * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date. 
  763. */ 
  764. public function set_date_on_sale_from( $date = null ) { 
  765. $this->set_date_prop( 'date_on_sale_from', $date ); 
  766.  
  767. /** 
  768. * Set date on sale to. 
  769. * 
  770. * @since 3.0.0 
  771. * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date. 
  772. */ 
  773. public function set_date_on_sale_to( $date = null ) { 
  774. $this->set_date_prop( 'date_on_sale_to', $date ); 
  775.  
  776. /** 
  777. * Set number total of sales. 
  778. * 
  779. * @since 3.0.0 
  780. * @param int $total Total of sales. 
  781. */ 
  782. public function set_total_sales( $total ) { 
  783. $this->set_prop( 'total_sales', absint( $total ) ); 
  784.  
  785. /** 
  786. * Set the tax status. 
  787. * 
  788. * @since 3.0.0 
  789. * @throws WC_Data_Exception 
  790. * @param string $status Tax status. 
  791. */ 
  792. public function set_tax_status( $status ) { 
  793. $options = array( 
  794. 'taxable',  
  795. 'shipping',  
  796. 'none',  
  797. ); 
  798.  
  799. // Set default if empty. 
  800. if ( empty( $status ) ) { 
  801. $status = 'taxable'; 
  802.  
  803. if ( ! in_array( $status, $options, true ) ) { 
  804. $this->error( 'product_invalid_tax_status', __( 'Invalid product tax status.', 'woocommerce' ) ); 
  805.  
  806. $this->set_prop( 'tax_status', $status ); 
  807.  
  808. /** 
  809. * Set the tax class. 
  810. * 
  811. * @since 3.0.0 
  812. * @param string $class Tax class. 
  813. */ 
  814. public function set_tax_class( $class ) { 
  815. $class = sanitize_title( $class ); 
  816. $class = 'standard' === $class ? '' : $class; 
  817. $this->set_prop( 'tax_class', $class ); 
  818.  
  819. /** 
  820. * Set if product manage stock. 
  821. * 
  822. * @since 3.0.0 
  823. * @param bool 
  824. */ 
  825. public function set_manage_stock( $manage_stock ) { 
  826. $this->set_prop( 'manage_stock', wc_string_to_bool( $manage_stock ) ); 
  827.  
  828. /** 
  829. * Set number of items available for sale. 
  830. * 
  831. * @since 3.0.0 
  832. * @param float|null $quantity Stock quantity. 
  833. */ 
  834. public function set_stock_quantity( $quantity ) { 
  835. $this->set_prop( 'stock_quantity', '' !== $quantity ? wc_stock_amount( $quantity ) : null ); 
  836.  
  837. /** 
  838. * Set stock status. 
  839. * 
  840. * @param string $status New status. 
  841. */ 
  842. public function set_stock_status( $status = '' ) { 
  843. $this->set_prop( 'stock_status', 'outofstock' === $status ? 'outofstock' : 'instock' ); 
  844.  
  845. /** 
  846. * Set backorders. 
  847. * 
  848. * @since 3.0.0 
  849. * @param string $backorders Options: 'yes', 'no' or 'notify'. 
  850. */ 
  851. public function set_backorders( $backorders ) { 
  852. $this->set_prop( 'backorders', $backorders ); 
  853.  
  854. /** 
  855. * Set if should be sold individually. 
  856. * 
  857. * @since 3.0.0 
  858. * @param bool 
  859. */ 
  860. public function set_sold_individually( $sold_individually ) { 
  861. $this->set_prop( 'sold_individually', wc_string_to_bool( $sold_individually ) ); 
  862.  
  863. /** 
  864. * Set the product's weight. 
  865. * 
  866. * @since 3.0.0 
  867. * @param float|string $weight Total weight. 
  868. */ 
  869. public function set_weight( $weight ) { 
  870. $this->set_prop( 'weight', '' === $weight ? '' : wc_format_decimal( $weight ) ); 
  871.  
  872. /** 
  873. * Set the product length. 
  874. * 
  875. * @since 3.0.0 
  876. * @param float|string $length Total length. 
  877. */ 
  878. public function set_length( $length ) { 
  879. $this->set_prop( 'length', '' === $length ? '' : wc_format_decimal( $length ) ); 
  880.  
  881. /** 
  882. * Set the product width. 
  883. * 
  884. * @since 3.0.0 
  885. * @param float|string $width Total width. 
  886. */ 
  887. public function set_width( $width ) { 
  888. $this->set_prop( 'width', '' === $width ? '' : wc_format_decimal( $width ) ); 
  889.  
  890. /** 
  891. * Set the product height. 
  892. * 
  893. * @since 3.0.0 
  894. * @param float|string $height Total height. 
  895. */ 
  896. public function set_height( $height ) { 
  897. $this->set_prop( 'height', '' === $height ? '' : wc_format_decimal( $height ) ); 
  898.  
  899. /** 
  900. * Set upsell IDs. 
  901. * 
  902. * @since 3.0.0 
  903. * @param array $upsell_ids IDs from the up-sell products. 
  904. */ 
  905. public function set_upsell_ids( $upsell_ids ) { 
  906. $this->set_prop( 'upsell_ids', array_filter( (array) $upsell_ids ) ); 
  907.  
  908. /** 
  909. * Set crosssell IDs. 
  910. * 
  911. * @since 3.0.0 
  912. * @param array $cross_sell_ids IDs from the cross-sell products. 
  913. */ 
  914. public function set_cross_sell_ids( $cross_sell_ids ) { 
  915. $this->set_prop( 'cross_sell_ids', array_filter( (array) $cross_sell_ids ) ); 
  916.  
  917. /** 
  918. * Set parent ID. 
  919. * 
  920. * @since 3.0.0 
  921. * @param int $parent_id Product parent ID. 
  922. */ 
  923. public function set_parent_id( $parent_id ) { 
  924. $this->set_prop( 'parent_id', absint( $parent_id ) ); 
  925.  
  926. /** 
  927. * Set if reviews is allowed. 
  928. * 
  929. * @since 3.0.0 
  930. * @param bool $reviews_allowed Reviews allowed or not. 
  931. */ 
  932. public function set_reviews_allowed( $reviews_allowed ) { 
  933. $this->set_prop( 'reviews_allowed', wc_string_to_bool( $reviews_allowed ) ); 
  934.  
  935. /** 
  936. * Set purchase note. 
  937. * 
  938. * @since 3.0.0 
  939. * @param string $purchase_note Purchase note. 
  940. */ 
  941. public function set_purchase_note( $purchase_note ) { 
  942. $this->set_prop( 'purchase_note', $purchase_note ); 
  943.  
  944. /** 
  945. * Set product attributes. 
  946. * 
  947. * Attributes are made up of: 
  948. * id - 0 for product level attributes. ID for global attributes. 
  949. * name - Attribute name. 
  950. * options - attribute value or array of term ids/names. 
  951. * position - integer sort order. 
  952. * visible - If visible on frontend. 
  953. * variation - If used for variations. 
  954. * Indexed by unqiue key to allow clearing old ones after a set. 
  955. * 
  956. * @since 3.0.0 
  957. * @param array $raw_attributes Array of WC_Product_Attribute objects. 
  958. */ 
  959. public function set_attributes( $raw_attributes ) { 
  960. $attributes = array_fill_keys( array_keys( $this->get_attributes( 'edit' ) ), null ); 
  961. foreach ( $raw_attributes as $attribute ) { 
  962. if ( is_a( $attribute, 'WC_Product_Attribute' ) ) { 
  963. $attributes[ sanitize_title( $attribute->get_name() ) ] = $attribute; 
  964.  
  965. uasort( $attributes, 'wc_product_attribute_uasort_comparison' ); 
  966. $this->set_prop( 'attributes', $attributes ); 
  967.  
  968. /** 
  969. * Set default attributes. 
  970. * 
  971. * @since 3.0.0 
  972. * @param array $default_attributes List of default attributes. 
  973. */ 
  974. public function set_default_attributes( $default_attributes ) { 
  975. $this->set_prop( 'default_attributes', array_filter( (array) $default_attributes ) ); 
  976.  
  977. /** 
  978. * Set menu order. 
  979. * 
  980. * @since 3.0.0 
  981. * @param int $menu_order Menu order. 
  982. */ 
  983. public function set_menu_order( $menu_order ) { 
  984. $this->set_prop( 'menu_order', intval( $menu_order ) ); 
  985.  
  986. /** 
  987. * Set the product categories. 
  988. * 
  989. * @since 3.0.0 
  990. * @param array $term_ids List of terms IDs. 
  991. */ 
  992. public function set_category_ids( $term_ids ) { 
  993. $this->set_prop( 'category_ids', array_unique( array_map( 'intval', $term_ids ) ) ); 
  994.  
  995. /** 
  996. * Set the product tags. 
  997. * 
  998. * @since 3.0.0 
  999. * @param array $term_ids List of terms IDs. 
  1000. */ 
  1001. public function set_tag_ids( $term_ids ) { 
  1002. $this->set_prop( 'tag_ids', array_unique( array_map( 'intval', $term_ids ) ) ); 
  1003.  
  1004. /** 
  1005. * Set if the product is virtual. 
  1006. * 
  1007. * @since 3.0.0 
  1008. * @param bool|string 
  1009. */ 
  1010. public function set_virtual( $virtual ) { 
  1011. $this->set_prop( 'virtual', wc_string_to_bool( $virtual ) ); 
  1012.  
  1013. /** 
  1014. * Set shipping class ID. 
  1015. * 
  1016. * @since 3.0.0 
  1017. * @param int 
  1018. */ 
  1019. public function set_shipping_class_id( $id ) { 
  1020. $this->set_prop( 'shipping_class_id', absint( $id ) ); 
  1021.  
  1022. /** 
  1023. * Set if the product is downloadable. 
  1024. * 
  1025. * @since 3.0.0 
  1026. * @param bool|string 
  1027. */ 
  1028. public function set_downloadable( $downloadable ) { 
  1029. $this->set_prop( 'downloadable', wc_string_to_bool( $downloadable ) ); 
  1030.  
  1031. /** 
  1032. * Set downloads. 
  1033. * 
  1034. * @since 3.0.0 
  1035. * @param $downloads_array array of WC_Product_Download objects or arrays. 
  1036. */ 
  1037. public function set_downloads( $downloads_array ) { 
  1038. $downloads = array(); 
  1039. $errors = array(); 
  1040.  
  1041. foreach ( $downloads_array as $download ) { 
  1042. if ( is_a( $download, 'WC_Product_Download' ) ) { 
  1043. $download_object = $download; 
  1044. } else { 
  1045. $download_object = new WC_Product_Download(); 
  1046. $download['previous_hash'] = isset( $download['previous_hash'] ) ? $download['previous_hash'] : ''; 
  1047. $file_hash = apply_filters( 'woocommerce_downloadable_file_hash', md5( $download['file'] ), $this->get_id(), $download['name'], $download['file'], $download['previous_hash'] ); 
  1048.  
  1049. $download_object->set_id( $file_hash ); 
  1050. $download_object->set_name( $download['name'] ); 
  1051. $download_object->set_file( $download['file'] ); 
  1052. $download_object->set_previous_hash( $download['previous_hash'] ); 
  1053.  
  1054. // Validate the file extension 
  1055. if ( ! $download_object->is_allowed_filetype() ) { 
  1056. if ( $this->get_object_read() ) { 
  1057. $errors[] = sprintf( __( 'The downloadable file %1$s cannot be used as it does not have an allowed file type. Allowed types include: %2$s', 'woocommerce' ), '<code>' . basename( $download_object->get_file() ) . '</code>', '<code>' . implode( ', ', array_keys( $download_object->get_allowed_mime_types() ) ) . '</code>' ); 
  1058. continue; 
  1059.  
  1060. // Validate the file exists. 
  1061. if ( ! $download_object->file_exists() ) { 
  1062. if ( $this->get_object_read() ) { 
  1063. $errors[] = sprintf( __( 'The downloadable file %s cannot be used as it does not exist on the server.', 'woocommerce' ), '<code>' . $download_object->get_file() . '</code>' ); 
  1064. continue; 
  1065.  
  1066. $downloads[ $download_object->get_id() ] = $download_object; 
  1067.  
  1068. if ( $errors ) { 
  1069. $this->error( 'product_invalid_download', $errors[0] ); 
  1070.  
  1071. $this->set_prop( 'downloads', $downloads ); 
  1072.  
  1073. /** 
  1074. * Set download limit. 
  1075. * 
  1076. * @since 3.0.0 
  1077. * @param int $download_limit 
  1078. */ 
  1079. public function set_download_limit( $download_limit ) { 
  1080. $this->set_prop( 'download_limit', -1 === (int) $download_limit || '' === $download_limit ? -1 : absint( $download_limit ) ); 
  1081.  
  1082. /** 
  1083. * Set download expiry. 
  1084. * 
  1085. * @since 3.0.0 
  1086. * @param int $download_expiry 
  1087. */ 
  1088. public function set_download_expiry( $download_expiry ) { 
  1089. $this->set_prop( 'download_expiry', -1 === (int) $download_expiry || '' === $download_expiry ? -1 : absint( $download_expiry ) ); 
  1090.  
  1091. /** 
  1092. * Set gallery attachment ids. 
  1093. * 
  1094. * @since 3.0.0 
  1095. * @param array $image_ids 
  1096. */ 
  1097. public function set_gallery_image_ids( $image_ids ) { 
  1098. $image_ids = wp_parse_id_list( $image_ids ); 
  1099.  
  1100. if ( $this->get_object_read() ) { 
  1101. $image_ids = array_filter( $image_ids, 'wp_attachment_is_image' ); 
  1102.  
  1103. $this->set_prop( 'gallery_image_ids', $image_ids ); 
  1104.  
  1105. /** 
  1106. * Set main image ID. 
  1107. * 
  1108. * @since 3.0.0 
  1109. * @param int $image_id 
  1110. */ 
  1111. public function set_image_id( $image_id = '' ) { 
  1112. $this->set_prop( 'image_id', $image_id ); 
  1113.  
  1114. /** 
  1115. * Set rating counts. Read only. 
  1116. * @param array $counts 
  1117. */ 
  1118. public function set_rating_counts( $counts ) { 
  1119. $this->set_prop( 'rating_counts', array_filter( array_map( 'absint', (array) $counts ) ) ); 
  1120.  
  1121. /** 
  1122. * Set average rating. Read only. 
  1123. * @param float $average 
  1124. */ 
  1125. public function set_average_rating( $average ) { 
  1126. $this->set_prop( 'average_rating', wc_format_decimal( $average ) ); 
  1127.  
  1128. /** 
  1129. * Set review count. Read only. 
  1130. * @param int $count 
  1131. */ 
  1132. public function set_review_count( $count ) { 
  1133. $this->set_prop( 'review_count', absint( $count ) ); 
  1134.  
  1135. /** 
  1136. |-------------------------------------------------------------------------- 
  1137. | Other Methods 
  1138. |-------------------------------------------------------------------------- 
  1139. */ 
  1140.  
  1141. /** 
  1142. * Ensure properties are set correctly before save. 
  1143. * @since 3.0.0 
  1144. */ 
  1145. public function validate_props() { 
  1146. // Before updating, ensure stock props are all aligned. Qty and backorders are not needed if not stock managed. 
  1147. if ( ! $this->get_manage_stock() ) { 
  1148. $this->set_stock_quantity( '' ); 
  1149. $this->set_backorders( 'no' ); 
  1150.  
  1151. // If we are stock managing and we don't have stock, force out of stock status. 
  1152. } elseif ( $this->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount' ) && 'no' === $this->get_backorders() ) { 
  1153. $this->set_stock_status( 'outofstock' ); 
  1154.  
  1155. // If the stock level is changing and we do now have enough, force in stock status. 
  1156. } elseif ( $this->get_stock_quantity() > get_option( 'woocommerce_notify_no_stock_amount' ) && array_key_exists( 'stock_quantity', $this->get_changes() ) ) { 
  1157. $this->set_stock_status( 'instock' ); 
  1158.  
  1159. /** 
  1160. * Save data (either create or update depending on if we are working on an existing product). 
  1161. * 
  1162. * @since 3.0.0 
  1163. */ 
  1164. public function save() { 
  1165. $this->validate_props(); 
  1166.  
  1167. if ( $this->data_store ) { 
  1168. // Trigger action before saving to the DB. Use a pointer to adjust object props before save. 
  1169. do_action( 'woocommerce_before_' . $this->object_type . '_object_save', $this, $this->data_store ); 
  1170.  
  1171. if ( $this->get_id() ) { 
  1172. $this->data_store->update( $this ); 
  1173. } else { 
  1174. $this->data_store->create( $this ); 
  1175. if ( $this->get_parent_id() ) { 
  1176. wc_deferred_product_sync( $this->get_parent_id() ); 
  1177. return $this->get_id(); 
  1178.  
  1179. /** 
  1180. |-------------------------------------------------------------------------- 
  1181. | Conditionals 
  1182. |-------------------------------------------------------------------------- 
  1183. */ 
  1184.  
  1185. /** 
  1186. * Check if a product supports a given feature. 
  1187. * 
  1188. * Product classes should override this to declare support (or lack of support) for a feature. 
  1189. * 
  1190. * @param string $feature string The name of a feature to test support for. 
  1191. * @return bool True if the product supports the feature, false otherwise. 
  1192. * @since 2.5.0 
  1193. */ 
  1194. public function supports( $feature ) { 
  1195. return apply_filters( 'woocommerce_product_supports', in_array( $feature, $this->supports ) ? true : false, $feature, $this ); 
  1196.  
  1197. /** 
  1198. * Returns whether or not the product post exists. 
  1199. * 
  1200. * @return bool 
  1201. */ 
  1202. public function exists() { 
  1203. return false !== $this->get_status(); 
  1204.  
  1205. /** 
  1206. * Checks the product type. 
  1207. * 
  1208. * Backwards compat with downloadable/virtual. 
  1209. * 
  1210. * @param string $type Array or string of types 
  1211. * @return bool 
  1212. */ 
  1213. public function is_type( $type ) { 
  1214. return ( $this->get_type() === $type || ( is_array( $type ) && in_array( $this->get_type(), $type ) ) ); 
  1215.  
  1216. /** 
  1217. * Checks if a product is downloadable. 
  1218. * 
  1219. * @return bool 
  1220. */ 
  1221. public function is_downloadable() { 
  1222. return apply_filters( 'woocommerce_is_downloadable', true === $this->get_downloadable(), $this ); 
  1223.  
  1224. /** 
  1225. * Checks if a product is virtual (has no shipping). 
  1226. * 
  1227. * @return bool 
  1228. */ 
  1229. public function is_virtual() { 
  1230. return apply_filters( 'woocommerce_is_virtual', true === $this->get_virtual(), $this ); 
  1231.  
  1232. /** 
  1233. * Returns whether or not the product is featured. 
  1234. * 
  1235. * @return bool 
  1236. */ 
  1237. public function is_featured() { 
  1238. return true === $this->get_featured(); 
  1239.  
  1240. /** 
  1241. * Check if a product is sold individually (no quantities). 
  1242. * 
  1243. * @return bool 
  1244. */ 
  1245. public function is_sold_individually() { 
  1246. return apply_filters( 'woocommerce_is_sold_individually', true === $this->get_sold_individually(), $this ); 
  1247.  
  1248. /** 
  1249. * Returns whether or not the product is visible in the catalog. 
  1250. * 
  1251. * @return bool 
  1252. */ 
  1253. public function is_visible() { 
  1254. $visible = 'visible' === $this->get_catalog_visibility() || ( is_search() && 'search' === $this->get_catalog_visibility() ) || ( ! is_search() && 'catalog' === $this->get_catalog_visibility() ); 
  1255.  
  1256. if ( 'publish' !== $this->get_status() && ! current_user_can( 'edit_post', $this->get_id() ) ) { 
  1257. $visible = false; 
  1258.  
  1259. if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $this->is_in_stock() ) { 
  1260. $visible = false; 
  1261.  
  1262. return apply_filters( 'woocommerce_product_is_visible', $visible, $this->get_id() ); 
  1263.  
  1264. /** 
  1265. * Returns false if the product cannot be bought. 
  1266. * 
  1267. * @return bool 
  1268. */ 
  1269. public function is_purchasable() { 
  1270. return apply_filters( 'woocommerce_is_purchasable', $this->exists() && ( 'publish' === $this->get_status() || current_user_can( 'edit_post', $this->get_id() ) ) && '' !== $this->get_price(), $this ); 
  1271.  
  1272. /** 
  1273. * Returns whether or not the product is on sale. 
  1274. * 
  1275. * @param string $context What the value is for. Valid values are view and edit. 
  1276. * @return bool 
  1277. */ 
  1278. public function is_on_sale( $context = 'view' ) { 
  1279. if ( '' !== (string) $this->get_sale_price( $context ) && $this->get_regular_price( $context ) > $this->get_sale_price( $context ) ) { 
  1280. $on_sale = true; 
  1281.  
  1282. if ( $this->get_date_on_sale_from( $context ) && $this->get_date_on_sale_from( $context )->getTimestamp() > current_time( 'timestamp', true ) ) { 
  1283. $on_sale = false; 
  1284.  
  1285. if ( $this->get_date_on_sale_to( $context ) && $this->get_date_on_sale_to( $context )->getTimestamp() < current_time( 'timestamp', true ) ) { 
  1286. $on_sale = false; 
  1287. } else { 
  1288. $on_sale = false; 
  1289. return 'view' === $context ? apply_filters( 'woocommerce_product_is_on_sale', $on_sale, $this ) : $on_sale; 
  1290.  
  1291. /** 
  1292. * Returns whether or not the product has dimensions set. 
  1293. * 
  1294. * @return bool 
  1295. */ 
  1296. public function has_dimensions() { 
  1297. return ( $this->get_length() || $this->get_height() || $this->get_width() ) && ! $this->get_virtual(); 
  1298.  
  1299. /** 
  1300. * Returns whether or not the product has weight set. 
  1301. * 
  1302. * @return bool 
  1303. */ 
  1304. public function has_weight() { 
  1305. return $this->get_weight() && ! $this->get_virtual(); 
  1306.  
  1307. /** 
  1308. * Returns whether or not the product is in stock. 
  1309. * 
  1310. * @return bool 
  1311. */ 
  1312. public function is_in_stock() { 
  1313. return apply_filters( 'woocommerce_product_is_in_stock', 'instock' === $this->get_stock_status(), $this ); 
  1314.  
  1315. /** 
  1316. * Checks if a product needs shipping. 
  1317. * 
  1318. * @return bool 
  1319. */ 
  1320. public function needs_shipping() { 
  1321. return apply_filters( 'woocommerce_product_needs_shipping', ! $this->is_virtual(), $this ); 
  1322.  
  1323. /** 
  1324. * Returns whether or not the product is taxable. 
  1325. * 
  1326. * @return bool 
  1327. */ 
  1328. public function is_taxable() { 
  1329. return apply_filters( 'woocommerce_product_is_taxable', $this->get_tax_status() === 'taxable' && wc_tax_enabled(), $this ); 
  1330.  
  1331. /** 
  1332. * Returns whether or not the product shipping is taxable. 
  1333. * 
  1334. * @return bool 
  1335. */ 
  1336. public function is_shipping_taxable() { 
  1337. return $this->get_tax_status() === 'taxable' || $this->get_tax_status() === 'shipping'; 
  1338.  
  1339. /** 
  1340. * Returns whether or not the product is stock managed. 
  1341. * 
  1342. * @return bool 
  1343. */ 
  1344. public function managing_stock() { 
  1345. if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { 
  1346. return $this->get_manage_stock(); 
  1347. return false; 
  1348.  
  1349. /** 
  1350. * Returns whether or not the product can be backordered. 
  1351. * 
  1352. * @return bool 
  1353. */ 
  1354. public function backorders_allowed() { 
  1355. return apply_filters( 'woocommerce_product_backorders_allowed', ( 'yes' === $this->get_backorders() || 'notify' === $this->get_backorders() ), $this->get_id(), $this ); 
  1356.  
  1357. /** 
  1358. * Returns whether or not the product needs to notify the customer on backorder. 
  1359. * 
  1360. * @return bool 
  1361. */ 
  1362. public function backorders_require_notification() { 
  1363. return apply_filters( 'woocommerce_product_backorders_require_notification', ( $this->managing_stock() && 'notify' === $this->get_backorders() ), $this ); 
  1364.  
  1365. /** 
  1366. * Check if a product is on backorder. 
  1367. * 
  1368. * @param int $qty_in_cart (default: 0) 
  1369. * @return bool 
  1370. */ 
  1371. public function is_on_backorder( $qty_in_cart = 0 ) { 
  1372. return $this->managing_stock() && $this->backorders_allowed() && ( $this->get_stock_quantity() - $qty_in_cart ) < 0 ? true : false; 
  1373.  
  1374. /** 
  1375. * Returns whether or not the product has enough stock for the order. 
  1376. * 
  1377. * @param mixed $quantity 
  1378. * @return bool 
  1379. */ 
  1380. public function has_enough_stock( $quantity ) { 
  1381. return ! $this->managing_stock() || $this->backorders_allowed() || $this->get_stock_quantity() >= $quantity; 
  1382.  
  1383. /** 
  1384. * Returns whether or not the product has any visible attributes. 
  1385. * 
  1386. * @return boolean 
  1387. */ 
  1388. public function has_attributes() { 
  1389. foreach ( $this->get_attributes() as $attribute ) { 
  1390. if ( $attribute->get_visible() ) { 
  1391. return true; 
  1392. return false; 
  1393.  
  1394. /** 
  1395. * Returns whether or not the product has any child product. 
  1396. * 
  1397. * @return bool 
  1398. */ 
  1399. public function has_child() { 
  1400. return 0 < count( $this->get_children() ); 
  1401.  
  1402. /** 
  1403. * Does a child have dimensions? 
  1404. * 
  1405. * @since 3.0.0 
  1406. * @return bool 
  1407. */ 
  1408. public function child_has_dimensions() { 
  1409. return false; 
  1410.  
  1411. /** 
  1412. * Does a child have a weight? 
  1413. * 
  1414. * @since 3.0.0 
  1415. * @return boolean 
  1416. */ 
  1417. public function child_has_weight() { 
  1418. return false; 
  1419.  
  1420. /** 
  1421. * Check if downloadable product has a file attached. 
  1422. * 
  1423. * @since 1.6.2 
  1424. * 
  1425. * @param string $download_id file identifier 
  1426. * @return bool Whether downloadable product has a file attached. 
  1427. */ 
  1428. public function has_file( $download_id = '' ) { 
  1429. return $this->is_downloadable() && $this->get_file( $download_id ); 
  1430.  
  1431. /** 
  1432. * Returns whether or not the product has additonal options that need 
  1433. * selecting before adding to cart. 
  1434. * 
  1435. * @since 3.0.0 
  1436. * @return boolean 
  1437. */ 
  1438. public function has_options() { 
  1439. return false; 
  1440.  
  1441. /** 
  1442. |-------------------------------------------------------------------------- 
  1443. | Non-CRUD Getters 
  1444. |-------------------------------------------------------------------------- 
  1445. */ 
  1446.  
  1447. /** 
  1448. * Get the product's title. For products this is the product name. 
  1449. * 
  1450. * @return string 
  1451. */ 
  1452. public function get_title() { 
  1453. return apply_filters( 'woocommerce_product_title', $this->get_name(), $this ); 
  1454.  
  1455. /** 
  1456. * Product permalink. 
  1457. * @return string 
  1458. */ 
  1459. public function get_permalink() { 
  1460. return get_permalink( $this->get_id() ); 
  1461.  
  1462. /** 
  1463. * Returns the children IDs if applicable. Overridden by child classes. 
  1464. * 
  1465. * @return array of IDs 
  1466. */ 
  1467. public function get_children() { 
  1468. return array(); 
  1469.  
  1470. /** 
  1471. * If the stock level comes from another product ID, this should be modified. 
  1472. * @since 3.0.0 
  1473. * @return int 
  1474. */ 
  1475. public function get_stock_managed_by_id() { 
  1476. return $this->get_id(); 
  1477.  
  1478. /** 
  1479. * Returns the price in html format. 
  1480. * @return string 
  1481. */ 
  1482. public function get_price_html( $deprecated = '' ) { 
  1483. if ( '' === $this->get_price() ) { 
  1484. $price = apply_filters( 'woocommerce_empty_price_html', '', $this ); 
  1485. } elseif ( $this->is_on_sale() ) { 
  1486. $price = wc_format_sale_price( wc_get_price_to_display( $this, array( 'price' => $this->get_regular_price() ) ), wc_get_price_to_display( $this ) ) . $this->get_price_suffix(); 
  1487. } else { 
  1488. $price = wc_price( wc_get_price_to_display( $this ) ) . $this->get_price_suffix(); 
  1489.  
  1490. return apply_filters( 'woocommerce_get_price_html', $price, $this ); 
  1491.  
  1492. /** 
  1493. * Get product name with SKU or ID. Used within admin. 
  1494. * 
  1495. * @return string Formatted product name 
  1496. */ 
  1497. public function get_formatted_name() { 
  1498. if ( $this->get_sku() ) { 
  1499. $identifier = $this->get_sku(); 
  1500. } else { 
  1501. $identifier = '#' . $this->get_id(); 
  1502. return sprintf( '%2$s (%1$s)', $identifier, $this->get_name() ); 
  1503.  
  1504. /** 
  1505. * Get min quantity which can be purchased at once. 
  1506. * 
  1507. * @since 3.0.0 
  1508. * @return int 
  1509. */ 
  1510. public function get_min_purchase_quantity() { 
  1511. return 1; 
  1512.  
  1513. /** 
  1514. * Get max quantity which can be purchased at once. 
  1515. * 
  1516. * @since 3.0.0 
  1517. * @return int Quantity or -1 if unlimited. 
  1518. */ 
  1519. public function get_max_purchase_quantity() { 
  1520. return $this->is_sold_individually() ? 1 : ( $this->backorders_allowed() || ! $this->get_manage_stock() ? -1 : $this->get_stock_quantity() ); 
  1521.  
  1522. /** 
  1523. * Get the add to url used mainly in loops. 
  1524. * 
  1525. * @return string 
  1526. */ 
  1527. public function add_to_cart_url() { 
  1528. return apply_filters( 'woocommerce_product_add_to_cart_url', $this->get_permalink(), $this ); 
  1529.  
  1530. /** 
  1531. * Get the add to cart button text for the single page. 
  1532. * 
  1533. * @return string 
  1534. */ 
  1535. public function single_add_to_cart_text() { 
  1536. return apply_filters( 'woocommerce_product_single_add_to_cart_text', __( 'Add to cart', 'woocommerce' ), $this ); 
  1537.  
  1538. /** 
  1539. * Get the add to cart button text. 
  1540. * 
  1541. * @return string 
  1542. */ 
  1543. public function add_to_cart_text() { 
  1544. return apply_filters( 'woocommerce_product_add_to_cart_text', __( 'Read more', 'woocommerce' ), $this ); 
  1545.  
  1546. /** 
  1547. * Returns the main product image. 
  1548. * 
  1549. * @param string $size (default: 'shop_thumbnail') 
  1550. * @param array $attr 
  1551. * @param bool True to return $placeholder if no image is found, or false to return an empty string. 
  1552. * @return string 
  1553. */ 
  1554. public function get_image( $size = 'shop_thumbnail', $attr = array(), $placeholder = true ) { 
  1555. if ( has_post_thumbnail( $this->get_id() ) ) { 
  1556. $image = get_the_post_thumbnail( $this->get_id(), $size, $attr ); 
  1557. } elseif ( ( $parent_id = wp_get_post_parent_id( $this->get_id() ) ) && has_post_thumbnail( $parent_id ) ) { 
  1558. $image = get_the_post_thumbnail( $parent_id, $size, $attr ); 
  1559. } elseif ( $placeholder ) { 
  1560. $image = wc_placeholder_img( $size ); 
  1561. } else { 
  1562. $image = ''; 
  1563. return str_replace( array( 'https://', 'http://' ), '//', $image ); 
  1564.  
  1565. /** 
  1566. * Returns the product shipping class SLUG. 
  1567. * 
  1568. * @return string 
  1569. */ 
  1570. public function get_shipping_class() { 
  1571. if ( $class_id = $this->get_shipping_class_id() ) { 
  1572. $term = get_term_by( 'id', $class_id, 'product_shipping_class' ); 
  1573.  
  1574. if ( $term && ! is_wp_error( $term ) ) { 
  1575. return $term->slug; 
  1576. return ''; 
  1577.  
  1578. /** 
  1579. * Returns a single product attribute as a string. 
  1580. * @param string $attribute to get. 
  1581. * @return string 
  1582. */ 
  1583. public function get_attribute( $attribute ) { 
  1584. $attributes = $this->get_attributes(); 
  1585. $attribute = sanitize_title( $attribute ); 
  1586.  
  1587. if ( isset( $attributes[ $attribute ] ) ) { 
  1588. $attribute_object = $attributes[ $attribute ]; 
  1589. } elseif ( isset( $attributes[ 'pa_' . $attribute ] ) ) { 
  1590. $attribute_object = $attributes[ 'pa_' . $attribute ]; 
  1591. } else { 
  1592. return ''; 
  1593. return $attribute_object->is_taxonomy() ? implode( ', ', wc_get_product_terms( $this->get_id(), $attribute_object->get_name(), array( 'fields' => 'names' ) ) ) : wc_implode_text_attributes( $attribute_object->get_options() ); 
  1594.  
  1595. /** 
  1596. * Get the total amount (COUNT) of ratings, or just the count for one rating e.g. number of 5 star ratings. 
  1597. * @param int $value Optional. Rating value to get the count for. By default returns the count of all rating values. 
  1598. * @return int 
  1599. */ 
  1600. public function get_rating_count( $value = null ) { 
  1601. $counts = $this->get_rating_counts(); 
  1602.  
  1603. if ( is_null( $value ) ) { 
  1604. return array_sum( $counts ); 
  1605. } elseif ( isset( $counts[ $value ] ) ) { 
  1606. return absint( $counts[ $value ] ); 
  1607. } else { 
  1608. return 0; 
  1609.  
  1610. /** 
  1611. * Get a file by $download_id. 
  1612. * 
  1613. * @param string $download_id file identifier 
  1614. * @return array|false if not found 
  1615. */ 
  1616. public function get_file( $download_id = '' ) { 
  1617. $files = $this->get_downloads(); 
  1618.  
  1619. if ( '' === $download_id ) { 
  1620. $file = sizeof( $files ) ? current( $files ) : false; 
  1621. } elseif ( isset( $files[ $download_id ] ) ) { 
  1622. $file = $files[ $download_id ]; 
  1623. } else { 
  1624. $file = false; 
  1625.  
  1626. return apply_filters( 'woocommerce_product_file', $file, $this, $download_id ); 
  1627.  
  1628. /** 
  1629. * Get file download path identified by $download_id. 
  1630. * 
  1631. * @param string $download_id file identifier 
  1632. * @return string 
  1633. */ 
  1634. public function get_file_download_path( $download_id ) { 
  1635. $files = $this->get_downloads(); 
  1636. $file_path = isset( $files[ $download_id ] ) ? $files[ $download_id ]->get_file() : ''; 
  1637.  
  1638. // allow overriding based on the particular file being requested 
  1639. return apply_filters( 'woocommerce_product_file_download_path', $file_path, $this, $download_id ); 
  1640.  
  1641. /** 
  1642. * Get the suffix to display after prices > 0. 
  1643. * 
  1644. * @param string $price to calculate, left blank to just use get_price() 
  1645. * @param integer $qty passed on to get_price_including_tax() or get_price_excluding_tax() 
  1646. * @return string 
  1647. */ 
  1648. public function get_price_suffix( $price = '', $qty = 1 ) { 
  1649. $html = ''; 
  1650.  
  1651. if ( ( $suffix = get_option( 'woocommerce_price_display_suffix' ) ) && wc_tax_enabled() && 'taxable' === $this->get_tax_status() ) { 
  1652. if ( '' === $price ) { 
  1653. $price = $this->get_price(); 
  1654. $replacements = array( 
  1655. '{price_including_tax}' => wc_price( wc_get_price_including_tax( $this, array( 'qty' => $qty, 'price' => $price ) ) ),  
  1656. '{price_excluding_tax}' => wc_price( wc_get_price_excluding_tax( $this, array( 'qty' => $qty, 'price' => $price ) ) ),  
  1657. ); 
  1658. $html = str_replace( array_keys( $replacements ), array_values( $replacements ), ' <small class="woocommerce-price-suffix">' . wp_kses_post( $suffix ) . '</small>' ); 
  1659. return apply_filters( 'woocommerce_get_price_suffix', $html, $this, $price, $qty ); 
  1660.  
  1661. /** 
  1662. * Returns the availability of the product. 
  1663. * 
  1664. * @return string[] 
  1665. */ 
  1666. public function get_availability() { 
  1667. return apply_filters( 'woocommerce_get_availability', array( 
  1668. 'availability' => $this->get_availability_text(),  
  1669. 'class' => $this->get_availability_class(),  
  1670. ), $this ); 
  1671.  
  1672. /** 
  1673. * Get availability text based on stock status. 
  1674. * 
  1675. * @return string 
  1676. */ 
  1677. protected function get_availability_text() { 
  1678. if ( ! $this->is_in_stock() ) { 
  1679. $availability = __( 'Out of stock', 'woocommerce' ); 
  1680. } elseif ( $this->managing_stock() && $this->is_on_backorder( 1 ) ) { 
  1681. $availability = $this->backorders_require_notification() ? __( 'Available on backorder', 'woocommerce' ) : ''; 
  1682. } elseif ( $this->managing_stock() ) { 
  1683. $availability = wc_format_stock_for_display( $this ); 
  1684. } else { 
  1685. $availability = ''; 
  1686. return apply_filters( 'woocommerce_get_availability_text', $availability, $this ); 
  1687.  
  1688. /** 
  1689. * Get availability classname based on stock status. 
  1690. * 
  1691. * @return string 
  1692. */ 
  1693. protected function get_availability_class() { 
  1694. if ( ! $this->is_in_stock() ) { 
  1695. $class = 'out-of-stock'; 
  1696. } elseif ( $this->managing_stock() && $this->is_on_backorder( 1 ) ) { 
  1697. $class = 'available-on-backorder'; 
  1698. } else { 
  1699. $class = 'in-stock'; 
  1700. return apply_filters( 'woocommerce_get_availability_class', $class, $this ); 
.