CRM-20621 : manage tags: the tag usage count is not accurate
authordeb.monish <monish.deb@jmaconsulting.biz>
Tue, 30 May 2017 12:36:16 +0000 (18:06 +0530)
committerdeb.monish <monish.deb@jmaconsulting.biz>
Thu, 15 Jun 2017 08:43:28 +0000 (14:13 +0530)
CRM/Admin/Page/AJAX.php
CRM/Core/BAO/Tag.php
tests/phpunit/CRM/Contact/Page/AjaxTest.php

index 35f5f2aabd567ed603b412846db8899c148f68b5..980fae3a2bebc200ea1123e15d1b5228a64ffa79 100644 (file)
@@ -304,20 +304,29 @@ class CRM_Admin_Page_AJAX {
     $parent = CRM_Utils_Type::escape(CRM_Utils_Array::value('parent_id', $_GET, 0), 'Integer');
     $result = array();
 
-    $parentClause = $parent ? "AND tag.parent_id = $parent" : 'AND tag.parent_id IS NULL';
-    $sql = "SELECT tag.*, child.id AS child, COUNT(et.id) as usages
-      FROM civicrm_tag tag
-      LEFT JOIN civicrm_entity_tag et ON et.tag_id = tag.id
-      LEFT JOIN civicrm_tag child ON child.parent_id = tag.id
-      WHERE tag.is_tagset <> 1 $parentClause
-      GROUP BY tag.id
-      ORDER BY tag.name";
+    $parentClause = $parent ? "AND parent_id = $parent" : 'AND parent_id IS NULL';
+    $sql = "SELECT *
+      FROM civicrm_tag
+      WHERE is_tagset <> 1 $parentClause
+      GROUP BY id
+      ORDER BY name";
+
+    // fetch all child tags in Array('parent_tag' => array('child_tag_1', 'child_tag_2', ...)) format
+    $childTagIDs = CRM_Core_BAO_Tag::getChildTags();
+
     $dao = CRM_Core_DAO::executeQuery($sql);
     while ($dao->fetch()) {
       $style = '';
       if ($dao->color) {
         $style = "background-color: {$dao->color}; color: " . CRM_Utils_Color::getContrast($dao->color);
       }
+      $hasChildTags = empty($childTagIDs[$dao->id]) ? FALSE : TRUE;
+      // get tag IDs includeing its child tags, later used to fetch usage count
+      $tagIDs = array($dao->id);
+      if ($hasChildTags) {
+        $tagIDs = array_merge($tagIDs, $childTagIDs[$dao->id]);
+      }
+      $usedFor = (array) explode(',', $dao->used_for);
       $result[] = array(
         'id' => $dao->id,
         'text' => $dao->name,
@@ -330,18 +339,25 @@ class CRM_Admin_Page_AJAX {
           'style' => $style,
           'class' => 'crm-tag-item',
         ),
-        'children' => (bool) $dao->child,
+        'children' => $hasChildTags,
         'data' => array(
           'description' => (string) $dao->description,
           'is_selectable' => (bool) $dao->is_selectable,
           'is_reserved' => (bool) $dao->is_reserved,
-          'used_for' => $dao->used_for ? explode(',', $dao->used_for) : array(),
+          'used_for' => $usedFor,
           'color' => $dao->color ? $dao->color : '#ffffff',
-          'usages' => (int) $dao->usages,
+          'usages' => civicrm_api3('EntityTag', 'getcount', array(
+            'entity_table' => array('IN' => $usedFor),
+            'tag_id' => array('IN' => $tagIDs),
+          )),
         ),
       );
     }
 
+    if (!empty($_REQUEST['is_unit_test'])) {
+      return $result;
+    }
+
     CRM_Utils_JSON::output($result);
   }
 
index 31722e897d7ff13a2162cd792a119ad55184bbdc..a8316323b76083e7503548f6f6e83717503b48e9 100644 (file)
@@ -531,4 +531,40 @@ class CRM_Core_BAO_Tag extends CRM_Core_DAO_Tag {
     return $tags;
   }
 
+  /**
+   * Get child tags IDs
+   *
+   * @return array $childTagIDs
+   *   associated array of child tags in Array('Parent Tag ID' => Array('Child Tag 1', ...)) format
+   */
+  public static function getChildTags() {
+    $childTagIDs = array();
+
+    // only fetch those tags which has child tags
+    $getChildGroupSQL = "SELECT parent.id as parent_id, GROUP_CONCAT(child.id) as child_id
+        FROM civicrm_tag parent,
+        civicrm_tag child
+        WHERE parent.is_tagset <> 1 AND child.parent_id = parent.id
+        GROUP BY parent.id
+    ";
+    $dao = CRM_Core_DAO::executeQuery($getChildGroupSQL);
+    while ($dao->fetch()) {
+      $childTagIDs[$dao->parent_id] = (array) explode(',', $dao->child_id);
+    }
+
+    // check if child tag has any childs, if found then include those child tags inside parent tag
+    //  i.e. format Array('parent_tag' => array('child_tag_1', ...), 'child_tag_1' => array(child_tag_1_1, ..), ..)
+    //  to Array('parent_tag' => array('child_tag_1', 'child_tag_1_1'...), ..)
+    foreach ($childTagIDs as $parentTagID => $childTags) {
+      foreach ($childTags as $childTag) {
+        // if $childTag has any child tag of its own
+        if (array_key_exists($childTag, $childTagIDs)) {
+          $childTagIDs[$parentTagID] = array_merge($childTagIDs[$parentTagID], $childTagIDs[$childTag]);
+        }
+      }
+    }
+
+    return $childTagIDs;
+  }
+
 }
index cbc02de1b601732374f06aeb32460b6b8e73465c..d655747d838462fd49e544434882b6e85aa9e818 100644 (file)
@@ -210,6 +210,89 @@ class CRM_Contact_Page_AjaxTest extends CiviUnitTestCase {
     $this->assertEquals(array('data' => array(), 'recordsTotal' => 0, 'recordsFiltered' => 0), $result);
   }
 
+  /**
+   * CRM-20621 : Test to check usage count of Tag tree
+   */
+  public function testGetTagTree() {
+    $contacts = array();
+    // create three contacts
+    for ($i = 0; $i < 3; $i++) {
+      $contacts[] = $this->individualCreate();
+    }
+
+    // Create Tag called as 'Parent Tag'
+    $parentTag = $this->tagCreate(array(
+      'name' => 'Parent Tag',
+      'used_for' => 'civicrm_contact',
+    ));
+    //assign first contact to parent tag
+    $params = array(
+      'entity_id' => $contacts[0],
+      'entity_table' => 'civicrm_contact',
+      'tag_id' => $parentTag['id'],
+    );
+    // TODO: EntityTag.create API is not working
+    CRM_Core_BAO_EntityTag::add($params);
+
+    // Create child Tag of $parentTag
+    $childTag1 = $this->tagCreate(array(
+      'name' => 'Child Tag Level 1',
+      'parent_id' => $parentTag['id'],
+      'used_for' => 'civicrm_contact',
+    ));
+    //assign contact to this level 1 child tag
+    $params = array(
+      'entity_id' => $contacts[1],
+      'entity_table' => 'civicrm_contact',
+      'tag_id' => $childTag1['id'],
+    );
+    CRM_Core_BAO_EntityTag::add($params);
+
+    // Create child Tag of $childTag1
+    $childTag2 = $this->tagCreate(array(
+      'name' => 'Child Tag Level 2',
+      'parent_id' => $childTag1['id'],
+      'used_for' => 'civicrm_contact',
+    ));
+    //assign contact to this level 2 child tag
+    $params = array(
+      'entity_id' => $contacts[2],
+      'entity_table' => 'civicrm_contact',
+      'tag_id' => $childTag2['id'],
+    );
+    CRM_Core_BAO_EntityTag::add($params);
+
+    // CASE I : check the usage count of parent tag which need to be 3
+    //  as it should also include the count of child tags
+    $_REQUEST['is_unit_test'] = TRUE;
+    $parentTagTreeResult = CRM_Admin_Page_AJAX::getTagTree();
+    foreach ($parentTagTreeResult as $result) {
+      if ($result['id'] == $parentTag['id']) {
+        $this->assertEquals(3, $result['data']['usages']);
+      }
+    }
+
+    // CASE 2 : check the usage count of level 1 child tag, which needs to be 2
+    //  as it should also include the count of its child tag
+    $_GET['parent_id'] = $parentTag['id'];
+    $childTagTree = CRM_Admin_Page_AJAX::getTagTree();
+    $this->assertEquals(2, $childTagTree[0]['data']['usages']);
+
+    // CASE 2 : check the usage count of child tag at level 2
+    //which needs to be 1 as it has no child tag
+    $_GET['parent_id'] = $childTag1['id'];
+    $childTagTree = CRM_Admin_Page_AJAX::getTagTree();
+    $this->assertEquals(1, $childTagTree[0]['data']['usages']);
+
+    //cleanup
+    foreach ($contacts as $id) {
+      $this->callAPISuccess('Contact', 'delete', array('id' => $id));
+    }
+    $this->callAPISuccess('Tag', 'delete', array('id' => $childTag2['id']));
+    $this->callAPISuccess('Tag', 'delete', array('id' => $childTag1['id']));
+    $this->callAPISuccess('Tag', 'delete', array('id' => $parentTag['id']));
+  }
+
   /**
    * Test to check contact reference field
    */