PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 11.0.1
WooCommerce v11.0.1
11.1.0 11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / src / StoreApi / Schemas / V1 / ProductSchema.php
woocommerce / src / StoreApi / Schemas / V1 Last commit date
AI 1 year ago Agentic 9 months ago AbstractAddressSchema.php 1 month ago AbstractSchema.php 2 months ago BatchSchema.php 2 years ago BillingAddressSchema.php 1 month ago CartCouponSchema.php 1 year ago CartExtensionsSchema.php 2 years ago CartFeeSchema.php 2 years ago CartItemSchema.php 2 months ago CartSchema.php 1 month ago CartShippingRateSchema.php 1 month ago CheckoutOrderSchema.php 2 years ago CheckoutSchema.php 1 month ago ErrorSchema.php 2 years ago ImageAttachmentSchema.php 5 months ago ItemSchema.php 1 year ago OrderCouponSchema.php 2 years ago OrderFeeSchema.php 2 years ago OrderItemSchema.php 2 months ago OrderSchema.php 4 months ago PatternsSchema.php 1 year ago ProductAttributeSchema.php 2 years ago ProductAttributeTermSchema.php 2 months ago ProductBrandSchema.php 1 year ago ProductCategorySchema.php 2 years ago ProductCollectionDataSchema.php 1 year ago ProductReviewSchema.php 2 years ago ProductSchema.php 1 month ago ShippingAddressSchema.php 2 years ago ShopperListItemSchema.php 2 months ago ShopperListSchema.php 2 months ago TermSchema.php 2 years ago
ProductSchema.php
1049 lines
1 <?php
2 namespace Automattic\WooCommerce\StoreApi\Schemas\V1;
3
4 use Automattic\WooCommerce\Enums\ProductType;
5 use Automattic\WooCommerce\StoreApi\SchemaController;
6 use Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema;
7 use Automattic\WooCommerce\StoreApi\Utilities\QuantityLimits;
8 use Automattic\WooCommerce\Blocks\Utils\ProductAvailabilityUtils;
9 use Automattic\WooCommerce\Enums\ProductStockStatus;
10 use Automattic\WooCommerce\Enums\TaxDisplayMode;
11
12 /**
13 * ProductSchema class.
14 */
15 class ProductSchema extends AbstractSchema {
16 /**
17 * The schema item name.
18 *
19 * @var string
20 */
21 protected $title = 'product';
22
23 /**
24 * The schema item identifier.
25 *
26 * @var string
27 */
28 const IDENTIFIER = 'product';
29
30 /**
31 * Image attachment schema instance.
32 *
33 * @var ImageAttachmentSchema
34 */
35 protected $image_attachment_schema;
36
37 /**
38 * Constructor.
39 *
40 * @param ExtendSchema $extend Rest Extending instance.
41 * @param SchemaController $controller Schema Controller instance.
42 */
43 public function __construct( ExtendSchema $extend, SchemaController $controller ) {
44 parent::__construct( $extend, $controller );
45 $this->image_attachment_schema = $this->controller->get( ImageAttachmentSchema::IDENTIFIER );
46 }
47
48 /**
49 * Product schema properties.
50 *
51 * @return array
52 */
53 public function get_properties() {
54 return [
55 'id' => [
56 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
57 'type' => 'integer',
58 'context' => [ 'view', 'edit', 'embed' ],
59 'readonly' => true,
60 ],
61 'name' => [
62 'description' => __( 'Product name.', 'woocommerce' ),
63 'type' => 'string',
64 'context' => [ 'view', 'edit', 'embed' ],
65 ],
66 'slug' => [
67 'description' => __( 'Product slug.', 'woocommerce' ),
68 'type' => 'string',
69 'context' => [ 'view', 'edit', 'embed' ],
70 ],
71 'parent' => [
72 'description' => __( 'ID of the parent product, if applicable.', 'woocommerce' ),
73 'type' => 'integer',
74 'context' => [ 'view', 'edit', 'embed' ],
75 'readonly' => true,
76 ],
77 'type' => [
78 'description' => __( 'Product type.', 'woocommerce' ),
79 'type' => 'string',
80 'context' => [ 'view', 'edit', 'embed' ],
81 'readonly' => true,
82 ],
83 'variation' => [
84 'description' => __( 'Product variation attributes, if applicable.', 'woocommerce' ),
85 'type' => 'string',
86 'context' => [ 'view', 'edit', 'embed' ],
87 ],
88 'permalink' => [
89 'description' => __( 'Product URL.', 'woocommerce' ),
90 'type' => 'string',
91 'format' => 'uri',
92 'context' => [ 'view', 'edit', 'embed' ],
93 'readonly' => true,
94 ],
95 'short_description' => [
96 'description' => __( 'Product short description in HTML format.', 'woocommerce' ),
97 'type' => 'string',
98 'context' => [ 'view', 'edit', 'embed' ],
99 ],
100 'description' => [
101 'description' => __( 'Product full description in HTML format.', 'woocommerce' ),
102 'type' => 'string',
103 'context' => [ 'view', 'edit', 'embed' ],
104 ],
105 'on_sale' => [
106 'description' => __( 'Is the product on sale?', 'woocommerce' ),
107 'type' => 'boolean',
108 'context' => [ 'view', 'edit', 'embed' ],
109 'readonly' => true,
110 ],
111 'sku' => [
112 'description' => __( 'Unique identifier.', 'woocommerce' ),
113 'type' => 'string',
114 'context' => [ 'view', 'edit', 'embed' ],
115 ],
116 'prices' => [
117 'description' => __( 'Price data provided using the smallest unit of the currency.', 'woocommerce' ),
118 'type' => 'object',
119 'context' => [ 'view', 'edit', 'embed' ],
120 'readonly' => true,
121 'properties' => array_merge(
122 $this->get_store_currency_properties(),
123 [
124 'price' => [
125 'description' => __( 'Current product price.', 'woocommerce' ),
126 'type' => 'string',
127 'context' => [ 'view', 'edit', 'embed' ],
128 'readonly' => true,
129 ],
130 'regular_price' => [
131 'description' => __( 'Regular product price.', 'woocommerce' ),
132 'type' => 'string',
133 'context' => [ 'view', 'edit', 'embed' ],
134 'readonly' => true,
135 ],
136 'sale_price' => [
137 'description' => __( 'Sale product price, if applicable.', 'woocommerce' ),
138 'type' => 'string',
139 'context' => [ 'view', 'edit', 'embed' ],
140 'readonly' => true,
141 ],
142 'price_range' => [
143 'description' => __( 'Price range, if applicable.', 'woocommerce' ),
144 'type' => [ 'object', 'null' ],
145 'context' => [ 'view', 'edit', 'embed' ],
146 'readonly' => true,
147 'properties' => [
148 'min_amount' => [
149 'description' => __( 'Price amount.', 'woocommerce' ),
150 'type' => 'string',
151 'context' => [ 'view', 'edit', 'embed' ],
152 'readonly' => true,
153 ],
154 'max_amount' => [
155 'description' => __( 'Price amount.', 'woocommerce' ),
156 'type' => 'string',
157 'context' => [ 'view', 'edit', 'embed' ],
158 'readonly' => true,
159 ],
160 ],
161 ],
162 ]
163 ),
164 ],
165 'price_html' => array(
166 'description' => __( 'Price string formatted as HTML.', 'woocommerce' ),
167 'type' => 'string',
168 'context' => array( 'view', 'edit', 'embed' ),
169 'readonly' => true,
170 ),
171 'average_rating' => [
172 'description' => __( 'Reviews average rating.', 'woocommerce' ),
173 'type' => 'string',
174 'context' => [ 'view', 'edit', 'embed' ],
175 'readonly' => true,
176 ],
177 'review_count' => [
178 'description' => __( 'Amount of reviews that the product has.', 'woocommerce' ),
179 'type' => 'integer',
180 'context' => [ 'view', 'edit', 'embed' ],
181 'readonly' => true,
182 ],
183 'images' => [
184 'description' => __( 'List of images.', 'woocommerce' ),
185 'type' => 'array',
186 'context' => [ 'view', 'edit', 'embed' ],
187 'items' => [
188 'type' => 'object',
189 'properties' => $this->image_attachment_schema->get_properties(),
190 ],
191 ],
192 'categories' => [
193 'description' => __( 'List of categories, if applicable.', 'woocommerce' ),
194 'type' => 'array',
195 'context' => [ 'view', 'edit', 'embed' ],
196 'items' => [
197 'type' => 'object',
198 'properties' => [
199 'id' => [
200 'description' => __( 'Category ID', 'woocommerce' ),
201 'type' => 'number',
202 'context' => [ 'view', 'edit', 'embed' ],
203 'readonly' => true,
204 ],
205 'name' => [
206 'description' => __( 'Category name', 'woocommerce' ),
207 'type' => 'string',
208 'context' => [ 'view', 'edit', 'embed' ],
209 'readonly' => true,
210 ],
211 'slug' => [
212 'description' => __( 'Category slug', 'woocommerce' ),
213 'type' => 'string',
214 'context' => [ 'view', 'edit', 'embed' ],
215 'readonly' => true,
216 ],
217 'link' => [
218 'description' => __( 'Category link', 'woocommerce' ),
219 'type' => 'string',
220 'context' => [ 'view', 'edit', 'embed' ],
221 'readonly' => true,
222 ],
223 ],
224 ],
225 ],
226 'tags' => [
227 'description' => __( 'List of tags, if applicable.', 'woocommerce' ),
228 'type' => 'array',
229 'context' => [ 'view', 'edit', 'embed' ],
230 'items' => [
231 'type' => 'object',
232 'properties' => [
233 'id' => [
234 'description' => __( 'Tag ID', 'woocommerce' ),
235 'type' => 'number',
236 'context' => [ 'view', 'edit', 'embed' ],
237 'readonly' => true,
238 ],
239 'name' => [
240 'description' => __( 'Tag name', 'woocommerce' ),
241 'type' => 'string',
242 'context' => [ 'view', 'edit', 'embed' ],
243 'readonly' => true,
244 ],
245 'slug' => [
246 'description' => __( 'Tag slug', 'woocommerce' ),
247 'type' => 'string',
248 'context' => [ 'view', 'edit', 'embed' ],
249 'readonly' => true,
250 ],
251 'link' => [
252 'description' => __( 'Tag link.', 'woocommerce' ),
253 'type' => 'string',
254 'context' => [ 'view', 'edit', 'embed' ],
255 'readonly' => true,
256 ],
257 ],
258 ],
259 ],
260 'brands' => [
261 'description' => __( 'List of brands, if applicable.', 'woocommerce' ),
262 'type' => 'array',
263 'context' => [ 'view', 'edit', 'embed' ],
264 'items' => [
265 'type' => 'object',
266 'properties' => [
267 'id' => [
268 'description' => __( 'Brand ID', 'woocommerce' ),
269 'type' => 'number',
270 'context' => [ 'view', 'edit', 'embed' ],
271 'readonly' => true,
272 ],
273 'name' => [
274 'description' => __( 'Brand name', 'woocommerce' ),
275 'type' => 'string',
276 'context' => [ 'view', 'edit', 'embed' ],
277 'readonly' => true,
278 ],
279 'slug' => [
280 'description' => __( 'Brand slug', 'woocommerce' ),
281 'type' => 'string',
282 'context' => [ 'view', 'edit', 'embed' ],
283 'readonly' => true,
284 ],
285 'link' => [
286 'description' => __( 'Brand link', 'woocommerce' ),
287 'type' => 'string',
288 'context' => [ 'view', 'edit', 'embed' ],
289 'readonly' => true,
290 ],
291 ],
292 ],
293 ],
294 'attributes' => [
295 'description' => __( 'List of attributes (taxonomy terms) assigned to the product. For variable products, these are mapped to variations (see the `variations` field).', 'woocommerce' ),
296 'type' => 'array',
297 'context' => [ 'view', 'edit', 'embed' ],
298 'items' => [
299 'type' => 'object',
300 'properties' => [
301 'id' => [
302 'description' => __( 'The attribute ID, or 0 if the attribute is not taxonomy based.', 'woocommerce' ),
303 'type' => 'integer',
304 'context' => [ 'view', 'edit', 'embed' ],
305 'readonly' => true,
306 ],
307 'name' => [
308 'description' => __( 'The attribute name.', 'woocommerce' ),
309 'type' => 'string',
310 'context' => [ 'view', 'edit', 'embed' ],
311 'readonly' => true,
312 ],
313 'taxonomy' => [
314 'description' => __( 'The attribute taxonomy, or null if the attribute is not taxonomy based.', 'woocommerce' ),
315 'type' => 'string',
316 'context' => [ 'view', 'edit', 'embed' ],
317 'readonly' => true,
318 ],
319 'has_variations' => [
320 'description' => __( 'True if this attribute is used by product variations.', 'woocommerce' ),
321 'type' => 'boolean',
322 'context' => [ 'view', 'edit', 'embed' ],
323 'readonly' => true,
324 ],
325 'terms' => [
326 'description' => __( 'List of assigned attribute terms.', 'woocommerce' ),
327 'type' => 'array',
328 'context' => [ 'view', 'edit', 'embed' ],
329 'items' => [
330 'type' => 'object',
331 'properties' => [
332 'id' => [
333 'description' => __( 'The term ID, or 0 if the attribute is not a global attribute.', 'woocommerce' ),
334 'type' => 'integer',
335 'context' => [ 'view', 'edit', 'embed' ],
336 'readonly' => true,
337 ],
338 'name' => [
339 'description' => __( 'The term name.', 'woocommerce' ),
340 'type' => 'string',
341 'context' => [ 'view', 'edit', 'embed' ],
342 'readonly' => true,
343 ],
344 'slug' => [
345 'description' => __( 'The term slug.', 'woocommerce' ),
346 'type' => 'string',
347 'context' => [ 'view', 'edit', 'embed' ],
348 'readonly' => true,
349 ],
350 'default' => [
351 'description' => __( 'If this is a default attribute', 'woocommerce' ),
352 'type' => 'boolean',
353 'context' => [ 'view', 'edit', 'embed' ],
354 'readonly' => true,
355 ],
356 ],
357 ],
358 ],
359 ],
360 ],
361 ],
362 'variations' => [
363 'description' => __( 'List of variation IDs, if applicable.', 'woocommerce' ),
364 'type' => 'array',
365 'context' => [ 'view', 'edit', 'embed' ],
366 'items' => [
367 'type' => 'object',
368 'properties' => [
369 'id' => [
370 'description' => __( 'The attribute ID, or 0 if the attribute is not taxonomy based.', 'woocommerce' ),
371 'type' => 'integer',
372 'context' => [ 'view', 'edit', 'embed' ],
373 'readonly' => true,
374 ],
375 'attributes' => [
376 'description' => __( 'List of variation attributes.', 'woocommerce' ),
377 'type' => 'array',
378 'context' => [ 'view', 'edit', 'embed' ],
379 'items' => [
380 'type' => 'object',
381 'properties' => [
382 'name' => [
383 'description' => __( 'The attribute name.', 'woocommerce' ),
384 'type' => 'string',
385 'context' => [ 'view', 'edit', 'embed' ],
386 'readonly' => true,
387 ],
388 'value' => [
389 'description' => __( 'The assigned attribute.', 'woocommerce' ),
390 'type' => 'string',
391 'context' => [ 'view', 'edit', 'embed' ],
392 'readonly' => true,
393 ],
394 ],
395 ],
396 ],
397 ],
398 ],
399 ],
400 'grouped_products' => [
401 'description' => __( 'List of grouped product IDs, if applicable.', 'woocommerce' ),
402 'type' => 'array',
403 'context' => [ 'view', 'edit', 'embed' ],
404 'items' => [
405 'description' => __( 'List of grouped product ids.', 'woocommerce' ),
406 'type' => 'integer',
407 'context' => [ 'view', 'edit', 'embed' ],
408 'readonly' => true,
409 ],
410 ],
411 'has_options' => [
412 'description' => __( 'Does the product have additional options before it can be added to the cart?', 'woocommerce' ),
413 'type' => 'boolean',
414 'context' => [ 'view', 'edit', 'embed' ],
415 'readonly' => true,
416 ],
417 'is_purchasable' => [
418 'description' => __( 'Is the product purchasable?', 'woocommerce' ),
419 'type' => 'boolean',
420 'context' => [ 'view', 'edit', 'embed' ],
421 'readonly' => true,
422 ],
423 'is_in_stock' => [
424 'description' => __( 'Is the product in stock?', 'woocommerce' ),
425 'type' => 'boolean',
426 'context' => [ 'view', 'edit', 'embed' ],
427 'readonly' => true,
428 ],
429 'is_on_backorder' => [
430 'description' => __( 'Is the product stock backordered? This will also return false if backorder notifications are turned off.', 'woocommerce' ),
431 'type' => 'boolean',
432 'context' => [ 'view', 'edit', 'embed' ],
433 'readonly' => true,
434 ],
435 'stock_availability' => [
436 'description' => __( 'Information about the product\'s availability.', 'woocommerce' ),
437 'type' => 'object',
438 'context' => [ 'view', 'edit', 'embed' ],
439 'readonly' => true,
440 'properties' => [
441 'text' => [
442 'description' => __( 'Stock availability text.', 'woocommerce' ),
443 'type' => 'string',
444 'context' => [ 'view', 'edit', 'embed' ],
445 'readonly' => true,
446 ],
447 'class' => [
448 'description' => __( 'Stock availability class.', 'woocommerce' ),
449 'type' => 'string',
450 'context' => [ 'view', 'edit', 'embed' ],
451 'readonly' => true,
452 ],
453 ],
454 ],
455 'low_stock_remaining' => [
456 'description' => __( 'Quantity left in stock if stock is low, or null if not applicable.', 'woocommerce' ),
457 'type' => [ 'number', 'null' ],
458 'context' => [ 'view', 'edit', 'embed' ],
459 'readonly' => true,
460 ],
461 'sold_individually' => [
462 'description' => __( 'If true, only one item of this product is allowed for purchase in a single order.', 'woocommerce' ),
463 'type' => 'boolean',
464 'context' => [ 'view', 'edit', 'embed' ],
465 'readonly' => true,
466 ],
467 'weight' => [
468 'description' => sprintf(
469 /* translators: %s: weight unit */
470 __( 'Product weight (%s).', 'woocommerce' ),
471 get_option( 'woocommerce_weight_unit' )
472 ),
473 'type' => 'string',
474 'context' => [ 'view', 'edit' ],
475 'readonly' => true,
476 ],
477 'dimensions' => [
478 'description' => __( 'Product dimensions.', 'woocommerce' ),
479 'type' => 'object',
480 'context' => [ 'view', 'edit' ],
481 'readonly' => true,
482 'properties' => [
483 'length' => [
484 'description' => sprintf(
485 /* translators: %s: dimension unit */
486 __( 'Product length (%s).', 'woocommerce' ),
487 get_option( 'woocommerce_dimension_unit' )
488 ),
489 'type' => 'string',
490 'context' => [ 'view', 'edit' ],
491 'readonly' => true,
492 ],
493 'width' => [
494 'description' => sprintf(
495 /* translators: %s: dimension unit */
496 __( 'Product width (%s).', 'woocommerce' ),
497 get_option( 'woocommerce_dimension_unit' )
498 ),
499 'type' => 'string',
500 'context' => [ 'view', 'edit' ],
501 'readonly' => true,
502 ],
503 'height' => [
504 'description' => sprintf(
505 /* translators: %s: dimension unit */
506 __( 'Product height (%s).', 'woocommerce' ),
507 get_option( 'woocommerce_dimension_unit' )
508 ),
509 'type' => 'string',
510 'context' => [ 'view', 'edit' ],
511 'readonly' => true,
512 ],
513 ],
514 ],
515 'formatted_weight' => [
516 'description' => __( 'Product weight formatted for display (e.g. "2.5 kg").', 'woocommerce' ),
517 'type' => 'string',
518 'context' => [ 'view', 'edit' ],
519 'readonly' => true,
520 ],
521 'formatted_dimensions' => [
522 'description' => __( 'Product dimensions formatted for display (e.g. "10 × 5 × 3 cm").', 'woocommerce' ),
523 'type' => 'string',
524 'context' => [ 'view', 'edit' ],
525 'readonly' => true,
526 ],
527 'add_to_cart' => [
528 'description' => __( 'Add to cart button parameters.', 'woocommerce' ),
529 'type' => 'object',
530 'context' => [ 'view', 'edit', 'embed' ],
531 'readonly' => true,
532 'properties' => [
533 'text' => [
534 'description' => __( 'Button text.', 'woocommerce' ),
535 'type' => 'string',
536 'context' => [ 'view', 'edit', 'embed' ],
537 'readonly' => true,
538 ],
539 'description' => [
540 'description' => __( 'Button description.', 'woocommerce' ),
541 'type' => 'string',
542 'context' => [ 'view', 'edit', 'embed' ],
543 'readonly' => true,
544 ],
545 'url' => [
546 'description' => __( 'Add to cart URL.', 'woocommerce' ),
547 'type' => 'string',
548 'context' => [ 'view', 'edit', 'embed' ],
549 'readonly' => true,
550 ],
551 'minimum' => [
552 'description' => __( 'The minimum quantity that can be added to the cart.', 'woocommerce' ),
553 'type' => 'number',
554 'context' => [ 'view', 'edit', 'embed' ],
555 'readonly' => true,
556 ],
557 'maximum' => [
558 'description' => __( 'The maximum quantity that can be added to the cart.', 'woocommerce' ),
559 'type' => 'number',
560 'context' => [ 'view', 'edit', 'embed' ],
561 'readonly' => true,
562 ],
563 'multiple_of' => [
564 'description' => __( 'The amount that quantities increment by. Quantity must be an multiple of this value.', 'woocommerce' ),
565 'type' => 'number',
566 'context' => [ 'view', 'edit', 'embed' ],
567 'readonly' => true,
568 'default' => 1,
569 ],
570 'single_text' => [
571 'description' => __( 'Button text in the single product page.', 'woocommerce' ),
572 'type' => 'string',
573 'context' => [ 'view', 'edit', 'embed' ],
574 'readonly' => true,
575 ],
576 ],
577 ],
578 'is_password_protected' => [
579 'description' => __( 'Whether the product requires a password to access its content.', 'woocommerce' ),
580 'type' => 'boolean',
581 'context' => [ 'view', 'edit', 'embed' ],
582 'readonly' => true,
583 ],
584 self::EXTENDING_KEY => $this->get_extended_schema( self::IDENTIFIER ),
585 ];
586 }
587
588 /**
589 * Convert a WooCommerce product into an object suitable for the response.
590 *
591 * @param \WC_Product $product Product instance.
592 * @return array
593 */
594 public function get_item_response( $product ) {
595 $availability = ProductAvailabilityUtils::get_product_availability( $product );
596 $password_required = post_password_required( $product->get_id() );
597 $short_description = $password_required ? '' : $this->prepare_html_response( wc_format_content( wp_kses_post( $product->get_short_description() ) ) );
598 $description = $password_required ? '' : $this->prepare_html_response( wc_format_content( wp_kses_post( $product->get_description() ) ) );
599
600 return [
601 'id' => $product->get_id(),
602 'name' => $this->prepare_html_response( $product->get_title() ),
603 'slug' => $product->get_slug(),
604 'parent' => $product->get_parent_id(),
605 'type' => $product->get_type(),
606 'variation' => $this->prepare_html_response( $product->is_type( ProductType::VARIATION ) ? wc_get_formatted_variation( $product, true, true, false ) : '' ),
607 'permalink' => $product->get_permalink(),
608 'sku' => $this->prepare_html_response( $product->get_sku() ),
609 'short_description' => $short_description,
610 'description' => $description,
611 'on_sale' => $product->is_on_sale(),
612 'prices' => (object) $this->prepare_product_price_response( $product ),
613 'price_html' => $this->prepare_html_response( $product->get_price_html() ),
614 'average_rating' => (string) $product->get_average_rating(),
615 'review_count' => $product->get_review_count(),
616 'images' => $this->get_images( $product ),
617 'categories' => $this->get_term_list( $product, 'product_cat' ),
618 'tags' => $this->get_term_list( $product, 'product_tag' ),
619 'brands' => $this->get_term_list( $product, 'product_brand' ),
620 'attributes' => $this->get_attributes( $product ),
621 'variations' => $this->get_variations( $product ),
622 'grouped_products' => $this->get_grouped_products( $product ),
623 'has_options' => $product->has_options(),
624 'is_purchasable' => $product->is_purchasable(),
625 'is_in_stock' => $product->is_in_stock(),
626 'is_on_backorder' => ProductStockStatus::ON_BACKORDER === $product->get_stock_status(),
627 'low_stock_remaining' => $this->get_low_stock_remaining( $product ),
628 'stock_availability' => (object) array(
629 'text' => $availability['availability'] ?? '',
630 'class' => $availability['class'] ?? '',
631 ),
632 'sold_individually' => $product->is_sold_individually(),
633 'weight' => $product->get_weight(),
634 'dimensions' => (object) [
635 'length' => $product->get_length(),
636 'width' => $product->get_width(),
637 'height' => $product->get_height(),
638 ],
639 'formatted_weight' => wc_format_weight( (float) $product->get_weight() ),
640 'formatted_dimensions' => html_entity_decode( wc_format_dimensions( (array) $product->get_dimensions( false ) ), ENT_QUOTES, get_bloginfo( 'charset' ) ),
641 'add_to_cart' => (object) array_merge(
642 [
643 'text' => $this->prepare_html_response( $product->add_to_cart_text() ),
644 'description' => $this->prepare_html_response( $product->add_to_cart_description() ),
645 'url' => $this->prepare_html_response( $product->add_to_cart_url() ),
646 'single_text' => $this->prepare_html_response( $product->single_add_to_cart_text() ),
647 ],
648 ( new QuantityLimits() )->get_add_to_cart_limits( $product )
649 ),
650 'is_password_protected' => '' !== $product->get_post_password(),
651 self::EXTENDING_KEY => $this->get_extended_data( self::IDENTIFIER, $product ),
652
653 ];
654 }
655
656 /**
657 * Get list of product images.
658 *
659 * @param \WC_Product $product Product instance.
660 * @return array
661 */
662 protected function get_images( \WC_Product $product ) {
663 $attachment_ids = array_filter( array_merge( [ $product->get_image_id() ], $product->get_gallery_image_ids() ) );
664 if ( ! empty( $attachment_ids ) ) {
665 // Prime caches to reduce future queries.
666 _prime_post_caches( $attachment_ids );
667 }
668
669 return array_values( array_filter( array_map( [ $this->image_attachment_schema, 'get_item_response' ], $attachment_ids ) ) );
670 }
671
672 /**
673 * Gets remaining stock amount for a product.
674 *
675 * @param \WC_Product $product Product instance.
676 * @return int|float|null
677 */
678 protected function get_remaining_stock( \WC_Product $product ) {
679 if ( is_null( $product->get_stock_quantity() ) ) {
680 return null;
681 }
682 return $product->get_stock_quantity();
683 }
684
685 /**
686 * If a product has low stock, return the remaining stock amount for display.
687 *
688 * @param \WC_Product $product Product instance.
689 * @return int|float|null
690 */
691 protected function get_low_stock_remaining( \WC_Product $product ) {
692 $remaining_stock = $this->get_remaining_stock( $product );
693 $stock_format = get_option( 'woocommerce_stock_format' );
694
695 // Don't show the low stock badge if the settings doesn't allow it.
696 if ( 'no_amount' === $stock_format ) {
697 return null;
698 }
699
700 // Show the low stock badge if the remaining stock is below or equal to the threshold.
701 if ( ! is_null( $remaining_stock ) && $remaining_stock <= wc_get_low_stock_amount( $product ) ) {
702 return max( $remaining_stock, 0 );
703 }
704
705 return null;
706 }
707
708 /**
709 * Returns true if the given attribute is valid.
710 *
711 * @param mixed $attribute Object or variable to check.
712 * @return boolean
713 */
714 protected function filter_valid_attribute( $attribute ) {
715 return is_a( $attribute, '\WC_Product_Attribute' );
716 }
717
718 /**
719 * Returns true if the given attribute is valid and used for variations.
720 *
721 * @param mixed $attribute Object or variable to check.
722 * @return boolean
723 */
724 protected function filter_variation_attribute( $attribute ) {
725 return $this->filter_valid_attribute( $attribute ) && $attribute->get_variation();
726 }
727
728 /**
729 * Get variation IDs and attributes from the DB.
730 *
731 * @param \WC_Product $product Product instance.
732 * @returns array
733 */
734 protected function get_variations( \WC_Product $product ) {
735 $variation_ids = $product->is_type( ProductType::VARIABLE ) ? $product->get_visible_children() : [];
736
737 if ( ! count( $variation_ids ) ) {
738 return [];
739 }
740
741 /**
742 * Gets default variation data which applies to all of this products variations.
743 */
744 $attributes = array_filter( $product->get_attributes(), [ $this, 'filter_variation_attribute' ] );
745 $default_variation_meta_data = array_reduce(
746 $attributes,
747 function ( $defaults, $attribute ) use ( $product ) {
748 $meta_key = wc_variation_attribute_name( $attribute->get_name() );
749 $defaults[ $meta_key ] = [
750 'name' => wc_attribute_label( $attribute->get_name(), $product ),
751 'value' => null,
752 ];
753 return $defaults;
754 },
755 []
756 );
757 $default_variation_meta_keys = array_keys( $default_variation_meta_data );
758
759 /**
760 * Gets individual variation data from the database, using cache where possible.
761 */
762 $cache_group = 'product_variation_meta_data';
763 $cache_value = wp_cache_get( $product->get_id(), $cache_group );
764 $last_modified = get_the_modified_date( 'U', $product->get_id() );
765
766 if ( false === $cache_value || $last_modified !== $cache_value['last_modified'] ) {
767 global $wpdb;
768 // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
769 $variation_meta_data = $wpdb->get_results(
770 "
771 SELECT post_id as variation_id, meta_key as attribute_key, meta_value as attribute_value
772 FROM {$wpdb->postmeta}
773 WHERE post_id IN (" . implode( ',', array_map( 'esc_sql', $variation_ids ) ) . ")
774 AND meta_key IN ('" . implode( "','", array_map( 'esc_sql', $default_variation_meta_keys ) ) . "')
775 "
776 );
777 // phpcs:enable
778
779 wp_cache_set(
780 $product->get_id(),
781 [
782 'last_modified' => $last_modified,
783 'data' => $variation_meta_data,
784 ],
785 $cache_group
786 );
787 } else {
788 $variation_meta_data = $cache_value['data'];
789 }
790
791 /**
792 * Merges and formats default variation data with individual variation data.
793 */
794 $attributes_by_variation = array_reduce(
795 $variation_meta_data,
796 function ( $values, $data ) use ( $default_variation_meta_keys ) {
797 // The query above only includes the keys of $default_variation_meta_data so we know all of the attributes
798 // being processed here apply to this product. However, we need an additional check here because the
799 // cache may have been primed elsewhere and include keys from other products.
800 // @see AbstractProductGrid::prime_product_variations.
801 if ( in_array( $data->attribute_key, $default_variation_meta_keys, true ) ) {
802 $values[ $data->variation_id ][ $data->attribute_key ] = $data->attribute_value;
803 }
804 return $values;
805 },
806 array_fill_keys( $variation_ids, [] )
807 );
808
809 $variations = [];
810
811 foreach ( $variation_ids as $variation_id ) {
812 $attribute_data = $default_variation_meta_data;
813
814 foreach ( $attributes_by_variation[ $variation_id ] as $meta_key => $meta_value ) {
815 if ( '' !== $meta_value ) {
816 $attribute_data[ $meta_key ]['value'] = $meta_value;
817 }
818 }
819
820 $variations[] = (object) [
821 'id' => $variation_id,
822 'attributes' => array_values( $attribute_data ),
823 ];
824 }
825
826 return $variations;
827 }
828
829 /**
830 * Get grouped product IDs.
831 *
832 * @param \WC_Product $product Product instance.
833 * @return array
834 */
835 protected function get_grouped_products( \WC_Product $product ) {
836 if ( $product->is_type( ProductType::GROUPED ) ) {
837 return array_map(
838 function ( $child ) {
839 return $child->get_id();
840 },
841 $product->get_visible_children(),
842 );
843 }
844 return [];
845 }
846
847 /**
848 * Get list of product attributes and attribute terms.
849 *
850 * @param \WC_Product $product Product instance.
851 * @return array
852 */
853 protected function get_attributes( \WC_Product $product ) {
854 $attributes = array_filter( $product->get_attributes(), [ $this, 'filter_valid_attribute' ] );
855 $default_attributes = $product->get_default_attributes();
856 $return = [];
857
858 foreach ( $attributes as $attribute_slug => $attribute ) {
859 // Only visible or variation attributes will be exposed by this API.
860 if ( ! $attribute->get_visible() && ! $attribute->get_variation() ) {
861 continue;
862 }
863
864 $terms = $attribute->is_taxonomy() ? array_map( [ $this, 'prepare_product_attribute_taxonomy_value' ], $attribute->get_terms() ) : array_map( [ $this, 'prepare_product_attribute_value' ], $attribute->get_options() );
865 // Custom attribute names are sanitized to be the array keys.
866 // So when we do the array_key_exists check below we also need to sanitize the attribute names.
867
868 $sanitized_attribute_name = sanitize_key( $attribute->get_name() );
869
870 if ( array_key_exists( $sanitized_attribute_name, $default_attributes ) ) {
871 foreach ( $terms as $term ) {
872 $term->default = $term->slug === $default_attributes[ $sanitized_attribute_name ];
873 }
874 }
875
876 $return[] = (object) [
877 'id' => $attribute->get_id(),
878 'name' => wc_attribute_label( $attribute->get_name(), $product ),
879 'taxonomy' => $attribute->is_taxonomy() ? $attribute->get_name() : null,
880 'has_variations' => true === $attribute->get_variation(),
881 'terms' => $terms,
882 ];
883 }
884
885 return $return;
886 }
887
888 /**
889 * Prepare an attribute term for the response.
890 *
891 * @param \WP_Term $term Term object.
892 * @return object
893 */
894 protected function prepare_product_attribute_taxonomy_value( \WP_Term $term ) {
895 return $this->prepare_product_attribute_value( $term->name, $term->term_id, $term->slug );
896 }
897
898 /**
899 * Prepare an attribute term for the response.
900 *
901 * @param string $name Attribute term name.
902 * @param int $id Attribute term ID.
903 * @param string $slug Attribute term slug.
904 * @return object
905 */
906 protected function prepare_product_attribute_value( $name, $id = 0, $slug = '' ) {
907 return (object) [
908 'id' => (int) $id,
909 'name' => $name,
910 'slug' => $slug ? $slug : $name,
911 ];
912 }
913
914 /**
915 * Get an array of pricing data.
916 *
917 * @param \WC_Product $product Product instance.
918 * @param string $tax_display_mode If returned prices are incl or excl of tax.
919 * @return array
920 */
921 protected function prepare_product_price_response( \WC_Product $product, $tax_display_mode = '' ) {
922 $prices = [];
923 $tax_display_mode = $this->get_tax_display_mode( $tax_display_mode );
924 $price_function = $this->get_price_function_from_tax_display_mode( $tax_display_mode );
925 $price_decimals = wc_get_price_decimals();
926
927 // If we have a variable product, get the price from the variations (this will use the min value).
928 if ( $product->is_type( ProductType::VARIABLE ) ) {
929 $regular_price = $product->get_variation_regular_price();
930 $sale_price = $product->get_variation_sale_price();
931 } else {
932 $regular_price = $product->get_regular_price();
933 $sale_price = $product->get_sale_price();
934 }
935
936 $prices['price'] = $this->prepare_money_response( $price_function( $product ), $price_decimals );
937 $prices['regular_price'] = $this->prepare_money_response( $price_function( $product, [ 'price' => $regular_price ] ), $price_decimals );
938 $prices['sale_price'] = $this->prepare_money_response( $price_function( $product, [ 'price' => $sale_price ] ), $price_decimals );
939 $prices['price_range'] = $this->get_price_range( $product, $tax_display_mode );
940
941 return $this->prepare_currency_response( $prices );
942 }
943
944 /**
945 * WooCommerce can return prices including or excluding tax; choose the correct method based on tax display mode.
946 *
947 * @param string $tax_display_mode Provided tax display mode.
948 * @return string Valid tax display mode.
949 */
950 protected function get_tax_display_mode( $tax_display_mode = '' ) {
951 return in_array( $tax_display_mode, [ TaxDisplayMode::INCLUSIVE, TaxDisplayMode::EXCLUSIVE ], true ) ? $tax_display_mode : get_option( 'woocommerce_tax_display_shop' );
952 }
953
954 /**
955 * WooCommerce can return prices including or excluding tax; choose the correct method based on tax display mode.
956 *
957 * @param string $tax_display_mode If returned prices are incl or excl of tax.
958 * @return string Function name.
959 */
960 protected function get_price_function_from_tax_display_mode( $tax_display_mode ) {
961 return TaxDisplayMode::INCLUSIVE === $tax_display_mode ? 'wc_get_price_including_tax' : 'wc_get_price_excluding_tax';
962 }
963
964 /**
965 * Get price range from certain product types.
966 *
967 * @param \WC_Product $product Product instance.
968 * @param string $tax_display_mode If returned prices are incl or excl of tax.
969 * @return object|null
970 */
971 protected function get_price_range( \WC_Product $product, $tax_display_mode = '' ) {
972 $tax_display_mode = $this->get_tax_display_mode( $tax_display_mode );
973
974 if ( $product->is_type( ProductType::VARIABLE ) ) {
975 $prices = $product->get_variation_prices( true );
976
977 if ( ! empty( $prices['price'] ) && ( min( $prices['price'] ) !== max( $prices['price'] ) ) ) {
978 return (object) [
979 'min_amount' => $this->prepare_money_response( min( $prices['price'] ), wc_get_price_decimals() ),
980 'max_amount' => $this->prepare_money_response( max( $prices['price'] ), wc_get_price_decimals() ),
981 ];
982 }
983 }
984
985 if ( $product->is_type( ProductType::GROUPED ) ) {
986 $children = $product->get_visible_children();
987 $price_function = TaxDisplayMode::INCLUSIVE === $tax_display_mode ? 'wc_get_price_including_tax' : 'wc_get_price_excluding_tax';
988
989 foreach ( $children as $child ) {
990 if ( '' !== $child->get_price() ) {
991 $child_prices[] = $price_function( $child );
992 }
993 }
994
995 if ( ! empty( $child_prices ) ) {
996 return (object) [
997 'min_amount' => $this->prepare_money_response( min( $child_prices ), wc_get_price_decimals() ),
998 'max_amount' => $this->prepare_money_response( max( $child_prices ), wc_get_price_decimals() ),
999 ];
1000 }
1001 }
1002
1003 return null;
1004 }
1005
1006 /**
1007 * Returns a list of terms assigned to the product.
1008 *
1009 * @param \WC_Product $product Product object.
1010 * @param string $taxonomy Taxonomy name.
1011 * @return array Array of terms (id, name, slug).
1012 */
1013 protected function get_term_list( \WC_Product $product, $taxonomy = '' ) {
1014 if ( ! $taxonomy ) {
1015 return [];
1016 }
1017
1018 $terms = get_the_terms( $product->get_id(), $taxonomy );
1019
1020 if ( ! $terms || is_wp_error( $terms ) ) {
1021 return [];
1022 }
1023
1024 $return = [];
1025 $default_category = (int) get_option( 'default_product_cat', 0 );
1026
1027 foreach ( $terms as $term ) {
1028 $link = get_term_link( $term, $taxonomy );
1029
1030 if ( is_wp_error( $link ) ) {
1031 continue;
1032 }
1033
1034 if ( $term->term_id === $default_category ) {
1035 continue;
1036 }
1037
1038 $return[] = (object) [
1039 'id' => $term->term_id,
1040 'name' => $term->name,
1041 'slug' => $term->slug,
1042 'link' => $link,
1043 ];
1044 }
1045
1046 return $return;
1047 }
1048 }
1049