dev/core#1845 Change FK on civicrm_group to delete the associated group if a saved...
authorMichael McAndrew <michael@3sd.io>
Thu, 11 Mar 2021 15:38:43 +0000 (15:38 +0000)
committerSeamus Lee <seamuslee001@gmail.com>
Tue, 23 Mar 2021 07:09:48 +0000 (18:09 +1100)
Add in delete function on Group BAO to handle deleting associated saved search if appropriate when group is deleted and move upgrade step appropriately

Add unit test and move code to the discard function

CRM/Contact/BAO/Group.php
CRM/Contact/DAO/Group.php
CRM/Upgrade/Incremental/php/FiveThirtySeven.php
CRM/Upgrade/Incremental/php/FiveThirtySix.php
tests/phpunit/api/v3/SavedSearchTest.php
xml/schema/Contact/Group.xml

index 138fd9e2d3eddf1b682151b3cca386006d9e9980..81feb0f2e3cdd37912bb0a28450b8414f14db270 100644 (file)
@@ -9,6 +9,8 @@
  +--------------------------------------------------------------------+
  */
 
+use Civi\Api4\Group;
+
 /**
  *
  * @package CRM
@@ -94,6 +96,18 @@ class CRM_Contact_BAO_Group extends CRM_Contact_DAO_Group {
     $query = "DELETE FROM civicrm_acl_entity_role where entity_table = 'civicrm_group' AND entity_id = %1";
     CRM_Core_DAO::executeQuery($query, $params);
 
+    //check whether this group contains  any saved searches and check if that saved search is appropriate to delete.
+    $groupDetails = Group::get(FALSE)->addWhere('id', '=', $id)->execute();
+    if (!empty($groupDetails[0]['saved_search_id'])) {
+      $savedSearch = new CRM_Contact_DAO_SavedSearch();
+      $savedSearch->id = $groupDetails[0]['saved_search_id'];
+      $savedSearch->find(TRUE);
+      // If it is a traditional saved search i.e has form values and there is no linked api_entity then delete the saved search as well.
+      if (!empty($savedSearch->form_values) && empty($savedSearch->api_entity) && empty($savedSearch->api_params)) {
+        $savedSearch->delete();
+      }
+    }
+
     // delete from group table
     $group = new CRM_Contact_DAO_Group();
     $group->id = $id;
index 56f2c52c249cbca98a2fe73cad55aaa647a351c5..b06e64a1687d84f5c702ca3d58df2298a77855cb 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Contact/Group.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:31478cb7fd1eee1299fb22225020be77)
+ * (GenCodeChecksum:8f7306d4427fc261d17944ad601bb422)
  */
 
 /**
index bad13a5b51dda39f2eb8c63b2a371f96a09a1381..24edc4a44e0c4bd1ede9700abef3963f065a22a5 100644 (file)
@@ -52,6 +52,16 @@ class CRM_Upgrade_Incremental_php_FiveThirtySeven extends CRM_Upgrade_Incrementa
    * (change the x in the function name):
    */
 
+  /**
+   * Upgrade function.
+   *
+   * @param string $rev
+   */
+  public function upgrade_5_37_alpha1($rev) {
+    $this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev);
+    $this->addTask('core-issue#1845 - Alter Foreign key on civicrm_group to delete when the associated group when the saved search is deleted', 'alterSavedSearchFK');
+  }
+
   //  /**
   //   * Upgrade function.
   //   *
@@ -69,4 +79,16 @@ class CRM_Upgrade_Incremental_php_FiveThirtySeven extends CRM_Upgrade_Incrementa
   //   return TRUE;
   // }
 
+  /**
+   * @param \CRM_Queue_TaskContext $ctx
+   *
+   * @return bool
+   */
+  public static function alterSavedSearchFK(CRM_Queue_TaskContext $ctx) {
+    CRM_Core_BAO_SchemaHandler::safeRemoveFK('civicrm_group', 'FK_civicrm_group_saved_search_id');
+    CRM_Core_DAO::executeQuery('DELETE civicrm_saved_search FROM civicrm_saved_search LEFT JOIN civicrm_group ON civicrm_saved_search.id = civicrm_group.saved_search_id WHERE civicrm_group.id IS NULL AND form_values IS NOT NULL and api_params IS NULL');
+    CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_group ADD CONSTRAINT `FK_civicrm_group_saved_search_id` FOREIGN KEY (`saved_search_id`) REFERENCES `civicrm_saved_search`(`id`) ON DELETE CASCADE', [], TRUE, NULL, FALSE, FALSE);
+    return TRUE;
+  }
+
 }
index d1dfe3112ad2343d830f04d9c99eee163b2db0c4..89a65a6c779d52c687dd1e814ef109ef4a715c71 100644 (file)
@@ -83,7 +83,6 @@ class CRM_Upgrade_Incremental_php_FiveThirtySix extends CRM_Upgrade_Incremental_
       'civicrm_saved_search', 'description', "text DEFAULT NULL");
 
     $this->addTask('core-issue#2422 - Add constraints to civicrm_saved_search', 'taskAddConstraints');
-
   }
 
   /**
index 2b227ca07d56f76564b363440262617a9e79937c..ed303c8ae491d94d36a1955e00976f815661425f 100644 (file)
@@ -40,7 +40,7 @@ class api_v3_SavedSearchTest extends CiviUnitTestCase {
   protected $_entity;
   public $DBResetRequired = FALSE;
 
-  public function setUp() {
+  public function setUp(): void {
     parent::setUp();
 
     // The line below makes it unneccessary to do cleanup after a test,
@@ -68,7 +68,7 @@ class api_v3_SavedSearchTest extends CiviUnitTestCase {
   /**
    * Create a saved search, and see whether the returned values make sense.
    */
-  public function testCreateSavedSearch() {
+  public function testCreateSavedSearch(): void {
     $contactID = $this->createLoggedInUser();
     $result = $this->callAPIAndDocument(
         $this->_entity, 'create', $this->params, __FUNCTION__, __FILE__)['values'];
@@ -91,7 +91,7 @@ class api_v3_SavedSearchTest extends CiviUnitTestCase {
    * Create a saved search, retrieve it again, and check for ID and one of
    * the field values.
    */
-  public function testCreateAndGetSavedSearch() {
+  public function testCreateAndGetSavedSearch(): void {
     // Arrange:
     // (create a saved search)
     $create_result = $this->callAPISuccess(
@@ -115,7 +115,7 @@ class api_v3_SavedSearchTest extends CiviUnitTestCase {
    * Create a saved search, and test whether it can be used for a smart
    * group.
    */
-  public function testCreateSavedSearchWithSmartGroup() {
+  public function testCreateSavedSearchWithSmartGroup(): void {
     // First create a volunteer for the default organization
 
     $result = $this->callAPISuccess('Contact', 'create', [
@@ -161,7 +161,61 @@ class api_v3_SavedSearchTest extends CiviUnitTestCase {
     $this->assertEquals($contact_id, $get_result['values'][$contact_id]['id']);
   }
 
-  public function testDeleteSavedSearch() {
+  /**
+   * Create a saved search, and test whether it can be used for a smart
+   * group. Also check that when the Group is deleted the associated saved search gets deleted.
+   * @dataProvider versionThreeAndFour
+   */
+  public function testSavedSearchIsDeletedWhenSmartGroupIs($apiVersion): void {
+    $this->_apiVersion = $apiVersion;
+    // First create a volunteer for the default organization
+
+    $result = $this->callAPISuccess('Contact', 'create', [
+      'first_name' => 'Joe',
+      'last_name' => 'Schmoe',
+      'contact_type' => 'Individual',
+      'api.Relationship.create' => [
+        'contact_id_a' => '$value.id',
+        // default organization:
+        'contact_id_b' => 1,
+        // volunteer relationship:
+        'relationship_type_id' => 6,
+        'is_active' => 1,
+      ],
+    ]);
+    $contact_id = $result['id'];
+
+    // Now create our saved search, and chain the creation of a smart group.
+    $params = $this->params;
+    $params['api.Group.create'] = [
+      'name' => 'my_smartgroup',
+      'title' => 'my smartgroup',
+      'description' => 'Volunteers for the default organization',
+      'saved_search_id' => '$value.id',
+      'is_active' => 1,
+      'visibility' => 'User and User Admin Only',
+      'is_hidden' => 0,
+      'is_reserved' => 0,
+    ];
+
+    $create_result = $this->callAPISuccess($this->_entity, 'create', $params);
+
+    $created_search = CRM_Utils_Array::first($create_result['values']);
+    $group_id = $created_search['api.Group.create']['id'];
+
+    // Search for contacts in our new smart group
+    $get_result = $this->callAPISuccess('Contact', 'get', ['group' => $group_id]);
+
+    // Expect our contact to be there.
+    $this->assertEquals(1, $get_result['count']);
+    $this->assertEquals($contact_id, $get_result['values'][$contact_id]['id']);
+
+    $this->callAPISuccess('Group', 'delete', ['id' => $group_id]);
+    $savedSearch = $this->callAPISuccess('SavedSearch', 'get', ['id' => $created_search['id']]);
+    $this->assertCount(0, $savedSearch['values']);
+  }
+
+  public function testDeleteSavedSearch(): void {
     // Create saved search, delete it again, and try to get it
     $create_result = $this->callAPISuccess($this->_entity, 'create', $this->params);
     $delete_params = ['id' => $create_result['id']];
index a53625142a2d681d4b0522c8bbe87147c343ea7f..79708451399df8dfb80b33ca2eb3a5b290b818a9 100644 (file)
@@ -82,7 +82,7 @@
     <table>civicrm_saved_search</table>
     <key>id</key>
     <add>1.1</add>
-    <onDelete>SET NULL</onDelete>
+    <onDelete>CASCADE</onDelete>
   </foreignKey>
   <field>
     <name>is_active</name>