From 41c5f7169e9ed98f182e5209439f2731acef81e5 Mon Sep 17 00:00:00 2001 From: Brandon Shipley Date: Fri, 31 Oct 2025 01:29:56 -0700 Subject: [PATCH] toggle behavior on skus for default product sku, photos for default category photo and default product photo --- src/Model/Behavior/SecondToggleBehavior.php | 37 +++++++ src/Model/Table/ProductPhotosTable.php | 17 ++- src/Model/Table/ProductSkusTable.php | 7 ++ tests/Fixture/ProductPhotosFixture.php | 37 ++++++- .../ProductPhotosControllerTest.php | 103 +++++++++++++++++- 5 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 src/Model/Behavior/SecondToggleBehavior.php diff --git a/src/Model/Behavior/SecondToggleBehavior.php b/src/Model/Behavior/SecondToggleBehavior.php new file mode 100644 index 0000000..91b7b64 --- /dev/null +++ b/src/Model/Behavior/SecondToggleBehavior.php @@ -0,0 +1,37 @@ + + */ + protected array $_defaultConfig = [ + 'field' => 'primary', + 'on' => 'afterSave', // afterSave (without transactions) or beforeSave (with transactions) + 'scopeFields' => [], + 'scope' => [], + 'findOrder' => null, // null = autodetect modified/created, false to disable + 'implementedMethods' => [], // to prevent conflict with public toggleField method + ]; +} diff --git a/src/Model/Table/ProductPhotosTable.php b/src/Model/Table/ProductPhotosTable.php index c7c2c22..33c8609 100644 --- a/src/Model/Table/ProductPhotosTable.php +++ b/src/Model/Table/ProductPhotosTable.php @@ -58,7 +58,22 @@ class ProductPhotosTable extends Table ); $this->addBehavior('Timestamp'); - + $this->addBehavior('Tools.Toggle', [ + 'field' => 'primary_photo', + 'scopeFields' => ['product_id'], + 'scope' => [ + 'deleted IS' => null, + 'product_id IS NOT' => null, + ], + ]); + $this->addBehavior('CakeProducts.SecondToggle', [ + 'field' => 'primary_category_photo', + 'scopeFields' => ['product_category_id'], + 'scope' => [ + 'deleted IS' => null, + 'product_category_id IS NOT' => null, + ], + ]); $this->belongsTo('Products', [ 'foreignKey' => 'product_id', 'joinType' => 'LEFT', diff --git a/src/Model/Table/ProductSkusTable.php b/src/Model/Table/ProductSkusTable.php index 3f0c2cb..63ffe4f 100644 --- a/src/Model/Table/ProductSkusTable.php +++ b/src/Model/Table/ProductSkusTable.php @@ -59,6 +59,13 @@ class ProductSkusTable extends Table ); $this->addBehavior('Timestamp'); + $this->addBehavior('Tools.Toggle', [ + 'field' => 'default_sku', + 'scopeFields' => ['product_id'], + 'scope' => [ + 'deleted IS' => null, + ], + ]); $this->belongsTo('Products', [ 'className' => 'CakeProducts.Products', diff --git a/tests/Fixture/ProductPhotosFixture.php b/tests/Fixture/ProductPhotosFixture.php index ef59da7..ccda639 100644 --- a/tests/Fixture/ProductPhotosFixture.php +++ b/tests/Fixture/ProductPhotosFixture.php @@ -22,7 +22,7 @@ class ProductPhotosFixture extends TestFixture 'id' => '2c386086-f4c5-4093-bea5-ee9c29479f58', 'product_id' => 'cfc98a9a-29b2-44c8-b587-8156adc05317', 'product_sku_id' => null, - 'product_category_id' => null, + 'product_category_id' => '6d223283-361b-4f9f-a7f1-c97aa0ca4c23', 'photo_dir' => 'cfc98a9a-29b2-44c8-b587-8156adc05317', 'photo_filename' => '2c386086-f4c5-4093-bea5-ee9c29479f58.png', 'primary_photo' => 1, @@ -38,10 +38,10 @@ class ProductPhotosFixture extends TestFixture 'id' => '2c386086-f4c5-4093-bea5-ee9c29479f51', 'product_id' => 'cfc98a9a-29b2-44c8-b587-8156adc05317', 'product_sku_id' => null, - 'product_category_id' => '3c2377c5-b97c-4bc9-9660-8f77b4893d8b', + 'product_category_id' => '6d223283-361b-4f9f-a7f1-c97aa0ca4c23', 'photo_dir' => 'categories', 'photo_filename' => '2c386086-f4c5-4093-bea5-ee9c29479f51.png', - 'primary_photo' => 1, + 'primary_photo' => 0, 'primary_category_photo' => 1, 'photo_position' => 100, 'enabled' => 1, @@ -49,6 +49,37 @@ class ProductPhotosFixture extends TestFixture 'modified' => '2025-08-10 04:32:10', 'deleted' => null, ], + + [ + 'id' => '2c386086-f4c5-4093-bea5-ee9c29479f50', + 'product_id' => 'cfc98a9a-29b2-44c8-b587-8156adc05317', + 'product_sku_id' => null, + 'product_category_id' => '6d223283-361b-4f9f-a7f1-c97aa0ca4c23', + 'photo_dir' => 'cfc98a9a-29b2-44c8-b587-8156adc05317', + 'photo_filename' => '2c386086-f4c5-4093-bea5-ee9c29479f58.png', + 'primary_photo' => 0, + 'primary_category_photo' => 0, + 'photo_position' => 100, + 'enabled' => 1, + 'created' => '2025-08-10 04:32:10', + 'modified' => '2025-08-10 04:32:10', + 'deleted' => null, + ], + [ + 'id' => '2c386086-f4c5-4093-bea5-ee9c29479f53', + 'product_id' => 'cfc98a9a-29b2-44c8-b587-8156adc05317', + 'product_sku_id' => null, + 'product_category_id' => '6d223283-361b-4f9f-a7f1-c97aa0ca4c23', + 'photo_dir' => 'categories', + 'photo_filename' => '2c386086-f4c5-4093-bea5-ee9c29479f51.png', + 'primary_photo' => 0, + 'primary_category_photo' => 0, + 'photo_position' => 100, + 'enabled' => 1, + 'created' => '2025-08-10 04:32:10', + 'modified' => '2025-08-10 04:32:10', + 'deleted' => null, + ], ]; parent::init(); } diff --git a/tests/TestCase/Controller/ProductPhotosControllerTest.php b/tests/TestCase/Controller/ProductPhotosControllerTest.php index bab9d6d..fce9ff8 100644 --- a/tests/TestCase/Controller/ProductPhotosControllerTest.php +++ b/tests/TestCase/Controller/ProductPhotosControllerTest.php @@ -148,7 +148,7 @@ class ProductPhotosControllerTest extends BaseControllerTest * * @uses \CakeProducts\Controller\ProductPhotosController::add */ - public function testAddPostLoggedInSuccess(): void + public function testAddPostLoggedInSuccessDefaultProductPhoto(): void { $cntBefore = $this->ProductPhotos->find()->count(); @@ -166,6 +166,12 @@ class ProductPhotosControllerTest extends BaseControllerTest if (!copy($file, $toUseFile)) { $this->fail('Failed to copy test image'); } + $productId = 'cfc98a9a-29b2-44c8-b587-8156adc05317'; + $primaryPhotosCountBefore = $this->ProductPhotos->find()->where(['product_id' => $productId, 'primary_photo' => true])->count(); + $primaryPhotoBefore = $this->ProductPhotos->find()->where(['product_id' => $productId, 'primary_photo' => true])->first(); + + $this->assertEquals(1, $primaryPhotosCountBefore); + $image = new UploadedFile( $toUseFile, // stream or path to file representing the temp file 12345, // the filesize in bytes @@ -179,9 +185,10 @@ class ProductPhotosControllerTest extends BaseControllerTest ], ]); $data = [ - 'product_id' => 'cfc98a9a-29b2-44c8-b587-8156adc05317', + 'product_id' => $productId, 'product_sku_id' => '', - 'primary_photo' => 0, + 'primary_photo' => 1, + 'primary_category_photo' => 1, 'photo' => $image, 'enabled' => 1, ]; @@ -191,6 +198,96 @@ class ProductPhotosControllerTest extends BaseControllerTest $cntAfter = $this->ProductPhotos->find()->count(); $this->assertEquals($cntBefore + 1, $cntAfter); + + $new = $this->ProductPhotos->find()->where(['product_id' => $productId])->orderBy(['created' => 'DESC'])->first(); + + $this->assertTrue($new->primary_photo); + $this->assertTrue($new->primary_category_photo); + $this->assertEquals($productId, $new->product_id); + $primaryPhotosCountAfter = $this->ProductPhotos->find()->where(['product_id' => $productId, 'primary_photo' => true])->count(); + $primaryPhotoAfter = $this->ProductPhotos->find()->where(['product_id' => $productId, 'primary_photo' => true])->first(); + + $this->assertEquals(1, $primaryPhotosCountAfter); + $this->assertNotEquals($primaryPhotoBefore->id, $primaryPhotoAfter->id); + } + + /** + * Test add method + * + * Tests a POST request to the add action with a logged in user + * + * @return void + * @throws Exception + * + * @uses \CakeProducts\Controller\ProductPhotosController::add + */ + public function testAddPostLoggedInSuccessDefaultCategoryPhoto(): void + { + $cntBefore = $this->ProductPhotos->find()->count(); + + // $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeProducts', + 'controller' => 'ProductPhotos', + 'action' => 'add', + ]; + $file = Configure::readOrFail('App.paths.testWebroot') . 'images' . DS . 'cake_icon.png'; + $toUseFile = Configure::readOrFail('App.paths.testWebroot') . 'images' . DS . 'cake_icon_copy.png'; + if (!file_exists($file)) { + $this->fail('Test image did not exist'); + } + if (!copy($file, $toUseFile)) { + $this->fail('Failed to copy test image'); + } + $categoryId = '6d223283-361b-4f9f-a7f1-c97aa0ca4c23'; + $productId = 'cfc98a9a-29b2-44c8-b587-8156adc05317'; + + $primaryCategoryPhotosCountBefore = $this->ProductPhotos->find()->where(['product_category_id' => $categoryId, 'primary_category_photo' => true])->count(); + $primaryCategoryPhotoBefore = $this->ProductPhotos->find()->where(['product_category_id' => $categoryId, 'primary_category_photo' => true])->first(); + + $this->assertEquals(1, $primaryCategoryPhotosCountBefore); + + + $image = new UploadedFile( + $toUseFile, // stream or path to file representing the temp file + 12345, // the filesize in bytes + UPLOAD_ERR_OK, // the upload/error status + 'cake_icon.png', // the filename as sent by the client + 'image/png' // the mimetype as sent by the client + ); + $this->configRequest([ + 'files' => [ + 'photo' => $image, + ], + ]); + $data = [ + 'product_id' => $productId, + 'product_category_id' => $categoryId, + 'product_sku_id' => '', + 'primary_photo' => 0, + 'primary_category_photo' => 1, + 'photo' => $image, + 'enabled' => 1, + ]; + $this->post($url, $data); + $this->assertResponseCode(302); + $this->assertRedirectContains('product-photos'); + + $cntAfter = $this->ProductPhotos->find()->count(); + $this->assertEquals($cntBefore + 1, $cntAfter); + + $new = $this->ProductPhotos->find()->where(['product_category_id' => $categoryId])->orderBy(['created' => 'DESC'])->first(); + + $this->assertFalse($new->primary_photo); + $this->assertTrue($new->primary_category_photo); + $this->assertEquals($categoryId, $new->product_category_id); + + $primaryCategoryPhotosCountAfter = $this->ProductPhotos->find()->where(['product_category_id' => $categoryId, 'primary_category_photo' => true])->count(); + $primaryCategoryPhotoAfter = $this->ProductPhotos->find()->where(['product_category_id' => $categoryId, 'primary_category_photo' => true])->first(); + + $this->assertEquals(1, $primaryCategoryPhotosCountAfter); + $this->assertNotEquals($primaryCategoryPhotoBefore->id, $primaryCategoryPhotoAfter->id); + } /**