feat: Add product images support with WP Media Library integration

- Add WP Media Library integration for product and variation images
- Support images array (URLs) conversion to attachment IDs
- Add images array to API responses (Admin & Customer SPA)
- Implement drag-and-drop sortable images in Admin product form
- Add image gallery thumbnails in Customer SPA product page
- Initialize WooCommerce session for guest cart operations
- Fix product variations and attributes display in Customer SPA
- Add variation image field in Admin SPA

Changes:
- includes/Api/ProductsController.php: Handle images array, add to responses
- includes/Frontend/ShopController.php: Add images array for customer SPA
- includes/Frontend/CartController.php: Initialize WC session for guests
- admin-spa/src/lib/wp-media.ts: Add openWPMediaGallery function
- admin-spa/src/routes/Products/partials/tabs/GeneralTab.tsx: WP Media + sortable images
- admin-spa/src/routes/Products/partials/tabs/VariationsTab.tsx: Add variation image field
- customer-spa/src/pages/Product/index.tsx: Add gallery thumbnails display
This commit is contained in:
Dwindi Ramadhana
2025-11-26 16:18:43 +07:00
parent 909bddb23d
commit f397ef850f
69 changed files with 12481 additions and 156 deletions

View File

@@ -337,12 +337,33 @@ class ProductsController {
$product->set_tag_ids($data['tags']);
}
// Images
if (!empty($data['image_id'])) {
$product->set_image_id($data['image_id']);
}
if (!empty($data['gallery_image_ids']) && is_array($data['gallery_image_ids'])) {
$product->set_gallery_image_ids($data['gallery_image_ids']);
// Images - support both image_id/gallery_image_ids and images array
if (!empty($data['images']) && is_array($data['images'])) {
// Convert URLs to attachment IDs
$image_ids = [];
foreach ($data['images'] as $image_url) {
$attachment_id = attachment_url_to_postid($image_url);
if ($attachment_id) {
$image_ids[] = $attachment_id;
}
}
if (!empty($image_ids)) {
// First image is featured
$product->set_image_id($image_ids[0]);
// Rest are gallery
if (count($image_ids) > 1) {
$product->set_gallery_image_ids(array_slice($image_ids, 1));
}
}
} else {
// Legacy support for direct IDs
if (!empty($data['image_id'])) {
$product->set_image_id($data['image_id']);
}
if (!empty($data['gallery_image_ids']) && is_array($data['gallery_image_ids'])) {
$product->set_gallery_image_ids($data['gallery_image_ids']);
}
}
$product->save();
@@ -407,12 +428,35 @@ class ProductsController {
$product->set_tag_ids($data['tags']);
}
// Images
if (isset($data['image_id'])) {
$product->set_image_id($data['image_id']);
}
if (isset($data['gallery_image_ids'])) {
$product->set_gallery_image_ids($data['gallery_image_ids']);
// Images - support both image_id/gallery_image_ids and images array
if (isset($data['images']) && is_array($data['images']) && !empty($data['images'])) {
// Convert URLs to attachment IDs
$image_ids = [];
foreach ($data['images'] as $image_url) {
$attachment_id = attachment_url_to_postid($image_url);
if ($attachment_id) {
$image_ids[] = $attachment_id;
}
}
if (!empty($image_ids)) {
// First image is featured
$product->set_image_id($image_ids[0]);
// Rest are gallery
if (count($image_ids) > 1) {
$product->set_gallery_image_ids(array_slice($image_ids, 1));
} else {
$product->set_gallery_image_ids([]);
}
}
} else {
// Legacy support for direct IDs
if (isset($data['image_id'])) {
$product->set_image_id($data['image_id']);
}
if (isset($data['gallery_image_ids'])) {
$product->set_gallery_image_ids($data['gallery_image_ids']);
}
}
// Update custom meta fields (Level 1 compatibility)
@@ -596,7 +640,24 @@ class ProductsController {
$data['downloadable'] = $product->is_downloadable();
$data['featured'] = $product->is_featured();
// Gallery images
// Images array (URLs) for frontend - featured + gallery
$images = [];
$featured_image_id = $product->get_image_id();
if ($featured_image_id) {
$featured_url = wp_get_attachment_url($featured_image_id);
if ($featured_url) {
$images[] = $featured_url;
}
}
foreach ($product->get_gallery_image_ids() as $image_id) {
$url = wp_get_attachment_url($image_id);
if ($url) {
$images[] = $url;
}
}
$data['images'] = $images;
// Gallery images (detailed info)
$gallery = [];
foreach ($product->get_gallery_image_ids() as $image_id) {
$image = wp_get_attachment_image_src($image_id, 'full');
@@ -691,6 +752,11 @@ class ProductsController {
$formatted_attributes[$clean_name] = $value;
}
$image_url = $image ? $image[0] : '';
if (!$image_url && $variation->get_image_id()) {
$image_url = wp_get_attachment_url($variation->get_image_id());
}
$variations[] = [
'id' => $variation->get_id(),
'sku' => $variation->get_sku(),
@@ -702,7 +768,8 @@ class ProductsController {
'manage_stock' => $variation->get_manage_stock(),
'attributes' => $formatted_attributes,
'image_id' => $variation->get_image_id(),
'image_url' => $image ? $image[0] : '',
'image_url' => $image_url,
'image' => $image_url, // For form compatibility
];
}
}
@@ -749,7 +816,16 @@ class ProductsController {
if (isset($var_data['manage_stock'])) $variation->set_manage_stock($var_data['manage_stock']);
if (isset($var_data['stock_quantity'])) $variation->set_stock_quantity($var_data['stock_quantity']);
if (isset($var_data['attributes'])) $variation->set_attributes($var_data['attributes']);
if (isset($var_data['image_id'])) $variation->set_image_id($var_data['image_id']);
// Handle image - support both image_id and image URL
if (isset($var_data['image']) && !empty($var_data['image'])) {
$image_id = attachment_url_to_postid($var_data['image']);
if ($image_id) {
$variation->set_image_id($image_id);
}
} elseif (isset($var_data['image_id'])) {
$variation->set_image_id($var_data['image_id']);
}
$variation->save();
}