add soft delete via muffin/trash to all product related tables
CI / testsuite (mysql, 8.1, ) (push) Failing after 0s Details
CI / testsuite (mysql, 8.4, ) (push) Failing after 0s Details
CI / testsuite (pgsql, 8.1, ) (push) Failing after 0s Details
CI / testsuite (pgsql, 8.4, ) (push) Failing after 0s Details
CI / testsuite (sqlite, 8.1, ) (push) Failing after 0s Details
CI / testsuite (sqlite, 8.1, prefer-lowest) (push) Failing after 0s Details
CI / testsuite (sqlite, 8.4, ) (push) Failing after 0s Details
CI / Coding Standard & Static Analysis (push) Failing after 0s Details

This commit is contained in:
Brandon Shipley 2025-04-08 01:30:40 -07:00
parent 013bec1b54
commit cf7c67763c
Signed by: bmfs
GPG Key ID: 14E38571D8BB0DE4
33 changed files with 188 additions and 11 deletions

View File

@ -7,6 +7,7 @@
"require": {
"php": ">=8.1",
"dereuromark/cakephp-tools": "^3.9",
"muffin/trash": "^4.2",
"hi-powered-dev/cheese-cake": "dev-prod",
"cakephp/cakephp": "^5.0.1"
},

View File

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
use Migrations\BaseMigration;
class AddSoftDeleteToAllTables extends BaseMigration
{
/**
* Change Method.
*
* More information on this method is available here:
* https://book.cakephp.org/migrations/4/en/migrations.html#the-change-method
* @return void
*/
public function change(): void
{
$table = $this->table('products');
$table->addColumn('deleted', 'datetime', [
'default' => null,
'null' => true,
]);
$table->update();
$table = $this->table('product_category_attributes');
$table->addColumn('deleted', 'datetime', [
'default' => null,
'null' => true,
]);
$table->update();
$table = $this->table('product_category_attribute_options');
$table->addColumn('deleted', 'datetime', [
'default' => null,
'null' => true,
]);
$table->update();
$table = $this->table('product_categories');
$table->addColumn('deleted', 'datetime', [
'default' => null,
'null' => true,
]);
$table->update();
$table = $this->table('product_catalogs');
$table->addColumn('deleted', 'datetime', [
'default' => null,
'null' => true,
]);
$table->update();
$table = $this->table('external_product_catalogs_product_catalogs');
$table->addColumn('deleted', 'datetime', [
'default' => null,
'null' => true,
]);
$table->update();
}
}

View File

@ -141,6 +141,11 @@ class ProductCategoriesController extends AppController
$productCategoriesTable = $this->getTable();
$productCategory = $productCategoriesTable->get($id);
// $productCategoriesTable->behaviors()->get('Tree')->setConfig([
// 'scope' => [
// 'product_catalog_id' => $productCategory->product_catalog_id,
// ],
// ]);
if ($productCategoriesTable->delete($productCategory)) {
$this->Flash->success(__('The product category has been deleted.'));
} else {

View File

@ -14,6 +14,7 @@ use Cake\ORM\Entity;
* @property string $product_catalog_id
* @property DateTime $created
* @property bool $enabled
* @property DateTime|null $deleted
*
* @property ExternalProductCatalog $external_product_catalog
* @property ProductCatalog $product_catalog
@ -34,6 +35,8 @@ class ExternalProductCatalogsProductCatalog extends Entity
'product_catalog_id' => true,
'created' => true,
'enabled' => true,
'deleted' => true,
// entities
'external_product_catalog' => true,
'product_catalog' => true,
];

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace CakeProducts\Model\Entity;
use Cake\I18n\DateTime;
use Cake\ORM\Entity;
/**
@ -12,6 +13,7 @@ use Cake\ORM\Entity;
* @property string $name
* @property string $product_category_id
* @property \CakeProducts\Model\Enum\ProductProductTypeId $product_type_id
* @property DateTime|null $deleted
*
* @property \CakeProducts\Model\Entity\ProductCategory $product_category
* @property \CakeProducts\Model\Entity\ProductAttribute[] $product_attributes
@ -31,7 +33,9 @@ class Product extends Entity
'name' => true,
'product_category_id' => true,
'product_type_id' => true,
'product_category' => true,
'deleted' => true,
// entities
'product_category' => false,
'product_attributes' => true,
];
}

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace CakeProducts\Model\Entity;
use Cake\I18n\DateTime;
use Cake\ORM\Entity;
/**
@ -13,6 +14,7 @@ use Cake\ORM\Entity;
* @property string $product_category_attribute_id
* @property string|null $attribute_value
* @property string|null $product_category_attribute_option_id
* @property DateTime|null $deleted
*
* @property Product $product
* @property ProductCategoryAttribute $product_category_attribute
@ -34,6 +36,8 @@ class ProductAttribute extends Entity
'product_category_attribute_id' => true,
'attribute_value' => true,
'product_category_attribute_option_id' => true,
'deleted' => true,
// entities
'product' => false,
'product_category_attribute' => false,
'product_category_attribute_option' => false,

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace CakeProducts\Model\Entity;
use Cake\I18n\DateTime;
use Cake\ORM\Entity;
/**
@ -12,6 +13,7 @@ use Cake\ORM\Entity;
* @property string $name
* @property string|null $catalog_description
* @property bool $enabled
* @property DateTime|null $deleted
*
* @property ProductCategory[] $product_categories
* @property ExternalProductCatalog[] $external_product_catalogs
@ -31,6 +33,8 @@ class ProductCatalog extends Entity
'name' => true,
'catalog_description' => true,
'enabled' => true,
'deleted' => true,
// entities
'product_categories' => true,
'external_product_catalogs' => true,
];

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace CakeProducts\Model\Entity;
use Cake\I18n\DateTime;
use Cake\ORM\Entity;
/**
@ -17,6 +18,7 @@ use Cake\ORM\Entity;
* @property int $lft
* @property int $rght
* @property bool $enabled
* @property DateTime|null $deleted
*
* @property \CakeProducts\Model\Entity\ProductCatalog $product_catalog
* @property \CakeProducts\Model\Entity\ParentProductCategory $parent_product_category
@ -42,6 +44,8 @@ class ProductCategory extends Entity
'lft' => true,
'rght' => true,
'enabled' => true,
'deleted' => true,
// entities
'product_catalog' => true,
'parent_product_category' => true,
'child_product_categories' => true,

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace CakeProducts\Model\Entity;
use Cake\I18n\DateTime;
use Cake\ORM\Entity;
/**
@ -13,6 +14,7 @@ use Cake\ORM\Entity;
* @property string|null $product_category_id
* @property int $attribute_type_id
* @property bool $enabled
* @property DateTime|null $deleted
*
* @property ProductCategory $product_category
* @property ProductCategoryAttributeOption[] $product_category_attribute_options
@ -33,6 +35,8 @@ class ProductCategoryAttribute extends Entity
'product_category_id' => true,
'attribute_type_id' => true,
'enabled' => true,
'deleted' => true,
// entities
'product_category' => true,
'product_category_attribute_options' => true,
];

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace CakeProducts\Model\Entity;
use Cake\I18n\DateTime;
use Cake\ORM\Entity;
/**
@ -13,6 +14,7 @@ use Cake\ORM\Entity;
* @property string $attribute_value
* @property string $attribute_label
* @property bool $enabled
* @property DateTime|null $deleted
*
* @property ProductCategoryAttribute $product_category_attribute
*/
@ -32,6 +34,8 @@ class ProductCategoryAttributeOption extends Entity
'attribute_value' => true,
'attribute_label' => true,
'enabled' => true,
'deleted' => true,
// entities
'product_category_attribute' => true,
];
}

View File

@ -71,6 +71,8 @@ class ExternalProductCatalogsProductCatalogsTable extends Table
// 'foreignKey' => 'product_catalog_id',
'joinType' => 'INNER',
]);
$this->addBehavior('Muffin/Trash.Trash');
}
/**
@ -92,6 +94,10 @@ class ExternalProductCatalogsProductCatalogsTable extends Table
$validator
->boolean('enabled');
$validator
->dateTime('deleted')
->allowEmptyDateTime('deleted');
return $validator;
}

View File

@ -67,7 +67,11 @@ class ExternalProductCatalogsTable extends Table
$this->hasMany('ExternalProductCatalogsProductCatalogs', [
'foreignKey' => 'external_product_catalog_id',
'className' => 'CakeProducts.ExternalProductCatalogsProductCatalogs',
'dependent' => true,
'cascadeCallbacks' => true,
]);
$this->addBehavior('Muffin/Trash.Trash');
}
/**

View File

@ -63,6 +63,7 @@ class ProductAttributesTable extends Table
'foreignKey' => 'product_category_attribute_option_id',
'className' => 'CakeProducts.ProductCategoryAttributeOptions',
]);
}
/**
@ -90,6 +91,10 @@ class ProductAttributesTable extends Table
->uuid('product_category_attribute_option_id')
->allowEmptyString('product_category_attribute_option_id');
$validator
->dateTime('deleted')
->allowEmptyDateTime('deleted');
return $validator;
}

View File

@ -57,6 +57,8 @@ class ProductCatalogsTable extends Table
'through' => 'ExternalProductCatalogsProductCatalogs',
'className' => 'CakeProducts.ExternalProductCatalogs',
]);
$this->addBehavior('Muffin/Trash.Trash');
}
/**
@ -84,6 +86,10 @@ class ProductCatalogsTable extends Table
->requirePresence('enabled', 'create')
->notEmptyString('enabled');
$validator
->dateTime('deleted')
->allowEmptyDateTime('deleted');
return $validator;
}

View File

@ -65,7 +65,9 @@ class ProductCategoriesTable extends Table
$this->setEntityClass(
Configure::read('CakeProducts.ProductCategories.entity', 'CakeProducts\Model\Entity\ProductCategory')
);
$this->addBehavior('Tree');
$this->addBehavior('Tree', [
'cascadeCallbacks' => true,
]);
$this->belongsTo('ProductCatalogs', [
'foreignKey' => 'product_catalog_id',
@ -79,13 +81,18 @@ class ProductCategoriesTable extends Table
$this->hasMany('ChildProductCategories', [
'className' => 'CakeProducts.ProductCategories',
'foreignKey' => 'parent_id',
'dependent' => true,
'cascadeCallbacks' => true,
]);
$this->hasMany('ProductCategoryAttributes', [
'foreignKey' => 'product_category_id',
'bindingKey' => 'internal_id',
'className' => 'CakeProducts.ProductCategoryAttributes',
'dependent' => true,
'cascadeCallbacks' => true,
]);
$this->behaviors()->Tree->setConfig('scope', ['product_catalog_id' => $this->treeCatalogId]);
$this->addBehavior('Muffin/Trash.Trash');
}
/**
@ -118,6 +125,10 @@ class ProductCategoriesTable extends Table
->boolean('enabled')
->notEmptyString('enabled');
$validator
->dateTime('deleted')
->allowEmptyDateTime('deleted');
return $validator;
}

View File

@ -54,6 +54,8 @@ class ProductCategoryAttributeOptionsTable extends Table
'joinType' => 'INNER',
'className' => 'CakeProducts.ProductCategoryAttributes',
]);
$this->addBehavior('Muffin/Trash.Trash');
}
/**
@ -84,6 +86,10 @@ class ProductCategoryAttributeOptionsTable extends Table
->boolean('enabled')
->notEmptyString('enabled');
$validator
->dateTime('deleted')
->allowEmptyDateTime('deleted');
return $validator;
}

View File

@ -61,8 +61,12 @@ class ProductCategoryAttributesTable extends Table
'foreignKey' => 'product_category_attribute_id',
'className' => 'CakeProducts.ProductCategoryAttributeOptions',
'saveStrategy' => 'replace',
'dependent' => true,
'cascadeCallbacks' => true,
]);
$this->getSchema()->setColumnType('attribute_type_id', EnumType::from(ProductCategoryAttributeTypeId::class));
$this->addBehavior('Muffin/Trash.Trash');
}
/**
@ -93,6 +97,10 @@ class ProductCategoryAttributesTable extends Table
->requirePresence('enabled', 'create')
->notEmptyString('enabled');
$validator
->dateTime('deleted')
->allowEmptyDateTime('deleted');
return $validator;
}

View File

@ -60,9 +60,12 @@ class ProductsTable extends Table
$this->hasMany('ProductAttributes', [
'className' => 'CakeProducts.ProductAttributes',
'dependent' => true,
'cascadeCallbacks' => true,
]);
$this->getSchema()->setColumnType('product_type_id', EnumType::from(ProductProductTypeId::class));
$this->addBehavior('Muffin/Trash.Trash');
}
/**
@ -88,6 +91,10 @@ class ProductsTable extends Table
->requirePresence('product_type_id', 'create')
->notEmptyString('product_type_id');
$validator
->dateTime('deleted')
->allowEmptyDateTime('deleted');
return $validator;
}

View File

@ -23,7 +23,7 @@ class ExternalProductCatalogsFixture extends TestFixture
'base_url' => 'http://localhost:8766',
'api_url' => 'http://localhost:8766/api',
'created' => '2024-11-22 09:39:37',
'deleted' => '2024-11-22 09:39:37',
'deleted' => null,
],
];
parent::init();

View File

@ -19,10 +19,12 @@ class ExternalProductCatalogsProductCatalogsFixture extends TestFixture
{
$this->records = [
[
'id' => 1,
'external_product_catalog_id' => '115153f3-2f59-4234-8ff8-e1b205769999',
'product_catalog_id' => '115153f3-2f59-4234-8ff8-e1b205761428',
'created' => '2024-11-22 09:39:37',
'enabled' => false,
'deleted' => null,
],
];
parent::init();

View File

@ -23,12 +23,14 @@ class ProductCatalogsFixture extends TestFixture
'name' => 'Automotive',
'catalog_description' => '',
'enabled' => true,
'deleted' => null,
],
[
'id' => 'f56f3412-ed23-490b-be6e-016208c415d2',
'name' => 'Software',
'catalog_description' => '',
'enabled' => true,
'deleted' => null,
],
];
parent::init();

View File

@ -28,6 +28,7 @@ class ProductCategoriesFixture extends TestFixture
'lft' => 1,
'rght' => 4,
'enabled' => true,
'deleted' => null,
],
[
'id' => 2,
@ -39,6 +40,7 @@ class ProductCategoriesFixture extends TestFixture
'lft' => 2,
'rght' => 3,
'enabled' => true,
'deleted' => null,
],
[
'id' => 3,
@ -50,6 +52,7 @@ class ProductCategoriesFixture extends TestFixture
'lft' => 5,
'rght' => 8,
'enabled' => true,
'deleted' => null,
],
[
'id' => 4,
@ -61,6 +64,7 @@ class ProductCategoriesFixture extends TestFixture
'lft' => 6,
'rght' => 7,
'enabled' => true,
'deleted' => null,
],
[
'id' => 5,
@ -72,6 +76,7 @@ class ProductCategoriesFixture extends TestFixture
'lft' => 9,
'rght' => 12,
'enabled' => true,
'deleted' => null,
],
[
'id' => 6,
@ -83,6 +88,7 @@ class ProductCategoriesFixture extends TestFixture
'lft' => 10,
'rght' => 11,
'enabled' => true,
'deleted' => null,
],
[
'id' => 7,
@ -94,6 +100,7 @@ class ProductCategoriesFixture extends TestFixture
'lft' => 1,
'rght' => 2,
'enabled' => true,
'deleted' => null,
],
];
parent::init();

View File

@ -24,6 +24,7 @@ class ProductCategoryAttributeOptionsFixture extends TestFixture
'attribute_value' => 'Red',
'attribute_label' => 'Red',
'enabled' => 1,
'deleted' => null,
],
[
'id' => 'e06f1723-2456-483a-b3c4-004603e032a1',
@ -31,6 +32,7 @@ class ProductCategoryAttributeOptionsFixture extends TestFixture
'attribute_value' => 'Blue',
'attribute_label' => 'Blue',
'enabled' => 1,
'deleted' => null,
],
[
'id' => 'e06f1723-2456-483a-b3c4-004603e032a2',
@ -38,6 +40,7 @@ class ProductCategoryAttributeOptionsFixture extends TestFixture
'attribute_value' => 'Green',
'attribute_label' => 'Green',
'enabled' => 1,
'deleted' => null,
]
];
parent::init();

View File

@ -24,6 +24,7 @@ class ProductCategoryAttributesFixture extends TestFixture
'product_category_id' => '6d223283-361b-4f9f-a7f1-c97aa0ca4c23',
'attribute_type_id' => 1,
'enabled' => 1,
'deleted' => null,
],
];
parent::init();

View File

@ -23,6 +23,7 @@ class ProductsFixture extends TestFixture
'name' => '12AWG RED TXL Wire',
'product_category_id' => '6d223283-361b-4f9f-a7f1-c97aa0ca4c23',
'product_type_id' => 1,
'deleted' => null,
],
];
parent::init();

View File

@ -45,7 +45,7 @@ class ExternalProductCatalogsProductCatalogsControllerTest extends BaseControlle
// $this->enableCsrfToken();
// $this->enableSecurityToken();
$this->disableErrorHandlerMiddleware();
$this->ExternalProductCatalogsProductCatalogs = $this->getTableLocator()->get('ExternalProductCatalogsProductCatalogs');
$this->ExternalProductCatalogsProductCatalogs = $this->getTableLocator()->get('CakeProducts.ExternalProductCatalogsProductCatalogs');
}
/**
@ -99,6 +99,7 @@ class ExternalProductCatalogsProductCatalogsControllerTest extends BaseControlle
*/
public function testDelete(): void
{
$cntBeforeWithTrashed = $this->ExternalProductCatalogsProductCatalogs->find('withTrashed')->count();
$cntBefore = $this->ExternalProductCatalogsProductCatalogs->find()->count();
// $this->loginUserByRole('admin');
@ -112,7 +113,9 @@ class ExternalProductCatalogsProductCatalogsControllerTest extends BaseControlle
$this->assertResponseCode(302);
$this->assertRedirectContains('external-product-catalogs');
$cntAfterWithTrashed = $this->ExternalProductCatalogsProductCatalogs->find('withTrashed')->count();
$cntAfter = $this->ExternalProductCatalogsProductCatalogs->find()->count();
$this->assertEquals($cntBefore - 1, $cntAfter);
$this->assertEquals($cntBeforeWithTrashed, $cntAfterWithTrashed);
}
}

View File

@ -45,7 +45,7 @@ class ProductCategoriesControllerTest extends BaseControllerTest
// $this->enableCsrfToken();
// $this->enableSecurityToken();
$this->disableErrorHandlerMiddleware();
$this->ProductCategories = $this->getTableLocator()->get('ProductCategories');
$this->ProductCategories = $this->getTableLocator()->get('CakeProducts.ProductCategories');
}
/**
@ -315,7 +315,7 @@ class ProductCategoriesControllerTest extends BaseControllerTest
public function testDelete(): void
{
$cntBefore = $this->ProductCategories->find()->count();
$cntBeforeWithTrashed = $this->ProductCategories->find('withTrashed')->count();
$this->loginUserByRole('admin');
$url = [
'plugin' => 'CakeProducts',
@ -328,6 +328,9 @@ class ProductCategoriesControllerTest extends BaseControllerTest
$this->assertRedirectContains('product-categories');
$cntAfter = $this->ProductCategories->find()->count();
$this->assertEquals($cntBefore - 1, $cntAfter);
$cntAfterWithTrashed = $this->ProductCategories->find('withTrashed')->count();
$this->assertEquals($cntBefore - 2, $cntAfter); // has 1 child category
$this->assertEquals($cntBeforeWithTrashed, $cntAfterWithTrashed);
}
}

View File

@ -77,6 +77,7 @@ class ExternalProductCatalogsTableTest extends TestCase
// verify all behaviors loaded
$expectedBehaviors = [
'Timestamp',
'Trash',
];
$behaviors = $this->ExternalProductCatalogs->behaviors();

View File

@ -75,7 +75,9 @@ class ProductCatalogsTableTest extends TestCase
}
// verify all behaviors loaded
$expectedBehaviors = [];
$expectedBehaviors = [
'Trash',
];
$behaviors = $this->ProductCatalogs->behaviors();
$this->assertCount(count($expectedBehaviors), $behaviors);

View File

@ -78,6 +78,7 @@ class ProductCategoriesTableTest extends TestCase
// verify all behaviors loaded
$expectedBehaviors = [
'Tree',
'Trash',
];
$behaviors = $this->ProductCategories->behaviors();

View File

@ -71,7 +71,9 @@ class ProductCategoryAttributeOptionsTableTest extends TestCase
}
// verify all behaviors loaded
$expectedBehaviors = [];
$expectedBehaviors = [
'Trash',
];
$behaviors = $this->ProductCategoryAttributeOptions->behaviors();
$this->assertCount(count($expectedBehaviors), $behaviors);

View File

@ -74,7 +74,9 @@ class ProductCategoryAttributesTableTest extends TestCase
}
// verify all behaviors loaded
$expectedBehaviors = [];
$expectedBehaviors = [
'Trash',
];
$behaviors = $this->ProductCategoryAttributes->behaviors();
$this->assertCount(count($expectedBehaviors), $behaviors);

View File

@ -73,7 +73,9 @@ class ProductsTableTest extends TestCase
}
// verify all behaviors loaded
$expectedBehaviors = [];
$expectedBehaviors = [
'Trash',
];
$behaviors = $this->Products->behaviors();
$this->assertCount(count($expectedBehaviors), $behaviors);