From a471be8ff923344529e7309f61a8d3a4c1747512 Mon Sep 17 00:00:00 2001 From: Brandon Shipley Date: Tue, 4 Nov 2025 01:50:05 -0800 Subject: [PATCH] subscribtion category variants / system category variants --- ...8_AddIsSystemToProductCategoryVariants.php | 25 +++++++ .../CreateSystemCategoryVariantsSeed.php | 55 ++++++++++++++ .../ProductCategoryVariantsController.php | 13 +++- src/Controller/ProductPhotosController.php | 2 +- src/Model/Entity/ProductCategoryVariant.php | 2 + .../Table/ProductCategoryVariantsTable.php | 5 ++ templates/ProductCategoryVariants/add.php | 2 +- templates/ProductCategoryVariants/edit.php | 2 +- templates/ProductPhotos/view.php | 1 - .../element/ProductCategoryVariants/form.php | 27 ++++++- .../ProductCategoryVariantOptionsFixture.php | 37 ++++++++++ .../ProductCategoryVariantsFixture.php | 21 +++++- .../ProductCategoryVariantsControllerTest.php | 74 ++++++++++++++++++- 13 files changed, 254 insertions(+), 12 deletions(-) create mode 100644 config/Migrations/20251103150008_AddIsSystemToProductCategoryVariants.php create mode 100644 config/Seeds/CreateSystemCategoryVariantsSeed.php diff --git a/config/Migrations/20251103150008_AddIsSystemToProductCategoryVariants.php b/config/Migrations/20251103150008_AddIsSystemToProductCategoryVariants.php new file mode 100644 index 0000000..864f37e --- /dev/null +++ b/config/Migrations/20251103150008_AddIsSystemToProductCategoryVariants.php @@ -0,0 +1,25 @@ +table('product_category_variants'); + $table->addColumn('is_system_variant', 'boolean', [ + 'default' => false, + 'limit' => 11, + 'null' => false, + ]); + $table->update(); + } +} diff --git a/config/Seeds/CreateSystemCategoryVariantsSeed.php b/config/Seeds/CreateSystemCategoryVariantsSeed.php new file mode 100644 index 0000000..0f781fe --- /dev/null +++ b/config/Seeds/CreateSystemCategoryVariantsSeed.php @@ -0,0 +1,55 @@ + \Cake\Utility\Text::uuid(), + 'name' => 'Subscription Length', + 'product_category_id' => null, + 'enabled' => true, + 'is_system_variant' => true, + ], + [ + 'id' => \Cake\Utility\Text::uuid(), + 'name' => 'Subscription Length Units', + 'product_category_id' => null, + 'enabled' => true, + 'is_system_variant' => true, + ], + ]; + $table = $this->table('product_category_variants'); + $toInsert = []; + foreach ($data as $singleRecordToInsert) { + $stmt = $this->query('SELECT * FROM product_category_variants WHERE name="' . $singleRecordToInsert['name'] . '" AND product_category_id IS NULL;'); // returns PDOStatement + $rows = $stmt->fetchAll(); // returns the result as an array + if ($rows) { + continue; + } + $toInsert[] = $singleRecordToInsert; + } + if ($toInsert) { + $table->insert($data)->save(); + } + } +} diff --git a/src/Controller/ProductCategoryVariantsController.php b/src/Controller/ProductCategoryVariantsController.php index 184cf94..7d3b6b8 100644 --- a/src/Controller/ProductCategoryVariantsController.php +++ b/src/Controller/ProductCategoryVariantsController.php @@ -111,21 +111,32 @@ class ProductCategoryVariantsController extends AppController // if ($this->request->getSession()->read('Auth.User.id')) { // $postData['created_by'] = $this->request->getSession()->read('Auth.User.id'); // } + $postData = $productCategoryVariant->is_system_variant ? ['product_category_variant_options' => $this->request->getData('product_category_variant_options')] : $postData; $saveOptions = [ + 'fields' => $productCategoryVariant->is_system_variant ? [ + 'product_category_variant_options', + ] : [ + 'name', + 'product_category_id', + 'enabled', + 'product_category_variant_options', + ], 'associated' => [ 'ProductCategoryVariantOptions' ], ]; $productCategoryVariant = $productCategoryVariantsTable->patchEntity($productCategoryVariant, $postData, $saveOptions); +// dd($postData); if ($productCategoryVariantsTable->save($productCategoryVariant, $saveOptions)) { $this->Flash->success(__('The product category variant has been saved.')); return $this->redirect(['action' => 'index']); } +// dd($productCategoryVariant->getErrors()); $this->Flash->error(__('The product category variant could not be saved. Please, try again.')); } $productCategories = $productCategoryVariantsTable->ProductCategories->find('list', keyField: 'internal_id', valueField: 'name')->all(); - $products = $productCategoryVariantsTable->Products->find('list', limit: 200)->where(['product_category_id' => $productCategoryVariant->product_category_id])->all(); + $products = isset($productCategoryVariant->product_category_id) ? $productCategoryVariantsTable->Products->find('list', limit: 200)->where(['product_category_id' => $productCategoryVariant->product_category_id])->all() : []; $this->set(compact('productCategoryVariant', 'productCategories', 'products')); } diff --git a/src/Controller/ProductPhotosController.php b/src/Controller/ProductPhotosController.php index a6f67bd..7668f47 100644 --- a/src/Controller/ProductPhotosController.php +++ b/src/Controller/ProductPhotosController.php @@ -144,7 +144,7 @@ class ProductPhotosController extends AppController return $this->redirect(['action' => 'index']); } - dd($productPhoto->getErrors()); +// dd($productPhoto->getErrors()); $this->Flash->error(__('The product photo could not be saved. Please, try again.')); } $productCategory = $productPhoto->product_category_id ? $productPhotosTable->ProductCategories->find()->where(['internal_id' => $productPhoto->product_category_id ?? '-1'])->first() : null; diff --git a/src/Model/Entity/ProductCategoryVariant.php b/src/Model/Entity/ProductCategoryVariant.php index d56fa7b..9719859 100644 --- a/src/Model/Entity/ProductCategoryVariant.php +++ b/src/Model/Entity/ProductCategoryVariant.php @@ -14,6 +14,7 @@ use Cake\ORM\Entity; * @property string|null $product_category_id * @property string|null $product_id * @property bool $enabled + * @property bool $is_system_variant * * @property ProductCategory|EntityInterface $product_category * @property ProductCategoryVariantOption[]|EntityInterface[] $product_category_variant_options @@ -34,6 +35,7 @@ class ProductCategoryVariant extends Entity 'product_category_id' => true, 'product_id' => true, 'enabled' => true, + 'is_system_variant' => true, // entities 'product_category' => false, diff --git a/src/Model/Table/ProductCategoryVariantsTable.php b/src/Model/Table/ProductCategoryVariantsTable.php index 47943a7..2cbea7f 100644 --- a/src/Model/Table/ProductCategoryVariantsTable.php +++ b/src/Model/Table/ProductCategoryVariantsTable.php @@ -70,6 +70,7 @@ class ProductCategoryVariantsTable extends Table 'className' => 'CakeProducts.ProductCategoryVariantOptions', 'dependent' => true, 'cascadeCallbacks' => true, + 'saveStrategy' => 'replace', ]); $this->hasMany('ProductVariants', [ @@ -102,6 +103,10 @@ class ProductCategoryVariantsTable extends Table ->uuid('product_id') ->allowEmptyString('product_id'); + $validator + ->boolean('is_system_variant') + ->allowEmptyString('is_system_variant'); + $validator ->boolean('enabled') ->requirePresence('enabled', 'create') diff --git a/templates/ProductCategoryVariants/add.php b/templates/ProductCategoryVariants/add.php index 2a21bb5..49e9ed7 100644 --- a/templates/ProductCategoryVariants/add.php +++ b/templates/ProductCategoryVariants/add.php @@ -18,7 +18,7 @@ Form->create($productCategoryVariant) ?>
- element('ProductCategoryVariants/form'); ?> + element('ProductCategoryVariants/form', ['productCategoryVariant' => $productCategoryVariant]); ?>
Form->button(__('Submit')) ?> Form->end() ?> diff --git a/templates/ProductCategoryVariants/edit.php b/templates/ProductCategoryVariants/edit.php index e90f9fd..e0c8c6a 100644 --- a/templates/ProductCategoryVariants/edit.php +++ b/templates/ProductCategoryVariants/edit.php @@ -23,7 +23,7 @@ Form->create($productCategoryVariant) ?>
- element('ProductCategoryVariants/form'); ?> + element('ProductCategoryVariants/form', ['productCategoryVariant' => $productCategoryVariant]); ?>
Form->button(__('Submit')) ?> Form->end() ?> diff --git a/templates/ProductPhotos/view.php b/templates/ProductPhotos/view.php index f4fa6b3..b1528a3 100644 --- a/templates/ProductPhotos/view.php +++ b/templates/ProductPhotos/view.php @@ -26,7 +26,6 @@ id) ?> - hasValue('product_category') ? $this->Html->link($productPhoto->product_category->name, ['controller' => 'ProductCategories', 'action' => 'view', $productPhoto->product_category->internal_id]) : '' ?> diff --git a/templates/element/ProductCategoryVariants/form.php b/templates/element/ProductCategoryVariants/form.php index b0f99d1..6bfdec0 100644 --- a/templates/element/ProductCategoryVariants/form.php +++ b/templates/element/ProductCategoryVariants/form.php @@ -2,11 +2,30 @@ /** * @var array $productCategories * @var array $products + * @var \CakeProducts\Model\Entity\ProductCategoryVariant|\Cake\Datasource\EntityInterface $productCategoryVariant */ -echo $this->Form->control('name'); -echo $this->Form->control('product_category_id', ['options' => $productCategories, 'empty' => true]); -echo $this->Form->control('product_id', ['options' => $products, 'empty' => true]); -echo $this->Form->control('enabled'); + +$nameOptions = []; +$categoryOptions = ['options' => $productCategories, 'empty' => true]; +$enabledOptions = []; +if ($productCategoryVariant->is_system_variant) { + $nameOptions = [ + 'disabled' => true, + 'readonly' => true, + ]; + $categoryOptions = $categoryOptions + [ + 'disabled' => true, + 'readonly' => true, + ]; + $enabledOptions = [ + 'disabled' => true, + 'readonly' => true, + ]; + +} +echo $this->Form->control('name', $nameOptions); +echo $this->Form->control('product_category_id', $categoryOptions); +echo $this->Form->control('enabled', $enabledOptions); ?> ' . $this->Html->link('Add Option', '#', [ 'id' => 'add-option-button', diff --git a/tests/Fixture/ProductCategoryVariantOptionsFixture.php b/tests/Fixture/ProductCategoryVariantOptionsFixture.php index 9bade3a..c21c0cc 100644 --- a/tests/Fixture/ProductCategoryVariantOptionsFixture.php +++ b/tests/Fixture/ProductCategoryVariantOptionsFixture.php @@ -56,6 +56,43 @@ class ProductCategoryVariantOptionsFixture extends TestFixture 'modified' => '2025-07-04 12:00:00', 'enabled' => 1, ], + + [ + 'id' => '5a386e9f-6e7a-4ae7-9360-c8e529f78111', + 'variant_value' => 'Months', + 'variant_label' => 'Months', + 'product_category_variant_id' => '5a386e9f-6e7a-4ae7-9360-c8e529f78111', + 'created' => '2025-07-04 12:00:00', + 'modified' => '2025-07-04 12:00:00', + 'enabled' => 1, + ], + [ + 'id' => '5a386e9f-6e7a-4ae7-9360-c8e529f78112', + 'variant_value' => 'Years', + 'variant_label' => 'Years', + 'product_category_variant_id' => '5a386e9f-6e7a-4ae7-9360-c8e529f78111', + 'created' => '2025-07-04 12:00:00', + 'modified' => '2025-07-04 12:00:00', + 'enabled' => 1, + ], + [ + 'id' => '5a386e9f-6e7a-4ae7-9360-c8e529f22221', + 'variant_value' => '6', + 'variant_label' => '6', + 'product_category_variant_id' => '5a386e9f-6e7a-4ae7-9360-c8e529f78222', + 'created' => '2025-07-04 12:00:00', + 'modified' => '2025-07-04 12:00:00', + 'enabled' => 1, + ], + [ + 'id' => '5a386e9f-6e7a-4ae7-9360-c8e529f22222', + 'variant_value' => 12, + 'variant_label' => 12, + 'product_category_variant_id' => '5a386e9f-6e7a-4ae7-9360-c8e529f78222', + 'created' => '2025-07-04 12:00:00', + 'modified' => '2025-07-04 12:00:00', + 'enabled' => 1, + ], ]; parent::init(); } diff --git a/tests/Fixture/ProductCategoryVariantsFixture.php b/tests/Fixture/ProductCategoryVariantsFixture.php index 84cc369..0577222 100644 --- a/tests/Fixture/ProductCategoryVariantsFixture.php +++ b/tests/Fixture/ProductCategoryVariantsFixture.php @@ -18,17 +18,34 @@ class ProductCategoryVariantsFixture extends TestFixture public function init(): void { $this->records = [ + [ + 'id' => '5a386e9f-6e7a-4ae7-9360-c8e529f78111', + 'name' => 'Subscription Length Units', + 'product_category_id' => null, + 'enabled' => true, + 'is_system_variant' => true, + ], + [ + 'id' => '5a386e9f-6e7a-4ae7-9360-c8e529f78222', + 'name' => 'Subscription Length', + 'product_category_id' => null, + 'enabled' => true, + 'is_system_variant' => true, + ], + [ 'id' => '5a386e9f-6e7a-4ae7-9360-c8e529f78d93', 'name' => 'Color', 'product_category_id' => 'db4b4273-eddc-46d4-93c8-45cf7c6e058e', - 'enabled' => 1, + 'enabled' => true, + 'is_system_variant' => false, ], [ 'id' => '5a386e9f-6e7a-4ae7-9360-c8e529f78d94', 'name' => 'AWG', 'product_category_id' => 'db4b4273-eddc-46d4-93c8-45cf7c6e058e', - 'enabled' => 1, + 'enabled' => true, + 'is_system_variant' => false, ], ]; parent::init(); diff --git a/tests/TestCase/Controller/ProductCategoryVariantsControllerTest.php b/tests/TestCase/Controller/ProductCategoryVariantsControllerTest.php index 0a909fc..c9006ed 100644 --- a/tests/TestCase/Controller/ProductCategoryVariantsControllerTest.php +++ b/tests/TestCase/Controller/ProductCategoryVariantsControllerTest.php @@ -37,6 +37,7 @@ class ProductCategoryVariantsControllerTest extends BaseControllerTest */ protected array $fixtures = [ 'plugin.CakeProducts.ProductCategoryVariants', + 'plugin.CakeProducts.ProductCategoryVariantOptions', 'plugin.CakeProducts.ProductVariants', 'plugin.CakeProducts.ProductCategories', 'plugin.CakeProducts.Products', @@ -289,6 +290,77 @@ class ProductCategoryVariantsControllerTest extends BaseControllerTest // assert saved properly below } + /** + * Test edit method + * + * Tests a PUT request to the edit action with a logged in user + * + * @uses \CakeProducts\Controller\ProductCategoryVariantsController::edit() + * @throws Exception + * + * @return void + */ + public function testEditPutLoggedInSuccessSystemVariant(): void + { + //$this->loginUserByRole('admin'); + $id = '5a386e9f-6e7a-4ae7-9360-c8e529f78222'; // subscription length + $before = $this->ProductCategoryVariants->get($id); + $cntBeforeOptions = $this->ProductCategoryVariantOptions + ->find() + ->where(['product_category_variant_id' => $id]) + ->toArray(); + +// $this->assertEquals(2, count($cntBeforeOptions)); + $url = [ + 'plugin' => 'CakeProducts', + 'controller' => 'ProductCategoryVariants', + 'action' => 'edit', + $id, + ]; + $data = [ + // test new data here + 'name' => 'updated name', + 'product_category_id' => 'db4b4273-eddc-46d4-93c8-45cf7c6e058e', + 'enabled' => false, + 'product_category_variant_options' => [ + [ + 'variant_value' => '14', + 'variant_label' => '14', + 'enabled' => true, + ], + [ + 'id' => '5a386e9f-6e7a-4ae7-9360-c8e529f22221', + 'variant_value' => '6', + 'variant_label' => '6', + 'enabled' => true, + ], + [ + 'id' => '5a386e9f-6e7a-4ae7-9360-c8e529f22222', + 'variant_value' => 12, + 'variant_label' => 12, + 'enabled' => true, + ], + ], + ]; + $this->put($url, $data); + + $this->assertResponseCode(302); + $this->assertRedirectContains('product-category-variants'); + + $after = $this->ProductCategoryVariants->get($id); + $cntAfterOptions = $this->ProductCategoryVariantOptions + ->find() + ->where(['product_category_variant_id' => $id]) + ->toArray(); + + $this->assertEquals(count($cntBeforeOptions) + 1, count($cntAfterOptions)); + + $this->assertEquals($before->name, $after->name); + $this->assertNull($after->product_category_id); + $this->assertTrue($after->enabled); + // assert saved properly below + } + /** * Test edit method * @@ -312,7 +384,7 @@ class ProductCategoryVariantsControllerTest extends BaseControllerTest ]; $data = [ 'name' => '', - 'product_category_id' => 'db4b4273-eddc-46d4-93c8-45cf7c6e058e', + 'product_category_id' => 'NOT A VALID ID', 'enabled' => true, ]; $this->put($url, $data);