Skip to content

Commit 756122a

Browse files
Merge pull request #418 from Codeinwp/fix/categories
Fix multiple categories and backward compatibility
2 parents 9538c80 + 3603bdf commit 756122a

File tree

10 files changed

+183
-48
lines changed

10 files changed

+183
-48
lines changed

.github/workflows/test-php.yml

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,24 @@ jobs:
99
phpunit:
1010
name: Phpunit
1111
runs-on: ubuntu-22.04
12-
services:
13-
mysql:
14-
image: mysql:5.7
15-
env:
16-
MYSQL_ROOT_PASSWORD: root
17-
ports:
18-
- 3306/tcp
19-
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
2012
steps:
21-
- name: Setup PHP version
22-
uses: shivammathur/setup-php@v2
13+
- uses: actions/checkout@v4
14+
- uses: actions/setup-node@v4
2315
with:
24-
php-version: "7.2"
25-
extensions: simplexml, mysql
26-
tools: phpunit-polyfills
27-
- name: Checkout source code
28-
uses: actions/checkout@v2
29-
- name: Install WordPress Test Suite
16+
node-version: "18"
17+
cache: "yarn"
18+
- name: Install NPM deps
19+
run: |
20+
yarn install --frozen-lockfile
21+
- name: Install composer deps
22+
run: composer install
23+
- name: Install environment
3024
run: |
31-
bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1:${{ job.services.mysql.ports['3306'] }}
32-
- name: Install composer
33-
run: composer install --prefer-dist --no-progress --no-suggest
34-
- name: Run phpunit
35-
run: phpunit
25+
npm run wp-env start
26+
- name: Prepare Database
27+
run: bash ./bin/e2e-after-setup.sh
28+
- name: Run the tests
29+
run: |
30+
npm run test:unit:php
31+
env:
32+
GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }}

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ dist
77
vendor
88
languages/woocommerce-product-addon.pot
99
*.log
10-
artifacts
10+
artifacts
11+
.phpunit.result.cache

bin/env/create-products.sh

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1+
# Create new categories
2+
# NOTE: If the categories with the same slug are already present, it will raise an error.
3+
echo "Created Category 1 with ID: $category1_id"
4+
category1_id=$(wp wc product_cat create --name="Category 1" --slug="test_cat_1" --user=admin --porcelain)
5+
6+
echo "Created Category 2 with ID: $category2_id"
7+
category2_id=$(wp wc product_cat create --name="Category 2" --slug="test_cat_2" --user=admin --porcelain)
8+
9+
echo "Created Category 3 with ID: $category3_id"
10+
category3_id=$(wp wc product_cat create --name="Category 3" --slug="test_cat_3" --user=admin --porcelain)
11+
112
# Create products and collect their IDs
213
product1_id=$(wp wc product create --name="Product 1" --type="simple" --regular_price="9.99" --user=admin --porcelain)
14+
echo "Created Product 1 with ID: $product1_id"
15+
316
product2_id=$(wp wc product create --name="Product 2" --type="simple" --regular_price="19.99" --user=admin --porcelain)
4-
product3_id=$(wp wc product create --name="Product 3" --type="simple" --regular_price="29.99" --user=admin --porcelain)
17+
echo "Created Product 2 with ID: $product2_id"
518

6-
# Get the first category.
7-
category_id=$(wp wc product_cat list --user=admin --format=ids | awk '{print $1}')
8-
echo "Category ID: $category_id"
19+
product3_id=$(wp wc product create --name="Product 3" --type="simple" --regular_price="29.99" --user=admin --porcelain)
20+
echo "Created Product 3 with ID: $product3_id"
921

10-
# Add products to the first category
11-
wp wc product update $product1_id --user=admin --categories='[{"id": '$category_id'}]'
12-
wp wc product update $product2_id --user=admin --categories='[{"id": '$category_id'}]'
13-
wp wc product update $product3_id --user=admin --categories='[{"id": '$category_id'}]'
22+
# Add products to the new categories
23+
wp wc product update $product1_id --user=admin --categories='[{"id": '$category1_id'}]'
24+
wp wc product update $product2_id --user=admin --categories='[{"id": '$category2_id'}]'
25+
wp wc product update $product3_id --user=admin --categories='[{"id": '$category3_id'}]'

classes/admin.class.php

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ public function get_wc_categories( $current_values ) {
443443

444444
$used_categories = array();
445445
if ( ! empty( $current_values['productmeta_categories'] ) ) {
446-
$used_categories = explode( "\n", $current_values['productmeta_categories'] );
446+
$used_categories = preg_split('/\r\n|\n/', $current_values['productmeta_categories'] );
447447
}
448448

449449
foreach ( $product_categories as $category ) {
@@ -620,21 +620,10 @@ function ppom_attach_ppoms() {
620620
$updated_cat = count( $categories_to_attach );
621621

622622
// +----- Attach Field to Tags -----+
623-
$categories_to_tags = isset( $_POST['ppom-attach-to-tags'] ) && is_array( $_POST['ppom-attach-to-tags'] ) ? array_map( 'sanitize_key', $_POST['ppom-attach-to-tags'] ) : array();
624-
$updated_tags = count( $categories_to_tags );
623+
$tags_to_attach = isset( $_POST['ppom-attach-to-tags'] ) && is_array( $_POST['ppom-attach-to-tags'] ) ? array_map( 'sanitize_key', $_POST['ppom-attach-to-tags'] ) : false;
624+
$updated_tags = is_array( $tags_to_attach) ? count( $tags_to_attach ) : 0;
625625

626-
global $wpdb;
627-
$ppom_table = $wpdb->prefix . PPOM_TABLE_META;
628-
$wpdb->update(
629-
$ppom_table,
630-
array(
631-
'productmeta_categories' => implode( "\n", $categories_to_attach ), // NOTE: Keep the backward compatible format.
632-
'productmeta_tags' => serialize( $categories_to_tags )
633-
), // Data to update
634-
array( 'productmeta_id' => $ppom_id ), // Where clause
635-
array( '%s' ), // Data format
636-
array( '%d' ) // Where format
637-
);
626+
self::save_categories_and_tags( $ppom_id, $categories_to_attach, $tags_to_attach );
638627

639628
$response = array(
640629
'message' => "PPOM updated for {$updated_products} Products, {$updated_cat} Categories and {$updated_tags} Tags.",
@@ -644,6 +633,36 @@ function ppom_attach_ppoms() {
644633
wp_send_json( $response );
645634
}
646635

636+
/**
637+
* Save the categories and tags to the given PPOM field.
638+
*
639+
* @param int $ppom_id The ID of the PPOM field.
640+
* @param array $categories An array of categories to save.
641+
* @param array|bool $tags An array of tags to save.
642+
*
643+
* @global wpdb $wpdb WordPress database abstraction object.
644+
*
645+
*/
646+
public static function save_categories_and_tags( $ppom_id, $categories, $tags ) {
647+
global $wpdb;
648+
$ppom_table = $wpdb->prefix . PPOM_TABLE_META;
649+
650+
$data_to_update = array(
651+
'productmeta_categories' => implode( "\r\n", $categories ), // NOTE: Keep the backward compatible format.
652+
);
653+
654+
if ( is_array( $tags ) ) {
655+
$data_to_update['productmeta_tags'] = serialize( $tags );
656+
}
657+
658+
$wpdb->update(
659+
$ppom_table,
660+
$data_to_update,
661+
array( 'productmeta_id' => $ppom_id ), // Where clause
662+
array( '%s' ), // Data format
663+
array( '%d' ) // Where format
664+
);
665+
}
647666

648667
/*
649668
* Plugin Validation

classes/ppom.class.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,8 @@ function ppom_has_category_meta($product_id ) {
374374
$meta_found[] = $row->productmeta_id;
375375
} else {
376376
// making array of meta cats
377-
$meta_cat_array = explode( "\r\n", $row->productmeta_categories );
377+
378+
$meta_cat_array = preg_split('/\r\n|\n/', $row->productmeta_categories);
378379
// Now iterating the product_categories to check it's slug in meta cats
379380
foreach ( $product_categories as $cat ) {
380381
if ( in_array( $cat->slug, $meta_cat_array ) ) {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,6 @@
4141
"test:e2e:debug": "wp-scripts test-playwright --config tests/e2e/playwright.config.js --ui",
4242
"test:unit:php:setup": "wp-env start",
4343
"test:unit:php:setup:debug": "wp-env start --xdebug",
44-
"test:unit:php": "wp-env run --env-cwd='wp-content/plugins/woocommerce-product-addon' tests-wordpress vendor/bin/phpunit -c phpunit.xml.dist --verbose"
44+
"test:unit:php": "wp-env run --env-cwd='wp-content/plugins/woocommerce-product-addon' tests-wordpress vendor/bin/phpunit -c phpunit.xml --verbose"
4545
}
4646
}

tests/bootstrap.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
*/
2525
function _register_module() {
2626
require_once dirname( dirname( __FILE__ ) ) . '/woocommerce-product-addon.php';
27+
include_once PPOM_PATH . '/classes/admin.class.php';
28+
29+
$ppom_admin = new NM_PersonalizedProduct_Admin();
2730
}
2831

2932
tests_add_filter( 'muplugins_loaded', '_register_module' );

tests/e2e/specs/attach-modal.spec.js

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import { test, expect } from "@wordpress/e2e-test-utils-playwright";
55
import { createSimpleGroupField } from "../utils";
66

77
test.describe("Attach Modal", () => {
8-
test("attach to products", async ({ page, admin }) => {
8+
/**
9+
* Attach a new group field to the first product in list then check if it is rendered.
10+
*/
11+
test("attach to products and check", async ({ page, admin }) => {
912
await createSimpleGroupField(admin, page);
1013
await page.waitForTimeout(500);
1114
await admin.visitAdminPage("admin.php?page=ppom");
@@ -36,4 +39,47 @@ test.describe("Attach Modal", () => {
3639
await expect(elements.nth(i)).toBeVisible();
3740
}
3841
});
42+
43+
/**
44+
* Attach a new group to multiple categories then check.
45+
*/
46+
test("attach to multiple categories", async ({ page, admin }) => {
47+
const categoriesToUse = ["test_cat_1", "test_cat_2"];
48+
49+
await createSimpleGroupField(admin, page);
50+
await page.waitForTimeout(500);
51+
await admin.visitAdminPage("admin.php?page=ppom");
52+
53+
const firstRow = page
54+
.locator("#ppom-groups-export-form tbody tr")
55+
.first();
56+
const ppomId = await firstRow.locator("td").nth(1).innerText();
57+
await firstRow.getByText("Attach to Products").click();
58+
await page.waitForLoadState("networkidle");
59+
60+
await page.evaluate(() => {
61+
document.querySelector('#attach-to-categories > div.postbox').classList.remove('closed');
62+
});
63+
64+
const categoriesSelector = page.locator(
65+
'#ppom-product-modal select[name="ppom-attach-to-categories\\[\\]"]',
66+
);
67+
68+
// NOTE: categories created by `create-prodcuts.sh`
69+
await categoriesSelector.selectOption(
70+
categoriesToUse.map((c) => ({ value: c })),
71+
);
72+
await page.getByRole("button", { name: "Save" }).click();
73+
await page.waitForLoadState("networkidle");
74+
await page.reload();
75+
76+
for (const cat of categoriesToUse) {
77+
await page.goto(`/?product_cat=${cat}`);
78+
await page.locator('a.add_to_cart_button').first().click();
79+
80+
const elements = page.locator(`.ppom-id-${ppomId}`);
81+
const count = await elements.count();
82+
expect(count ).toBeGreaterThan(0);
83+
}
84+
});
3985
});

tests/e2e/specs/group-field-edit.spec.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ import {
1717
} from "../utils";
1818

1919
test.describe("Group Fields Edit", () => {
20+
21+
/**
22+
* Create two input fields and change their order then save and check.
23+
*/
2024
test("change fields order on saving", async ({ page, admin }) => {
2125
await createSimpleGroupField(admin, page);
2226
await page.waitForTimeout(500);
@@ -55,6 +59,9 @@ test.describe("Group Fields Edit", () => {
5559
expect(newOrderFieldIds).not.toEqual(fieldIds);
5660
});
5761

62+
/**
63+
* Create a select input with two option. Save then change their order and check again.
64+
*/
5865
test("change select option order on saving", async ({ page, admin }) => {
5966
await admin.visitAdminPage("admin.php?page=ppom");
6067

tests/test-field-saving.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
/**
3+
* Class Test_Field_Saving
4+
*
5+
* @package ppom-pro
6+
*/
7+
8+
class Test_Field_Saving extends WP_UnitTestCase {
9+
10+
/**
11+
* Test if the saved categories are in a backward compatible format and tags in a serialized format.
12+
*/
13+
public function test_saving_categories_compatibilities() {
14+
global $wpdb;
15+
16+
$table_name = $wpdb->prefix . PPOM_TABLE_META;
17+
$data = array(
18+
'productmeta_name' => 'Test Multiple Categories',
19+
'productmeta_validation' => '',
20+
'dynamic_price_display' => 'no',
21+
'send_file_attachment' => '',
22+
'show_cart_thumb' => '',
23+
'aviary_api_key' => '',
24+
'productmeta_style' => 'selector { }',
25+
'productmeta_js' => '',
26+
'productmeta_categories' => "accessories\r\nclothing",
27+
'the_meta' => '{"1":{"type":"text","title":"Test Cat","data_name":"test_cat","description":"","placeholder":"","error_message":"","maxlength":"","minlength":"","default_value":"","price":"","class":"","input_mask":"","width":"12","visibility":"everyone","visibility_role":"","conditions":{"visibility":"Show","bound":"All","rules":[{"elements":"test_cat","operators":"is","element_values":""}]},"status":"on","ppom_id":"63"}}',
28+
'productmeta_created' => '2024-10-16 11:38:45',
29+
'productmeta_tags' => 'a:1:{i:0;s:8:"test-tag";}'
30+
);
31+
32+
// Insert data into the table
33+
$result = $wpdb->insert($table_name, $data);
34+
$this->assertNotFalse( $result );
35+
36+
$field_id = $wpdb->insert_id;
37+
38+
NM_PersonalizedProduct_Admin::save_categories_and_tags( $field_id, ['accessories', 'clothing', 'test-cat'], false );
39+
40+
$saved_data = $wpdb->get_row( $wpdb->prepare( "SELECT productmeta_categories, productmeta_tags FROM $table_name WHERE productmeta_id = %d", $field_id ), ARRAY_A );
41+
42+
$expected_categories = "accessories\r\nclothing\r\ntest-cat";
43+
$this->assertEquals( $expected_categories, $saved_data['productmeta_categories'] );
44+
45+
// Tags should not be changed.
46+
$expected_tags = 'a:1:{i:0;s:8:"test-tag";}';
47+
$this->assertEquals( $expected_tags, $saved_data['productmeta_tags'] );
48+
}
49+
}

0 commit comments

Comments
 (0)