From 4c9235c97f4e578d13337eaa3262af49f529657a Mon Sep 17 00:00:00 2001 From: Marc Michalsky Date: Mon, 18 Mar 2024 17:53:38 +0100 Subject: [PATCH] try to fix problems with db transactions --- Civi/Twingle/Shop/BAO/TwingleProduct.php | 73 +++++++++++-------- Civi/Twingle/Shop/BAO/TwingleShop.php | 16 ++-- .../Shop/Exceptions/ProductException.php | 2 +- api/v3/TwingleProduct/Create.php | 8 +- api/v3/TwingleShop/Create.php | 8 +- sql/civicrm_twingle_shop.sql | 2 +- 6 files changed, 64 insertions(+), 45 deletions(-) diff --git a/Civi/Twingle/Shop/BAO/TwingleProduct.php b/Civi/Twingle/Shop/BAO/TwingleProduct.php index 355e369..7d47cdd 100644 --- a/Civi/Twingle/Shop/BAO/TwingleProduct.php +++ b/Civi/Twingle/Shop/BAO/TwingleProduct.php @@ -2,11 +2,13 @@ namespace Civi\Twingle\Shop\BAO; +use Civi\Api4\PriceField; use Civi\Twingle\Shop\DAO\TwingleProduct as TwingleProductDAO; use Civi\Twingle\Shop\DAO\TwingleShop as TwingleShopDAO; use Civi\Twingle\Shop\Exceptions\ProductException; use Civi\Twingle\Shop\Exceptions\ShopException; use CRM_Core_Exception; +use CRM_Core_Transaction; use CRM_Twingle_ExtensionUtil as E; use CRM_Utils_Type; use function Civi\Twingle\Shop\Utils\convert_int_to_bool; @@ -86,6 +88,7 @@ class TwingleProduct extends TwingleProductDAO { "twingle_shop_id", "financial_type_id", "price_field_id", + "project_id", "external_id", "tw_updated_at", "tw_created_at", @@ -306,15 +309,12 @@ public function createPriceField($mode = 'create') { if ($mode == 'edit') { $price_field_data['id'] = $this->price_field_id; } - try { - $price_field = civicrm_api3('PriceField', 'create', $price_field_data); - if ($price_field['is_error'] != 0) { - throw new ProductException( - E::ts('Could not create PriceField for this Twingle Product: %1', - [1 => $price_field['error_message']]), - ProductException::ERROR_CODE_COULD_NOT_CREATE_PRICE_FIELD); - } + $price_field = civicrm_api4( + 'PriceField', + 'create', + ['values' => $price_field_data], + )->first(); $this->price_field_id = (int) $price_field['id']; } catch (CRM_Core_Exception $e) { @@ -341,21 +341,24 @@ public function createPriceField($mode = 'create') { } // Create PriceFieldValue + $price_field_value_data = [ + 'price_field_id' => $this->price_field_id, + 'financial_type_id' => $this->financial_type_id, + 'label' => $this->name, + 'amount' => $this->price, + 'is_active' => $this->is_active, + 'description' => $this->description, + ]; + // Add id if in edit mode + if ($mode == 'edit' && $price_field_value) { + $price_field_value_data['id'] = $price_field_value['id']; + } try { - $price_field_value_data = [ - 'price_field_id' => $this->price_field_id, - 'financial_type_id' => $this->financial_type_id, - 'label' => $this->name, - 'amount' => $this->price, - 'is_active' => $this->is_active, - 'description' => $this->description, - ]; - // Add id if in edit mode - if ($mode == 'edit' && $price_field_value) { - $price_field_value_data['id'] = $price_field_value['id']; - } - - civicrm_api3('PriceFieldValue', 'create', $price_field_value_data); + civicrm_api4( + 'PriceFieldValue', + 'create', + ['values' => $price_field_value_data], + ); } catch (CRM_Core_Exception $e) { throw new ProductException( @@ -406,11 +409,16 @@ public static function findByExternalId($external_id) { /** * Add Twingle Product * + * @param string $mode + * 'create' or 'edit' * @return array * @throws \Civi\Twingle\Shop\Exceptions\ProductException * @throws \Exception */ - public function add() { + public function add($mode = 'create') { + + $tx = new CRM_Core_Transaction(); + // Try to lookup object in database try { $dao = TwingleShopDAO::executeQuery("SELECT * FROM civicrm_twingle_product WHERE external_id = %1", @@ -425,26 +433,27 @@ public function add() { ShopException::ERROR_CODE_COULD_NOT_FIND_SHOP_IN_DB); } - // Define hook type - $hook = $this->id ? 'edit' : 'create'; - // Register pre-hook - \CRM_Utils_Hook::pre($hook, 'TwingleProduct', $this->id); - - // Create associated PriceField - $this->createPriceField($hook); + \CRM_Utils_Hook::pre($mode, 'TwingleProduct', $this->id); // Set latest tw_updated_at as new updated_at $this->updated_at = \CRM_Utils_Time::date('Y-m-d H:i:s', $this->tw_updated_at); // Save object to database - $this->save(); + try { + $this->save(); + } catch (\Exception $e) { + $tx->rollback(); + throw new ProductException( + E::ts('Could not save TwingleProduct to database: ' . $e->getMessage()), + ProductException::ERROR_CODE_COULD_NOT_CREATE_PRODUCT); + } $result = self::findById($this->id); /* @var self $result */ $this->load($result->getAttributes()); // Register post-hook - \CRM_Utils_Hook::post($hook, 'TwingleProduct', $this->id, $instance); + \CRM_Utils_Hook::post($mode, 'TwingleProduct', $this->id, $instance); return $result->toArray(); } diff --git a/Civi/Twingle/Shop/BAO/TwingleShop.php b/Civi/Twingle/Shop/BAO/TwingleShop.php index b8f1a23..0656118 100644 --- a/Civi/Twingle/Shop/BAO/TwingleShop.php +++ b/Civi/Twingle/Shop/BAO/TwingleShop.php @@ -131,10 +131,12 @@ function getAttributes(): array { /** * Add Twingle Shop * + * @param string $mode + * 'create' or 'edit' * @return array * @throws \Civi\Twingle\Shop\Exceptions\ShopException */ - public function add() { + public function add($mode = 'create') { // Try to lookup object in database try { @@ -149,20 +151,14 @@ public function add() { ShopException::ERROR_CODE_COULD_NOT_FIND_SHOP_IN_DB); } - // Define hook type - $hook = $this->id ? 'edit' : 'create'; - // Register pre-hook - \CRM_Utils_Hook::pre($hook, 'TwingleShop', $this->id); - - // Create associated PriceSet - $this->createPriceSet($hook); + \CRM_Utils_Hook::pre($mode, 'TwingleShop', $this->id); // Save object to database $result = $this->save(); // Register post-hook - \CRM_Utils_Hook::post($hook, 'TwingleShop', $this->id, $instance); + \CRM_Utils_Hook::post($mode, 'TwingleShop', $this->id, $instance); return $result->toArray(); } @@ -310,6 +306,8 @@ public function getProducts() { /** * Creates Twingle Shop as a price set in CiviCRM. * + * @param string $mode + * 'create' or 'edit' * @throws \Civi\Twingle\Shop\Exceptions\ShopException */ public function createPriceSet($mode = 'create') { diff --git a/Civi/Twingle/Shop/Exceptions/ProductException.php b/Civi/Twingle/Shop/Exceptions/ProductException.php index e156970..c208ca2 100644 --- a/Civi/Twingle/Shop/Exceptions/ProductException.php +++ b/Civi/Twingle/Shop/Exceptions/ProductException.php @@ -20,6 +20,6 @@ class ProductException extends BaseException { public const ERROR_CODE_COULD_NOT_DELETE_PRICE_FIELD = 'price_field_deletion_failed'; public const ERROR_CODE_COULD_NOT_DELETE_PRICE_FIELD_VALUE = 'price_field_value_deletion_failed'; public const ERROR_CODE_PRICE_FIELD_STILL_EXISTS = 'price_field_still_exists'; - public const ERROR_CODE_PRICE_FIELD_VALUE_STILL_EXISTS = 'price_field_still_exists'; + public const ERROR_CODE_COULD_NOT_CREATE_PRODUCT = 'product_creation_failed'; } diff --git a/api/v3/TwingleProduct/Create.php b/api/v3/TwingleProduct/Create.php index 9a5d7d6..0c246eb 100644 --- a/api/v3/TwingleProduct/Create.php +++ b/api/v3/TwingleProduct/Create.php @@ -110,8 +110,14 @@ function civicrm_api3_twingle_product_Create($params): array { $product = new TwingleProduct(); $product->load($params); + // Define mode + $mode = $product->id ? 'edit' : 'create'; + + // Create associated PriceField + $product->createPriceField($mode); + // Save TwingleProduct - $product->add(); + $product->add($mode); $result = $product->getAttributes(); return civicrm_api3_create_success($result, $params, 'TwingleProduct', 'Create'); } diff --git a/api/v3/TwingleShop/Create.php b/api/v3/TwingleShop/Create.php index ebb34f4..4aa74b7 100644 --- a/api/v3/TwingleShop/Create.php +++ b/api/v3/TwingleShop/Create.php @@ -65,8 +65,14 @@ function civicrm_api3_twingle_shop_Create($params) { $shop = new TwingleShop(); $shop->load($params); + // Define mode + $mode = $shop->id ? 'edit' : 'create'; + + // Create associated PriceSet + $shop->createPriceSet($mode); + // Save TwingleShop - $result = $shop->add(); + $result = $shop->add($mode); // Return success return civicrm_api3_create_success($result, $params, 'TwingleShop', 'Create'); diff --git a/sql/civicrm_twingle_shop.sql b/sql/civicrm_twingle_shop.sql index 9a811a2..918b0ef 100644 --- a/sql/civicrm_twingle_shop.sql +++ b/sql/civicrm_twingle_shop.sql @@ -60,7 +60,7 @@ CREATE TABLE `civicrm_twingle_product` ( `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Timestamp of when the product was created in the database', `updated_at` datetime NOT NULL COMMENT 'Timestamp of when the product was last updated in the Twingle database', PRIMARY KEY (`id`), - CONSTRAINT FK_civicrm_twingle_product_price_field_id FOREIGN KEY (`price_field_id`) REFERENCES `civicrm_contact`(`id`) ON DELETE CASCADE, + CONSTRAINT FK_civicrm_twingle_product_price_field_id FOREIGN KEY (`price_field_id`) REFERENCES `civicrm_price_field`(`id`) ON DELETE CASCADE, CONSTRAINT FK_civicrm_twingle_product_twingle_shop_id FOREIGN KEY (`twingle_shop_id`) REFERENCES `civicrm_twingle_shop`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB;